不时记录一下自己刷题的想法,以免后续遗忘,所有的代码都是用Python写的,因为日后需要,尽量所有代码都用Python写了,单链表的应该没有特别大的难度的东西了,有几道题写了测试的代码,应该都能看出来哈,没有加图解,对找环不清楚的,可以去看看知乎
1、链表的翻转
【leetcode 206】
我最先想到的办法就是利用头插法,重新构造一个链表,但这样势必会增加空间复杂度
最不费时间复杂度的方法,就是利用双指针法指针就行修改就行了。。。。。双指针法的时候要注意一下改变之后,率先移动的是后面的指针,而不是前面的指针,否则pre会迷失自己的方向。。。。。。
# Definition for singly-linked list.
# 最简单的思路,找到尾节点
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def reverseList(self, head):
new_head = ListNode(val=-1 , next=None) # 假设出一个哨兵节点
cur = head
while cur:
tmp = ListNode(val=cur.val,next=new_head.next) # 注意了,python中等于号相当于起了一个别名!!!不申请新空间就出问题可还行
# tmp = cur # 这么写的话,两个指针指的是一个东西,烦死了
# tmp.next = new_head
new_head.next = tmp
cur = cur.next
return new_head.next
L5 = ListNode(5,None)
L4 = ListNode(4,L5)
L3 = ListNode(3,L4)
L2 = ListNode(2,L3)
L1 = ListNode(1,L2)
s =Solution()
res = s.reverseList(L1)
while res:
print(res.val)
res = res.next
2、链表找交点
【leetcode 160】
这个我之前没有推理过,加入过两个链表相交
两个链表相交的情况:
1 a+b
2 c + b
那么当一个链表走到结尾的时候移动到另一个链表的表头就好了,保证他们两者走的路径都是a+b+c。即便两个链表的长度一样也没关系,如果一样的话,那更容易,只要两个指的是同一个点就退出循环了呗。
两个链表不相交的情况:
-
链表长度相同,两个链表同时走到终点,终点的值不相同,注定他俩是没有交点的
-
链表长度不同,两个链表也是会同时走到终点的,因为都走了m+n的路径,直接返回None就好了
# Definition for singly-linked list.
# 内存中指向的是同一个位置的才是交点,仅仅是数值相同的话,不算是交点的
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode):
if not headA or not headB: # 有一个链表为空的话,就返回空置
return None
h_a = headA
h_b = headB
while h_a!= h_b: # 两者要是不相交,并且相等的话,一定最后会有返回值的,一定保证每一个都走了 a+b+c的距离
if h_a.next == None and h_b.next == None: # 到达链表尾部了
return None # 两者同时到达终点的情况 只有两种可能,一种是链表长度相等,一种是二者根本不相交,无论哪一种,都是不相交
elif h_b.next == None:
h_b = headA
h_a = h_a.next
elif h_a.next == None:
h_a = headB
h_b = h_b.next
else:
h_a = h_a.next
h_b = h_b.next
return h_a
3、判断链表是否有环,并且找到环的位置
【leetcode 142】
快慢指针的想法,快指针一次走两步,慢指针一次走一步,快指针如果走向了空,那么就说明没有环(但是要注意的一点是,快指针每走一步走要判断一下是不是空,要不然直接走两步会出现None.next的情况
如果要是相遇了的话,就证明有环,同时将快指针移动至链表的头部,之后快慢指针都一次走一步,这样会在环的入口处相遇,证明过程自己画一个图就很好理解的。核心思想就是快指针走的路途是慢指针的二倍,利用这个等式就可以得出,最终两个指针分在相遇点和起点出发,最终一定会在环的入口相遇的。不明白的一定一定自己画一个图,立马就明白了
# Definition for singly-linked list.
# 判断两个链表是否有环,有环的话,需要找到环的位置
# 双指针的思想,快指针先走,每次走两步,如果走的时候就遇见了None,则说明这个链表没有环
# 如果快指针和慢指针相遇了,则记录一下相遇的那一个点,两个指针一个回到表头,一个接着从这里走
# 再次相遇的时候就是,就是环的入口的位置
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
class Solution:
def detectCycle(self, head):
if not head: # 没有节点的情况
return None
fast = head # 都从表头开始走
slow = head
count = 0 # 第一次最开始的时候指定都指向了一个点,所以要处理一下第一次的点
while True:
if fast.next!=None:
fast = fast.next
if fast.next!=None:
fast = fast.next
else:
return None # 不能走两步
else:
return None # 甚至fast下一步都没有
slow = slow.next
if slow == fast:
break
# 到这里slow和fast已经相遇了
fast = head
while slow != fast:
fast = fast.next
slow = slow.next
return slow # 这就是最后相遇的点
4、删除倒数第n个节点
【leetcode 19】
也采用双指针的思想就好了,快指针先进行移动,慢指针在快指针走了n-1步之后同时进行移动,这样快指针走到终点的时候,慢指针也恰好移动至倒数第n个位置了
# Definition for singly-linked list.
# 删除倒数第n个节点,只要等到出发了n个之后再出发就好了,不保存前一个指针,直接交换值
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def removeNthFromEnd(self, head, n):
count = 0
h = ListNode(-1,head)
cur = head
prr = h
pr = head
while cur.next: # 一定要注意这里,如果不是next的话,会多移动一次,造成pr指的是cur,prr指的是应该被删除的元素
count += 1
if count >= n: # 检查一下这里是不是对的
pr = pr.next
prr = prr.next
cur = cur.next
# 找到之后,有可能删除最后一个节点
prr.next = pr.next
return h.next
L5 = ListNode(5,None)
L4 = ListNode(4,L5)
L3 = ListNode(3,L4)
L2 = ListNode(2,L3)
L1 = ListNode(1,L2)
s = Solution()
res = s.removeNthFromEnd(L1,2)
while res:
print(res.val)
res = res.next