python算法--双指针问题

395 阅读6分钟

本文为《你也能看懂的python算法书》学习笔记,仅供自己复习回顾使用,若有侵权可删除

合并有序序列

arr1 = [1,3,4,6,10]
arr2 = [2,5,8,11]
ans = arr1.copy()
ind = 0
for i in range(0, len(arr2)):
    while ind < len(arr1):
        if arr2[i] <= arr1[ind]:
            ans.insert(ind+i,arr2[i])
            break
        else:
            ind += 1
    else:
        ans = ans + arr2[i:]
        break
'''
为什么不使用 for 循环而要用 while 呢?因为同一个数前面可能要插入两个数字,而 for 循环只能让它的前面插入一个数。
while...else 语句是用来判断 while 循环是否被完整执行完的语句。
'''

二分查找

二分查找又叫作折半查找,意思为每次查找后,查找的范围都折半。
二分查找需要两个指针,一个指向数组的第一个元素,叫作头指针;另一个指向数组最后一个元素的后方,叫作尾指针,尾指针的定义是指向查找范围中最后一个数的后面的指针, 它本身是不包含在查找范围内的。
由于整个查找过程将要重复相似的操作,并且不确定总共的查找次数, 因此使用 while 循环来进行查找.

number = [1,3,5,6,7,8,13,14,15,17,18,24,30,43,56]
search = int(input('请输入需要查找的数值'))
head = 0
tail = len(number)
while tail - head != 1:
    mid = (tail+head)//2
    if search > number[mid]:
        head = mid
    elif search < number[mid]:
        tail = mid
    else:
        ans = mid
        break
else:
    if search == number[head]:
        ans = head
    else:
        ans = -1
print(ans)

链表

链表是用指针连接的用于存储数据的数组, 它最大的优点在于可以有效利用零碎的内存空间。
链表的每个元素不仅仅存储这个元素的值,还要存储与它相连的元素的指针的值.\

单链表的每个元素包含一个本身的值和一个指向下一个数的指针。因为链表的最后一个数没有下一个数,所以它的指针为空指针.

## 单链表
ListValue = [1,5,6,2,4,3]
ListPointer = [3,2,-1,5,1,4]
head = 0
print(ListValue[head])
next = ListPointer[head]
while next != -1:
    print(ListValue[next])
    next = ListPointer[next]
    

LinkedList = [ [1,3], [5,2], [6,-1], [2,5], [4,1], [3,4] ]
value = 0
pointer = 1
head = 0
print(LinkedList[head][value]) #直接输出第一个元素的值
next = LinkedList[head][pointer] #给 next 赋初始值

while next != -1: #next 是指向下一个元素的指针,它不等于-1 代表后面还有元素
    print(LinkedList[next][value]) #输出下一个元素中存储的值
    next = LinkedList[next][pointer]

双链表比起单链表的好处就是它可以双向遍历在第一种模拟方法中,可以在 ListLeft 中寻找-1 的位置从而确定链表的第一个元素。 因为与单链表不同,双链表的第一个元素有一个指向它前一个元素的指针,这个指针为空

ListValue = [1,5,6,2,7,3]
ListRight = [3,2,4,5,-1,1] #用于存储下一个元素下标的指针
ListLeft = [-1,5,1,0,2,3]

head = ListLeft.index(-1) #头指针的值为-1ListLeft 中的位置
print(ListValue[head])
Next = ListRight[head]

while Next > -1:
    print(ListValue[Next])
    Next = ListRight[Next]
    print() #换行隔开两个链表的输出结果

right = 1 #小数组的第二个数存储下一个元素的下标
left = 2 #小数组的第三个数存储上一个元素的下标
value = 0 #小数组的第一个数存储元素的值
LinkedList = [[1,3,-1],[5,2,5],[6,4,1],[2,5,0],[7,-1,2],[3,1,3]]
head = 0 
print(LinkedList[head][value])
Next = LinkedList[head][right]

while Next > -1:
    print(LinkedList[Next][value])
    Next = LinkedList[Next][right]
    print() #换行隔开两个链表的输出结果

双向输出双链表

ListValue = [1,5,6,2,7,3]
ListRight = [3,2,4,5,-1,1] #用于存储下一个元素下标的指针
ListLeft = [-1,5,1,0,2,3]

head = ListRight.index(-1) #头指针的值为-1ListLeft 中的位置
print(ListValue[head])
Next = ListLeft[head]

while Next > -1:
    print(ListValue[Next])
    Next = ListLeft[Next]
    print() #换行隔开两个链表的输出结果

向单链表中添加元素

  • 首先,把新元素的指针指向它将要插入的位置后方的元素
  • 随后,把新元素前面的元素的指针指向这个新元素,

ListValue = [1, 5, 6, 2, 7, 3]
ListRight = [3, 2, 4, 5, -1, 1]
head = 0
prepos = 5 #已知要插入的位置的上一个元素的位置

ListValue.append(4)
ListRight.append(ListRight[prepos])
ListRight[prepos] = len(ListRight) -1


##完整版
def Output(ListValue,ListRight,head): #定义一个函数用于输出链表
    print(ListValue[head])
    next = ListRight[head]
    while next != -1:
        print(ListValue[next])
        next = ListRight[next]

ListValue = [1, 5, 6, 2, 7, 3]
ListRight = [3, 2, 4, 5, -1, 1]
head = 0
prepos = 5 #已知要插入的位置的上一个元素的位置
Output(ListValue,ListRight,head) #输出未插入元素的链表
print()
ListValue.append(4) #向数组末尾加上新元素的值 4
ListRight.append(ListRight[prepos]) #加上新元素指针指向的位置(下一个元素)
ListRight[prepos] = len(ListValue)-1 #上一个元素的指针指向新元素
Output(ListValue,ListRight,head) #输出已经插入元素的链表

向双链表中添加元素

  • 第一步操作:给新元素指向前后元素的指针赋值。
  • 接下来, 为前后两个元素指向新元素的指针赋值
  • 双链表比起单链表的好处就是可以通过知道上一个元素的位置来插入新元素,也可以通过知道下一个元素的位置来插入新元素,这大大方便了查找
##完整版
def Output(ListValue,ListRight,head): #定义一个函数用于输出链表
    print(ListValue[head])
    next = ListRight[head]
    while next != -1:
        print(ListValue[next])
        next = ListRight[next]

ListValue = [1, 5, 6, 2, 7, 3] #建立一个双链表
ListRight = [3, 2, 4, 5, -1, 1]
ListLeft = [-1, 5, 1, 0, 2, 3]
head = 0 #确定第一个元素的位置
prepos = 5 #确定要插入的位置的前一个元素的位
Output(ListValue,ListRight,head) #输出未插入元素的链表
print()
ListValue.append(4) #向数组末尾加上新元素的值 4
ListRight.append(ListRight[prepos]) #加上新元素指针指向的位置(下一个元素)
ListLeft.append(prepos) #加上新元素指针指向的位置(下一个元素)

ListRight[prepos] = len(ListValue)-1 #上一个元素的指针指向新元素
ListLeft[ListRight[prepos]] = len(ListValue)-1 #上一个元素的指针指向新元素
Output(ListValue,ListRight,head) #输出已经插入元素的链表

删除列表中的元素

单链表的删除只需要一步就可以完成。直接通过当前元素指向下一个元素的指针来找到下下一个元素的位置,然后把这个位置赋给当前元素的指针

ListValue = [1, 5, 6, 2, 7, 3]
ListRight = [3, 2, 4, 5, -1, 1]
head = 0 #确定头指针
prepos = 5 #确定要删除的元素的前一个数的位
ListRight[prepos] = ListRight[ListRight[prepos]]

和前面一样,我们知道要删除的元素的前一个数的位置。然后通过要删除的元素来确定下下一个元素的位置,最后直接连接要删除的元素的前后两个数就完成了删除。虽然被删除的元素仍然有指针指向前后两个元素,但它不会出现在链表中。

ListValue = [1, 5, 6, 2, 7, 3] #建立双链表
ListRight = [3, 2, 4, 5, -1, 1]
ListLeft = [-1, 5, 1, 0, 2, 3]
head = 0 #确定头指针
prepos = 5 #确定前一个元素的位置

ListRight[prepos] = ListRight[ListRight[prepos]]
ListLeft[ListRight[ListRight[prepos]]] = prepos