Linked List
基本上都要用dummy
dummy = Listnode()
dummy.next = head
return的时候return dummy.next--因为head的位置有可能变化了(head有可能做指针用)
1.获取第N个节点的值(从0开始)
1.判断n在Limit之间
if n < 0 and n > size - 1:
return None
2.创建指针cur, 让cur = dummy.next
3.遍历到n节点(当n = 0的时候,skip while loop, cur还是head,head是第0个节点)
while n:
cur = cur.next
n-=1
return cur
2.头部插入节点
1.创建newNode
2.让newNode指向head(dummy.next)
newNode.next = dummy.next
3.让dummy指向newNode
dummy.next = newNode
3.尾部插入节点
1.cur = dummy
2.让cur指到null之前的最后一个节点(while找到cur.next为空就停止了)
while(cur.next):
cur = cur.next
3.让cur直接指向newNode, newNode不用指向null,因为后面本来就是null
cur.next = newNode
4.第n个节点前插入节点
第n个节点是cur.next, 第n-1个节点是cur
1.cur = dummy
2.遍历寻找第n个节点(举极端的例子,n= 0,cur = dummy, 由于第n-1个节点是cur, 符合题意,所以遍历没有写错)
while n:
cur = cur.next
n-= 1
3.让newNode指向cur.next(先更新后面的边)
newNode.next = cur.next
4.让cur指向newNode(再更新前面的边)
cur.next = newNode
5.删除第n个节点
1. cur = dummy
2.第n个节点是cur.next, 第n-1个节点是cur,操作cur才能删掉第n个节点.找要删的第n个节点=cur.next
(n=0时不要while loop. cur = dummy. cur.next = head. head是要删掉的第0个节点。cur.next是想要删掉的节点)
while n:
cur = cur.next
n-=1
3.删除cur.next. 让cur直接指向cur.next.next
cur.next = cur.next.next
4.删了一个节点,size减少1
size -= 1
2. 两数相加
最好重新create一个列表的时候都用dummy开头
如果是两个列表,要对于值进行计算,遍历的时候直接 while l1不要l1.next应该会更清楚一点
要建立每一个node都需要用Listnode(),如果里面有值的话,比如将值5填进 Listnode(5)
这一题的难点在于怎么进位,用addDigit。还有就是l1和l2不一定长度一样,如果l1或者l2没有值了该如何表示。
def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:
#create dummy
dummy = ListNode(0)
cur = dummy
#进位
addDigit = 0
#只要有一个list有值,就不能断
while l1 or l2:
if l1:
val1 = l1.val
if not l1:
val1 = 0
if l2:
val2 = l2.val
if not l2:
val2 = 0
total = val1 + val2 + addDigit
#每一个node都要用listNode来建立
#这个node的值是余数
cur.next = ListNode(total%10)
#进位加到下一个node上(更新addDigit的值)
addDigit = total // 10
print(addDigit)
if l1:
l1 = l1.next
if l2:
l2 = l2.next
cur = cur.next
if addDigit:
cur.next = ListNode(addDigit)
return dummy.next
445. 两数相加 II
要倒着来,有两种方法,一种是把两个linked list倒着,相加,然后把新的linked list 再到过来
一种是取两个stack,把所有的Node push进去,然后pop出来相加,再用头插法构建新的linked list.
def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
cur = dummy = ListNode(0)
#使用两个栈,分别push() 进去各自节点, 一次pop()出来,就相当于反转
stack1 = []
stack2 = []
#l1 = [7,2,4,3]
#把l1里所有的node.val都倒进栈里
while l1:
stack1.append(l1.val)
l1 = l1.next
#l2 = [5,6,4]
#把l2里所有的node.val都倒进栈里
while l2:
stack2.append(l2.val)
l2 = l2.next
#建立dummyhead
dummy = ListNode(0)
addDigit = 0
while stack1 or stack2 or addDigit:
s1 = stack1.pop() if stack1 else 0
s2 = stack2.pop() if stack2 else 0
#pop的时候从最后pop,l1先pop 3,l2 pop 4.
#加成total
total = s1 + s2 + addDigit
cur = ListNode(total%10)
addDigit = total // 10
#头插法
#先把3+4=7这个node加进dummy后面
#然后把4+6=0这个node加入dummy后面,7的前面
#然后把2+5+1=8这个node加入dummy后面,0的前面
#然后把7+0=7这个Node加入dummy后面,8的前面
#dummy->7->8->0->7
#把cur这个node加到head后,head.next之前
cur.next = dummy.next
dummy.next = cur
#7->8->0->7
return dummy.next
24. 两两交换链表中的节点
def swapPairs(self, head: ListNode) -> ListNode:
if not head or not head.next:
return head
p = dummy = ListNode(0)
dummy.next = head
stack = []
#head = [1,2,3,4]
#把前两个1,2放进stack
while head and head.next:
stack.append(head)
stack.append(head.next)
#p移到两个node的末尾,cur移到下一个两个node的开头
#head移动到3的位置
head = head.next.next
#pop 2, 把p连接2(p现在是dummy)
a = stack.pop()
p.next = a
#pop 1,把2连接1
b = stack.pop()
a.next = b
#把1连接3(head是3)
b.next = head
#把p也往后移两个,移动到2
p = p.next.next
#最后如果剩下一个一个node(奇数)
if head:
#p后面直接接head
p.next = head
#如果没剩Node(偶数)
else:
#p后面直接接Null
p.next = None
return dummy.next
方法二:递归
def swapPairs(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
#base case(递归结束条件)
if not head or not head.next:
return head
#head = [1,2,3,4]
# 确定cur位置,cur = 2
cur = head.next
# head指向递归函数,函数的head是3,3是2的next
head.next = self.swapPairs(cur.next)
#cur = 2 指向head = 1
cur.next = head
#现在cur 是第一个数,所以从cur开始
return cur
23. 合并K个升序链表
在lists里面找第二个到最后一个: lists[1:]
head和list1 都是整个list也是pointer
def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
def mergeTwoLists(l1,l2):
#建立一个空的list
dummy = ListNode(0)
#让cur从dummy开始比较好
cur = dummy
#如果只有l1,就return l1
if not l2:
return l1
#如果只有l2,就return l2
if not l1:
return l2
#有四种情况
#有l2和l1
while l1 and l2:
#1.l1大
if l1.val > l2.val:
cur.next = l2
l2 = l2.next
#2.l2大
else:
cur.next = l1
l1 = l1.next
cur = cur.next
#3.有l1没有l2了
if l1:
cur.next = l1
#4.有l2没有l1了
if l2:
cur.next = l2
return dummy.next
if not lists:
return None
#拿一个sublist做res
res = lists[0]
#res需要和后面sublists的合并
for list in lists[1:]:
#两个变一个
res = mergeTwoLists(res, list)
return res
19. 删除链表的倒数第 N 个结点
找到链表的倒数第n个节点:
让p1从head走k步:
让p2到head:
让p1和p2一起走,走到p1 == null:
p2正好在倒数第k步:
问题:
如何删除链表中的一个Node:
用node.next = node.next.next删除
为什么使用dummy:
因为如果要删除第n个节点,我们要求的是n-1个节点,然后让node.next = node.next.next.
在要删除第一个节点(倒数第n个节点)的情况下,我们假设总共五个节点,需要删除第一个节点(倒数第五个节点),那我们就要找到倒数第六个节点,然后删除他后面的node,由于倒数第六个节点没有,所以我们要设置dummy node来找到倒数第六个节点。
此题详细图解:
碰到这个类型的题时,我们要先把快指针指向最后的null,把慢指针指向我们需要的位置(这一题中应该是n-k位置),然后把慢指针移到dummy, 快指针挪同样的步骤,然后算快指针从dummy到现在的位置需要几步(需要几步有几次for loop循环)。
这个时候, while fast, 因为fast指向的是null。
删除慢指针后面一个node,最后return dummy.next
如果创造了dummy,我们一定要return dummy.next
不确定是while fast对不对,可以写while fast != none,这样肯定取不到后面的none
def removeNthFromEnd(self, head, n):
dummy = ListNode()
#要让dummy.next = head,只创造dummy不行
dummy.next = head
p1 = dummy
p2 = dummy
for i in range(n+1):
p1 = p1.next
#fast有值,是指fast是最后一个数
while p1:
p1 = p1.next
p2 = p2.next
p2.next = p2.next.next
return dummy.next
876. 链表的中间结点
具体的图:
力扣https://leetcode.cn/problems/middle-of-the-linked-list/solution/dong-hua-yan-shi-kuai-man-zhi-zhen-876-l-qdto/中间节点我们也可以用fast和slow指针来做
fast 移动两步,slow移动一步
while fast.next有值的时候,才能移动fast,这样正好可以得到slow的值是正确的。
但是我们要让fast也有值因为如果fast没值,fast就不能有next
def middleNode(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
fast = head
slow = head
while fast and fast.next:
fast = fast.next.next
slow = slow.next
return slow
成环:
如果fast走两步,slow走一步,他们最终能相遇,那就是环
160. 相交链表
方法一:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
if not headA or not headB:
return None
node1 = headA
node2 = headB
#他俩相等的时候开始相交
while node1 != node2:
#如果里面不用if用while,那么就变成了O(n^2)了
if node1 is None:
#node1这时候已经是none了,所以直接等于headB.不是node1.next = headB
node1 = headB
else:
node1 = node1.next
if node2 is None:
node2 = headA
else:
node2 = node2.next
return node1
第二种解法:
JAVA代码
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
int lenA = 0, lenB = 0;
// 计算两条链表的长度
for (ListNode p1 = headA; p1 != null; p1 = p1.next) {
lenA++;
}
for (ListNode p2 = headB; p2 != null; p2 = p2.next) {
lenB++;
}
// 让 p1 和 p2 到达尾部的距离相同
ListNode p1 = headA, p2 = headB;
if (lenA > lenB) {
for (int i = 0; i < lenA - lenB; i++) {
p1 = p1.next;
}
} else {
for (int i = 0; i < lenB - lenA; i++) {
p2 = p2.next;
}
}
// 看两个指针是否会相同,p1 == p2 时有两种情况:
// 1、要么是两条链表不相交,他俩同时走到尾部空指针
// 2、要么是两条链表相交,他俩走到两条链表的相交点
while (p1 != p2) {
p1 = p1.next;
p2 = p2.next;
}
return p1;
}
141. 环形链表
让两个指针 slow 和 fast 分别指向链表头结点 head。每当慢指针 slow 前进一步,快指针 fast 就前进两步。
如果 fast 最终遇到空指针(注意此处由于快指针每次是走两步,所以也可能是fast.next遇到空指针,即fast.next的next为空,fast.next.next为空),说明链表中没有环;如果 fast 最终和 slow 相遇,那肯定是 fast 超过了 slow 一圈,说明链表中含有环。
def hasCycle(self, head: Optional[ListNode]) -> bool:
if not head:
return head
fast = head
slow = head
# 除了fast可能遇到空指针,fast.next也可能遇到空指针
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow == fast:
return True
return False
双指针 fast一次两步,slow一次一步,如果能追上就是有环
若存在环。开始找环。
设head到入口长度为x,入口到相遇点长度为y,相遇点到入口距离为z
y+z为环长度。有fast走两步slow走一步可知,相当于进环之后fast在一步一步追slow对吧,直到二者相遇,且这段距离绝对不大于y+z。但slow进环之前fast转了多少圈并不知道。设为n
x+y为总循环次数,x + y = n(z+y), so x = (