技巧
- 使用容器(数组、哈希表等)
- 快慢指针
1 快慢指针
1.1 链表节点
(1)输入链表头节点,奇数长度返回中点,偶数长度返回上中点
class Node:
def __init__(self,value):
self.value = value
self.next = None
class Linklist:
def __init__(self):
self.head = Node(None)
self.head.next = None
def pushlist(self,value): # 尾插法
node = Node(value)
node.next = None
node_p = self.head
while node_p.next is not None:
node_p = node_p.next
node_p.next = node
def bianli(self):
if self.head is None:
return
else:
node_p = self.head.next
while node_p is not None:
print(node_p.value)
node_p = node_p.next
def lenght(self,list):
node_p = self.head
count = 0
while node_p is not None:
node_p = node_p.next
count = count + 1
return count
def getzhongdian(self,list):
if list.head == None or list.head.next == None or list.head.next.next == None:
return list.head.next.value
length = self.lenght(list) - 1 # 知道链表长度,不减1,它算的是带头节点的链表长度
mid = int(length / 2)
if length % 2 == 1: # 如果是奇数,返回中点
count = 0
current = self.head
while count <= mid:
current = current.next
count = count + 1 # 循环一次
else:
count = 0
current = self.head
while count < mid:
current = current.next
count = count + 1 # 循环一次
mid_value = current.value
return mid_value
class Linkcaozuo:
L = Linklist()
L.pushlist(1)
L.pushlist(2)
L.pushlist(3)
L.pushlist(4)
# L.pushlist(5)
print(L.getzhongdian(L))
(2)输入链表头节点,奇数长度返回中点,偶数长度返回下中点
def getzhongdian(self,list):
if list.head == None or list.head.next == None:
return list.head.next.value
length = self.lenght(list) - 1 # 知道链表长度
mid = int(length / 2)
if length % 2 == 1: # 如果是奇数,返回中点
count = 0
current = self.head
while count <= mid:
current = current.next
count = count + 1 # 循环一次
else: # 如果是偶数返回下中点
count = 0
current = self.head
while count < mid:
current = current.next
count = count + 1 # 循环一次
mid_value = current.next.value
return mid_value
(3)输入链表头节点,奇数长度返回中点前一个,偶数长度返回上中点前一个
def getzhongdian(self,list):
if list.head == None or list.head.next == None or list.head.next.next == None:
return 0
length = self.lenght(list) - 1 # 知道链表长度
mid = int(length / 2)
if length % 2 == 1: # 如果是奇数,返回中点前一个
count = 0
current = self.head
while count < mid:
current = current.next
count = count + 1 # 循环一次
else: # 如果是偶数返回上中点前一个
count = 0
current = self.head
while count < mid - 1:
current = current.next
count = count + 1 # 循环一次
mid_value = current.value
return mid_value
(4)输入链表头节点,奇数长度返回中点前一个,偶数长度返回下中点前一个
def getzhongdian(self,list):
if list.head == None or list.head.next == None:
return 0
length = self.lenght(list) - 1 # 知道链表长度
mid = int(length / 2)
if length % 2 == 1: # 如果是奇数,返回中点前一个
count = 0
current = self.head
while count < mid:
current = current.next
count = count + 1 # 循环一次
else: # 如果是偶数返回下中点前一个
count = 0
current = self.head
while count < mid:
current = current.next
count = count + 1 # 循环一次
mid_value = current.value
return mid_value
下面开始优化代码:使用快慢指针
(1)
def getzhongdian(self,list):
if list.head.next is None or list.head.next.next is None:
return list.head.next.value
slow = list.head.next.next # 慢指针
fast = list.head.next.next.next # 快指针
while fast.next is not None and fast.next.next is not None:
slow = slow.next
fast = fast.next.next
return slow.value
(2)
def getzhongdian(self,list):
if list.head.next is None or list.head.next.next is None:
return list.head.next.value
slow = list.head.next.next
fast = list.head.next.next
while fast.next is not None and fast.next.next is not None:
slow = slow.next
fast = fast.next.next
return slow.value
(3)
def getzhongdian(self,list):
if list.head.next is None or list.head.next.next is None:
return list.head.next.value
slow = list.head.next # 慢指针跑一个
fast = list.head.next.next.next # 快指针跑两个
while fast.next is not None and fast.next.next is not None:
slow = slow.next
fast = fast.next.next
return slow.value
(4)
def getzhongdian(self,list):
if list.head.next is None or list.head.next.next is None:
return 0
if list.head.next.next.next is None:
return list.head.next.value
slow = list.head.next # 慢指针跑一个
fast = list.head.next.next # 快指针跑两个
while fast.next is not None and fast.next.next is not None:
slow = slow.next
fast = fast.next.next
return slow.value
1.2 判断是否回文
1.2.1变成数组形式(这个很简单)
def anagrams(self,list):
if self.head is None:
return 0
num = []
cur = self.head
while cur is not None:
value = cur.value
cur = cur.next
num.append(value) # 将链表的数放入数组内
i = 0
j = len(num)-1
flag = 0
if i == j: # 一个数字也算回文
return True
while i < j:
if num[i] == num[j]:
flag = 1
else:
flag = 0
break # 只要不相等一次,就不是回文
i = i + 1
j = j - 1
if flag == 1:
print("回文")
else:
print("不是回文")
1.2.2 利用栈结构
def isPalind(self,list):
stack = Stack()
cur = list.head
while cur is not None:
stack.push(cur.value)
cur = cur.next
while list.head is not None:
if list.head.value != stack.pop():
return False
list.head = list.head.next
return True
1.2.3 再优化:利用快慢指针,不开辟空间
def isPalind(self, list): # 回文判断
if list.head == None or list.head.next == None:
return True
n1 = list.head
n2 = list.head
while n2.next != None and n2.next.next != None: # 保证长度>=3个
n1 = n1.next # n1->mid
n2 = n2.next.next # n2->end
# 奇数个n1来到中点,偶数n1到上中点
n2 = n1.next # n2指向了右边第一个节点
n1.next = None # mid.next -> NONE 中间节点指向空
while n2 != None: # 右边翻转
n3 = n2.next # 保存下一个节点,n3记一下位置
n2.next = n1 # 右边的下一个节点指向中间
n1 = n2 # n1指向已经反转的节点
n2 = n3
n3 = n1 # n3-> 保存最后一个节点,n3记一下最后一个节点
n2 = list.head # n2-> 左边的第一个节点
res = True
# n2,n1往中间走
while n1 != None and n2 != None: # n1在最右边,n2在最左边
if n1.value != n2.value:
res = False
break
n1 = n1.next
n2 = n2.next
n1 = n3.next
n3.next = None
while n1 != None: # 再将链表转回来
n2 = n1.next
n1.next = n3
n3 = n1
n1 = n2
return res
2 单链表分左中右区域(1)
在使用荷兰国旗问题解决时遇到了返回头节点只输出了一个值
错误原因 return nodeArr[0] ,不能用return返回嘛?不能返回一串链表,实际缺只能返回一个节点??
def listpartition(self,pivot): # pivot 是指定的分隔数
if self.head is None: # 链表为空返回头节点
return self.head
nodeArr = [] # 创建一个数组
i = 0
cur = self.head
while cur is not None: # 将节点全部放入数组
nodeArr.append(cur) # nodeArr是数组,存放的是节点
cur = cur.next
self.partition(nodeArr,0,len(nodeArr)-1,pivot) # 对节点数组进行快速排序
for i in range(1,len(nodeArr)):
nodeArr[i-1].next = nodeArr[i] # 第一个节点与下一个节点相连
nodeArr[i].next = None
current = nodeArr[0]
while current is not None: # 证明了节点之间是相连接的
print(current.value)
current = current.next
return nodeArr[0] # 返回头节点
探讨return就此过去,耗费时间过长
修改正确
class Node:
def __init__(self,value):
self.value = value
self.next = None
class Linklist:
def __init__(self):
self.head = None
def pushlist(self,value): # 尾插法
node = Node(value)
node.next = None
if self.head is None:
self.head = node
else:
cur = self.head
while cur.next is not None:
cur = cur.next
cur.next = node
def bianli(self):
if self.head is None:
return
else:
node_p = self.head
while node_p is not None:
print(node_p.value)
node_p = node_p.next
def lenght(self):
if self.head is None:
return 0
node_p = self.head
count = 1
while node_p is not None:
node_p = node_p.next
count = count + 1
return count
def listpartition(self,pivot): # pivot 是指定的分隔数
if self.head is None: # 链表为空返回头节点
return self.head
nodeArr = [] # 创建一个数组
i = 0
cur = self.head
while cur is not None: # 将节点全部放入数组
nodeArr.append(cur) # nodeArr是数组,存放的是节点
cur = cur.next
self.partition(nodeArr,0,len(nodeArr)-1,pivot) # 对节点数组进行快速排序
for i in range(1,len(nodeArr)):
nodeArr[i-1].next = nodeArr[i] # 第一个节点与下一个节点相连
nodeArr[i].next = None
self.head = nodeArr[0] # 接受返回头节点
def swap(self,arr, i, j):
tmp = arr[i]
arr[i] = arr[j]
arr[j] = tmp
def partition(self,arr,L,R,pri):
if L > R:
return -1
if L == R:
return L
less = L - 1 # 小于区域右边界,不包括左边第一个数
more = R + 1 # 大于区域左边界,不包括右边第一个数
index = L # 左边第一个数
while index < more: # 左右两个指针没有相遇时
if arr[index].value == pri: # 以pri作分割
index = index + 1
elif arr[index].value < pri:
less = less + 1 # 小于区域向右扩一个位置
self.swap(arr, index, less) # 把小值和小于区域的数交换
index = index + 1 # index移动到下一个
else: # 第一次,如果右边第一个数大于左边
more = more - 1 # 大于区域向左扩一个位置,more = 5
self.swap(arr, index, more) # R[5] 与 左边第一个数交换
L = Linklist()
L.pushlist(10) # 长度为9
L.pushlist(5)
L.pushlist(4)
L.pushlist(1)
L.listpartition(4) # 以4分割
L.bianli()
对链表返回节点做实验—>
优化 对链表内的数做快排
class Node:
def __init__(self,value):
self.value = value
self.next = None
class Linklist:
def __init__(self):
self.head = None
def pushlist(self,value): # 尾插法
node = Node(value)
node.next = None
if self.head is None:
self.head = node
else:
cur = self.head
while cur.next is not None:
cur = cur.next
cur.next = node
def bianli(self):
if self.head is None:
return
else:
node_p = self.head
while node_p is not None:
print(node_p.value)
node_p = node_p.next
def lenght(self,list):
if self.head is None:
return 0
node_p = self.head
count = 1
while node_p is not None:
node_p = node_p.next
count = count + 1
return count
def listpartition(self,list):
if list.head is None: # 链表为空返回头节点
return list.head
nodeArr = [] # 创建一个数组
i = 0
cur = list.head
while cur is not None: # 将节点全部放入数组
nodeArr.append(cur) # nodeArr是数组,存放的是节点
cur = cur.next
self.quickSort(nodeArr) # 对节点数组进行快速排序
for i in range(1,len(nodeArr)):
nodeArr[i-1].next = nodeArr[i] # 第一个节点与下一个节点相连
nodeArr[i-1].next = None
return nodeArr[0] # 返回头节点
def swap(self,arr, i, j):
tmp = arr[i]
arr[i] = arr[j]
arr[j] = tmp
def quickSort(self,nodeArr):
if nodeArr == None or len(nodeArr) < 2:
return
self.process(nodeArr,0,len(nodeArr)-1)
def process(self,arr,L,R):
if L >= R:
return
equalArea = self.partition(arr, L, R) # equalArea接受的是中间数的位置
self.process(arr, L, equalArea[0] - 1) # equalArea[0] - 1 是小于区域的最后一个数
self.process(arr, equalArea[1] + 1, R) # equalArea[1] + 1 是大于区域的第一个数
def partition(self,arr,L,R):
if L > R:
return [-1, -1]
if L == R:
return [L, R]
less = L - 1 # 小于区域右边界,不包括左边第一个数
more = R # 大于区域左边界,[6],more = 6,包括了右边第一个数,要固定右边第一个数
index = L # 左边第一个数
while index < more: # 左右两个指针没有相遇时
if arr[index].value == arr[R].value: # 以arr[R]作分割
index = index + 1
elif arr[index].value < arr[R].value:
less = less + 1 # 小于区域向右扩一个位置
self.swap(arr, index, less) # 把小值和小于区域的数交换
index = index + 1 # index移动到下一个
else: # 第一次,如果右边第一个数大于左边
more = more - 1 # 大于区域向左扩一个位置,more = 5,先将R[6]固定在最后一个位置上
self.swap(arr, index, more) # R[5] 与 左边第一个数交换,将R[6]数固定在最后一个位置上
self.swap(arr, more, R) # 将大于区域的第一个数和R(固定的数)位置上交换,接触R位置固定
return [less + 1, more] # 返回等于区域的位置,返回大小为2的数组,第一个数是等于区域最左边的数,第二个数是等于区域最右边的数
L = Linklist()
L.pushlist('1')
L.pushlist('2')
L.pushlist('3')
L.pushlist('3')
L.pushlist('2')
L.pushlist('1')
L.listpartition(L)
L.bianli()
3 完成链表复制,返回头节点
next无环
3.1 使用哈希表(字典)
class Node:
def __init__(self,value):
self.value = value
self.next = None
self.rand = None
class Linklist:
def __init__(self):
self.head = None
self.map = dict()
def copylist(self):
cur = self.head
while cur is not None:
# 老节点克隆新节点放到map去
new_node = Node(cur.value)
self.map.update({cur:new_node}) # 往字典里面放值
cur = cur.next
# 老节点新节点已经克隆好了,新关系已存在map里
cur = self.head
while cur is not None:
# cur 老节点 , 因为cur是key,get是value
# map.get(cur) 新节点
# 得到当前节点的value,也就是新节点,新节点的next指向老节点下一个对应的新节点
self.map.get(cur).next = self.map.get(cur.next)
self.map.get(cur).rand = self.map.get(cur.rand)
cur = cur.next
# 返回给self.head的克隆头节点
self.head = self.map.get(self.head)
3.2 不用哈希表
人为构造关系
class Node:
def __init__(self,value):
self.value = value
self.next = None
self.rand = None
class Linklist:
def __init__(self):
self.head = None
self.map = dict()
def copylist(self):
if self.head == None:
return None
cur = self.head
# 复制节点,并且将复制节点放到中间
# 1->2
# 1->1'>2
while cur is not None:
# cur 老节点
next = cur.next
cur.next = Node(cur.value)
cur.next.next = next # cur将新节点指向下一个老节点
cur = next # cur到下一个老节点
cur = self.head
# 设置克隆的rand指针
# 1->1'->2->2'
while cur is not None:
# cur 新节点
# cur.next 新复制的节点
next = cur.next.next # next指向下一个老节点,中间是有新节点的
curCopy = cur.next # 指向第一个新节点
if cur.rand is not None: # 如果老节点不是空
# 新节点的rand就指向老节点rand指向的下一个,因为位置关系是确定的
curCopy.rand = cur.rand.next
else:
curCopy.rand = None
cur = next
res = self.head.next
cur = self.head
# 分离新旧节点
while cur is not None:
next = cur.next.next # 指向旧节点的第二个
curCopy = cur.next # 指向新节点
cur.next = next # 第一个旧节点断开与第一个新节点链接,链接到第二个旧节点
if next is not None:
curCopy.next = next.next # 新节点的下一个为第二个旧节点的下一个新节点
else:
curCopy.next = None
cur = next
self.head = res
4 两个头节点相交
4.1 情况一:单链表有环找交点
使用字典找环入口处
使用快慢指针找环入口处
def getLoopNode(list): # 找到链表第一个入环节点,如果无环,返回None
if list.head == None or list.head.next == None or list.head.next.next == None: # 2个节点没有环,保证节点数量大于3
return None
n1 = list.head.next # 慢指针
n2 = list.head.next.next # 快指针
while n1 != n2:
if n2.next == None or n2.next.next == None:
return None
n2 = n2.next.next
n1 = n1.next
n2 = list.head # 快指针回到头节点
while n1 != n2:
n1 = n1.next
n2 = n2.next
return n1
L = Linklist()
L.pushlist(10) # 长度为9
L.pushlist(5)
L.pushlist(4)
L.pushlist(1)
getLoopNode(L)
4.2 情况二:单链表无环找交点
两条链表相交之后剩下的只有公共部分了,因为next只能指向一个
所以两个链表最后一个节点地址相同,说明已经相交
# 如果两个链表都无环,返回第一个相交节点,如果不相交,返回None
def noLoop(list1,list2):
if list1.head == None or list2.head == None:
return None
cur1 = list1.head
cur2 = list2.head
n = 0
while cur1.next != None: # 让cur1指针指向尾节点
n = n + 1
cur1 = cur1.next
while cur2.next != None: # 让cur2指针指向尾节点
n = n - 1
cur2 = cur2.next
if cur1 != cur2: # 如果尾节点不相交,返回None
return None
# n值 ,链表1长度减去链表2长度的值
if n > 0: # 谁长,谁的头变成cur1,重定向
cur1 = list1.head
else:
cur1 = list2.head
if cur1 == list1.head: # 谁短,谁的头变成cur2
cur2 = list2.head
else:
cur2 = list1.head
n = abs(n)
while n != 0:
n = n - 1 # 长链表先走差值数
cur1 = cur1.next
while cur1 != cur2:
cur1 = cur1.next
cur2 = cur2.next
return cur1
4.3 情况三:相交直线+环
loop1 loop2 是相交的第一个节点地址
# 两个有环链表,返回第一个相交节点,如果不相交返回None
def bothLoop(list1,loop1,list2,loop2):
if loop1 == loop2: # loop是第一个相交节点地址
cur1 = list1.head
cur2 = list2.head
n = 0
while cur1 != loop1: # 遇到loop1停
n = n + 1
cur1 = cur1.next
while cur2 != loop2: # 遇到loop2停
n = n - 1
cur2 = cur2.next
if n > 0:
cur1 = list1.head
else:
cur1 = list2.head
if cur1 == list1.head: # 谁短,谁的头变成cur2
cur2 = list2.head
else:
cur2 = list1.head
n = abs(n)
while n != 0:
n = n - 1 # 长链表先走差值数
cur1 = cur1.next
while cur1 != cur2:
else:
cur1 = loop1.next
while cur1 != loop1:
if cur1 == loop2:
return loop1 # 返回loop1 loop2都行,都是第一个相交的节点
cur1 = cur1.next
return None # 否则不相交
4.4 主函数
def getInters(list1,list2):
if list1.head == None or list2.head == None:
return None
loop1 = getLoopNode(list1.head)
loop2 = getLoopNode(list2.head)
if loop1 == None and loop2 == None:
return noLoop(list1.head,list2.head)
if loop1 != None and loop2 != None:
return bothLoop(list1.head,loop1,list2.head,loop2)
return None