链表篇
24. 两两交换链表中的节点
- 思路1:引入哨兵节点,采取双指针pre、cur即可
class Solution(object):
def swapPairs(self, head):
auxi = ListNode(0,next = head)
pre,cur = auxi,head
while cur and cur.next:
tmp = cur.next
cur.next = tmp.next
tmp.next = cur
pre.next = tmp
pre = cur
cur = cur.next
return auxi.next
总结:头结点不确定时,可引入哨兵节点
141. 环形链表
- 思路1:快慢指针,如果成环则一定相交
- 思路2:用集合记录访问过的节点,如果存在则成环
#思路1:快慢指针
class Solution(object):
def hasCycle(self, head):
if not head: return head
fast,slow = head,head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow==fast: return True
return False
#思路2:集合
class Solution(object):
def hasCycle(self, head):
myset = set()
while head:
if head not in myset:
myset.add(head)
else: return True
head = head.next
return False
总结:链表成环,用快慢指针
142. 环形链表II
- 思路1:快慢指针,如果成环 则头节点和快慢指针相交节点分别next,最终相交即为环形相交节点
- 思路2:用集合记录访问过的节点,如果成环则重复的第一个节点即为相交节点
#思路1:快慢指针
class Solution(object):
def detectCycle(self, head):
if not head or not head.next: return None
pre,cur = head,head
while cur and cur.next:
pre = pre.next
cur = cur.next.next
if pre == cur: # 代表成环
p1 = head
p2 = cur
while p1!=p2:
p1 = p1.next
p2 = p2.next
return p1
return None
#思路2:集合
class Solution(object):
def detectCycle(self, head):
mySet = set()
while(head):
if head not in mySet:
mySet.add(head)
else:
return head
head = head.next
return None
总结:链表成环,用快慢指针
86. 分隔链表
- 思路1:需要分离链表元素的题,都可以考虑引入 多个哨兵节点,连接不同的子链表 然后再拼接起来
- 思路2:借助两个额外数组分离链表节点,然后拼接数组,再把数组的节点前后连接起来
# 思路1:哨兵节点
class Solution(object):
def partition(self, head, x):
if not head: return None
auxi1,auxi2 = ListNode(0),ListNode(0)
cur1,cur2 = auxi1,auxi2
while head:
if head.val < x:
cur1.next = head
cur1 = cur1.next
else:
cur2.next = head
cur2 = cur2.next
head = head.next
cur1.next = auxi2.next
cur2.next = None
return auxi1.next
# 借助数组
class Solution(object):
def partition(self, head, x):
if not head: return None
nums1,nums2 = [],[]
while head:
if head.val < x:
nums1.append(head)
else:
nums2.append(head)
head = head.next
nums1.extend(nums2)
for i in range(len(nums1)-1):
nums1[i].next = nums1[i+1]
nums1[-1].next = None
return nums1[0]
总结:引入多个哨兵节点分离,链表最后一个元素指向None
328. 奇偶链表
- 思路1:需要分离链表元素的题,都可以考虑引入 多个哨兵节点,连接不同的子链表 然后再拼接起来
- 思路2:借助两个额外数组分离链表节点,然后拼接数组,再把数组的节点前后连接起来
# 思路1:哨兵节点
class Solution(object):
def oddEvenList(self, head):
auxi1,auxi2 = ListNode(0),ListNode(0)
cur1,cur2 = auxi1,auxi2
cnt = 0
while head:
cnt += 1
if cnt % 2!= 0 :
cur1.next = head
cur1 = cur1.next
else:
cur2.next = head
cur2 = cur2.next
head = head.next
cur1.next = auxi2.next
cur2.next = None
return auxi1.next
# 借助数组
class Solution(object):
def oddEvenList(self, head):
if not head: return None
nums1,nums2 = [],[]
cnt = 1
while head:
if cnt%2==1:
nums1.append(head)
else:
nums2.append(head)
cnt += 1
head = head.next
nums1.extend(nums2)
for i in range(len(nums1)-1):
nums1[i].next = nums1[i+1]
nums1[-1].next = None
return nums1[0]
总结:引入多个哨兵节点分离,链表最后一个元素指向None
19. 删除链表中的倒数第N个节点
- 思路1:快慢指针,慢指针比快指针晚走n步
class Solution(object):
def removeNthFromEnd(self, head, n):
tmp = ListNode(0)
tmp.next = head
pre,cur = tmp,head
cnt = 0
while head:
cnt += 1
if cnt > n: # 慢指针晚走 n步
pre = cur
cur = cur.next
head = head.next
pre.next = cur.next # 快慢指针删除 倒数第n个节点
return tmp.next
22. 链表中倒数第k个节点
- 思路1:快慢指针,慢指针比快指针晚走k步
class Solution(object):
def getKthFromEnd(self, head, k):
pre,cur = head,head
cnt = 0
while cur:
cnt += 1
if cnt>k:
pre = pre.next
cur = cur.next
return pre
61. 旋转链表
- 思路1:先遍历到链表末尾并记录链表长度L,末尾节点指向头节点形成环,然后指向头节点指针cur移动L-k次,断开环,cur即为新头节点
class Solution(object):
def rotateRight(self, head, k):
if not head: return head
cnt,cur,pre = 0,head,head
while cur:
cnt += 1
pre = cur
cur = cur.next
pre.next = head # 成环
pre,cur = head,head
k = cnt - k % cnt
while cur:
k -= 1
pre = cur
cur = cur.next
if k == 0:
pre.next = None
break
return cur
总结:链表最后一个元素指向None
138. 复制带随机指针的链表
- 思路1:在原始链表每个节点后之间复制一份节点,然后根据奇偶性分离,即可获得新拷贝链表
- 思路2:引入字典,遍历一遍链表,将旧节点和新节点一一对应记录下来,新节点的random指针指向的位置,可以通过新节点对应的旧节点的random指针指向位置,再根据指向位置查字典获得
# 思路一
class Solution(object):
def copyRandomList(self, head):
if not head: return None
cur = head
while cur:
tmp = cur.next
cur.next = Node(cur.val,next=tmp,random=cur.random)
cur = tmp
cur = head
while cur:
tmp = cur.next.next
if cur.random:
cur.next.random = cur.random.next
else:
cur.next.random = None
cur = tmp
ret,pre,cur = head.next,head,head.next
while cur and cur.next:
tmp1 = pre.next.next
tmp2 = tmp1.next
pre.next = tmp1
cur.next = tmp2
cur = tmp2
pre = tmp1
cur.next = None
pre.next = None
return ret
# 思路二
class Solution(object):
def copyRandomList(self, head):
auxi = Node(0)
pre,mydict = auxi,{}
while head:
tmp = Node(head.val)
pre.next = tmp
pre = tmp
mydict[head] = tmp
head = head.next
for k,v in mydict.items():
if k.random: v.random = mydict[k.random] # k.random不是None,才会存字典
else: v.random = None
return auxi.next
总结:复制链表,可在原始链表每个节点后之间复制一份,然后分离;存在一一对应关系,可引入字典。
146. LRU缓存机制
- 思路1:采取双向链表+字典形式实现,字典实现快速查找,双向链表实现 将最近访问的数据节点更新到head,最近最久时间每访问的节点通过tail找到并删除
# 核心思想:双向链表+字典
class DlinkNode(object):
def __init__(self,key=0,val=0):
self.key = key
self.val = val
self.pre = None
self.next = None
class LRUCache(object):
def __init__(self, capacity):
self.head = DlinkNode()
self.tail = DlinkNode()
self.head.next = self.tail
self.tail.pre = self.head
self.capacity = capacity
self.size = 0
self.cache = {}
def get(self, key):
if key not in self.cache:
return -1
else:
node = self.cache[key]
self.moveToHead(node)
return node.val
def put(self, key, value):
if key in self.cache:
self.cache[key].val = value
self.moveToHead(self.cache[key])
else:
node = DlinkNode(key,value)
if self.size == self.capacity:
tail = self.removeTail()
self.cache.pop(tail.key)
self.size -= 1
self.cache[key] = node
self.addToHead(node)
self.size += 1
def removeNode(self,node):
node.pre.next = node.next
node.next.pre = node.pre
def addToHead(self,node):
self.head.next.pre = node
node.next = self.head.next
node.pre = self.head
self.head.next = node
def moveToHead(self,node):
self.removeNode(node)
self.addToHead(node)
def removeTail(self):
node = self.tail.pre
self.removeNode(node)
return node
总结:LRU缓存采取双向链表+字典的思想