leetcode 链表相关

  • 链表的数据结构和list一样,可变的,不同的变量名指向这个内存空间后,谁操作都会让其指向发生变化。如果是str或者int则是改变就改变了索引。
  • 每一次是cur.next = xxx 仅仅是变了指向,本次指针没变化,除非是cur = cur.next 或者cur= pre这样,才是把cur的指针变化了。
  • 链表脑子里一定要有对应的图产生,没有图和运动很难作对。

链表的基本操作

链表排序 lc 148

# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def sortList(self, head: ListNode) -> ListNode:
        if not (head and head.next):
            return head
        # step1:找中点的范式,先从dummy开始
        dummy = ListNode(0)
        dummy.next = head
        # step2:两个都从dummy开始
        slow = fast = dummy
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
        # slow的下一个就是中点
        mid = slow.next
        slow.next = None

        right = self.sortList(mid)
        left = self.sortList(head)

        cur = res = ListNode(0)
        while right and left:
            if right.val<=left.val:
                cur.next = right
                right = right.next
            else:
                cur.next = left
                left = left.next
            cur = cur.next
        cur.next = left or right
        return res.next
  • 使用归并排序。

删除倒数第k个位置的节点 lc 19

class ListNode:
    def __init__(self, x):
        self.val = x
        self.next = None
class Solution:
    def removeNthFromEnd_(self, head: 'ListNode', n: 'int') -> 'ListNode':
        #别翻转了,直接求出来应该的位置
        cnt = 0
        cur = head
        while cur is not None:
            cnt+=1
            cur = cur.next
        cur = head
        for _ in range(cnt-n-1):
            cur = cur.next
        cur.next = cur.next.next
        return head

    def removeNthFromEnd(self, head: 'ListNode', n: 'int') -> 'ListNode':
        dummy = ListNode(0)
        dummy.next = head

        # step1: 快指针先走n步
        slow, fast = dummy, dummy
        for _ in range(n):
            fast = fast.next

        # step2: 快慢指针同时走,直到fast指针到达尾部节点,此时slow到达倒数第N个节点的前一个节点
        while fast and fast.next:
            slow, fast = slow.next, fast.next

            # step3: 删除节点,并重新连接
        slow.next = slow.next.next
        return dummy.next
  • 有dummy就能保证。next.next合法
  • 要么翻转,但是翻转操作比较大。
  • 直接求总体长度,或者直接用双指针是最好的。
  • 但是必须要dummyNode节点,要不倒数最后一个就不好弄了。

回文链表 lc 234

# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def isPalindrome(self, head: ListNode) -> bool:
        dummy = ListNode(0)
        dummy.next = head
        slow = fast = dummy
        while fast and fast.next:
            fast = fast.next.next
            slow = slow.next
        mid = slow.next
        slow.next = None

        stack = []
        while mid:
            stack.append(mid.val)
            mid = mid.next
        while stack:
            if head.val!=stack.pop():
                return False
            head = head.next
        return True
# 废弃
class Solution:
    def isPalindrome(self, head: ListNode) -> bool:
        slow=fast = head
        stack = []
        while fast and fast.next:
            stack.append(slow.val)
            slow = slow.next
            fast = fast.next.next
        if fast:
            slow = slow.next
        while slow is not None:
            if stack.pop()!=slow.val:
                return False
            slow = slow.next
        return True 
  • 与上一题目一样,也是由快慢指针能指向两个位置,先画一下图。
  • while是比较精彩的地方,出了while才是True,而且对于一个单独值的链表也能很好的处理。

链表合并

leetcode 21 合并2个有序链表

#class ListNode:
#	def __init__(self,x):
#		self.val = x
#		self.next = None

class Solution:
	def mergeTwoList(self,l1,l2):
		'''
		l1: ListNode
		l2: ListNode
		rtype: ListNode
		'''
		curr = dummy = ListNode(0)
		while l1 and l2:
			if l1.val < l2.val:
				curr.next = l1
				l1 = l1.next
			else:
				curr.next = l2
				l2 = l2.next
			curr = curr.next
		curr.next = l1 or l2
		return dummy.next
  • 这个题目的难度在于链表的数据类型的表示,代码上注释掉的那部分,一个类,每次都是一个数val,和指向下一个指针。是牵一发动全身的感觉。
  • 每次都是l这个指针被重新赋值的过程。

合并两个有序的list

class Solution:
	def merge(self,lst1,lst2):
	    res = []
	    while lst1 and lst2:
	        if lst1[0]<lst2[0]:
	            res.append(lst1.pop(0))
	        else:
	            res.append(lst2.pop(0))
	    res.extend(lst1 or lst2)
	    return res
  • 和上面的思路其实是一样的,都是往下舍弃,只不过是对list的操作还是对链表的操作,对链表需要用next。

合并两个有序list到第一个list里 lc 88

给你两个有序整数数组 nums1 和 nums2,请你将 nums2 合并到 nums1 中,使 nums1 成为一个有序数组。

初始化 nums1 和 nums2 的元素数量分别为 m 和 n 。你可以假设 nums1 的空间大小等于 m + n,这样它就有足够的空间保存来自 nums2 的元素。

class Solution:
    def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
        """
        Do not return anything, modify nums1 in-place instead.
        """
        while m>0 and n>0:
            #因为排序好了,所以比较要从后往前走,有点类似于冒泡
            if nums1[m-1]<nums2[n-1]:
                nums1[m-1+n]=nums2[n-1]
                n-=1
            else:
                nums1[m-1+n],nums1[m-1]=nums1[m-1],nums1[m-1+n]
                m-=1
        if m == 0 and n>0:
            nums1[:n] = nums2[:n]
  • 把整个思路想清楚,从后往前走,因为后面是大的。一个一个移动位置。

leetcode 23 合并n个有序链表

from queue import PriorityQueue
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
# 自己的思路写的无需记住特别的api
class Solution:
    def mergeKLists(self, lists: List[ListNode]) -> ListNode:
        heap = []
        # 必须要有index,要不不让形成heap
        for index,ln in enumerate(lists):
            if ln:
                heap.append((ln.val,index,ln))
        heapq.heapify(heap)

        dummy=ListNode(0)
        cur = dummy
        while heap:
            val,index,ln_index = heapq.heappop(heap)
            cur.next = ln_index
            cur = cur.next

            if ln_index.next:
                ln_index = ln_index.next
                heapq.heappush(heap,(ln_index.val,index,ln_index))
        return dummy.next

class Solution:
    def mergeKLists(self, lists):
        k = len(lists)
        q = PriorityQueue(maxsize=k)
        curr = dummy = ListNode(None)
        for list_idx, node in enumerate(lists):
            if node: 
            	q.put((node.val, list_idx, node))
        while q.qsize() > 0:
            poped = q.get()
            curr.next, list_idx = poped[2], poped[1]
            curr = curr.next
            if curr.next: q.put((curr.next.val, list_idx, curr.next))
        return dummy.next

#解法二
from queue import PriorityQueue
class Solution(object):
    def mergeKLists(self, lists):
        """
        :type lists: List[ListNode]
        :rtype: ListNode
        """
        head = point = ListNode(0)
        q = PriorityQueue()
        
        for index ,l in enumerate(lists):
            if l:
                q.put((l.val,index, l))
        while not q.empty():
            val, index,node = q.get()
            point.next = node
            point = point.next
            node = node.next
            if node:
                q.put((node.val,index, node))
        return head.next
  • 意思是说先把list的链表存放在堆里,然后再把他们从堆里取出来,还原成链表。
  • 使用堆的数据结构让整个取出来的数和排序都是O(logn)
  • 因为对原始list形成的链表集合进行enumerate的遍历,所以设置最大值与否一样,设置了最大值如果中途没有消费会堵塞,这个需要启动的多线程多进程,要不然卡住不动了。

链表有环 lc 141

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
# 废弃这个方法
class Solution:
    def hasCycle(self, head: ListNode) -> bool:
        if not head:
            return False
        # 必须错开一步否则第一开始进入就废了,但是整个思路不自然废弃掉
        slow = head
        fast = head.next

        while slow and fast and fast.next:
            if slow==fast:
                return True
            else:
            	#不相等就开足马力跑,没有环就会出去,每次只关心当前所以判断三个
                slow = slow.next
                fast = fast.next.next
        return False
        
# 比较正常的一种个想法
class Solution:
    def hasCycle(self, head: ListNode) -> bool:
        s = set()
        node = head
        while node:
            if node in s:
                return True
            else:
                s.add(node)
                node = node.next
        return False
        
class Solution:
    def hasCycle(self, head: ListNode) -> bool:
        fast = slow = head
        while fast and fast.next:
            fast = fast.next.next
            slow = slow.next
            if fast == slow:
                return True
        return False
  • 只有找中点的时候特别的需要dummy和mid=slow.next让后面的少

链表有环 lc 142

找到环的第一次接口的位置

# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def detectCycle(self, head: ListNode) -> ListNode:
        s = set()
        node = head
        while node:
            if node in s:
                return node
            else:
                s.add(node)
                node = node.next
        return 

# 和上面那个思路非常顺畅的下来
class Solution:
    def detectCycle(self, head: ListNode) -> ListNode:
        fast = slow = head
        while fast and fast.next:
            fast = fast.next.next
            slow = slow.next
            if fast == slow:
                fast = head
                while fast!=slow:
                    fast = fast.next
                    slow = slow.next
                return fast
        return

翻转链表 lc 206

class Solution:
    def reverseList(self,head):
        pre = None
        cur = head
        while cur is not None:
            tmp = cur.next
            cur.next = pre
            pre = cur
            cur = tmp
        return pre

翻转列表2 lc 92

翻转m,n之间的列表内容

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def reverseBetween(self, head: ListNode, m: int, n: int) -> ListNode:
        #新发生操作的动作
        dummynode = ListNode(0)
        dummynode.next = head
        fixm = dummynode
        for _ in range(m-1):
            fixm = fixm.next
        cur = fixm.next
        pre = None
        #这得反转时反的2->none,2这个节点有两个值指过来,1,3
        for _ in range(n-m+1):
            tmp = cur.next
            cur.next = pre
            pre = cur
            cur = tmp
        #第一个next是值,第二个是指针
        #链表必须先变后面的,在变化前面的
        fixm.next.next = cur
        fixm.next = pre
        return dummynode.next
  • 脑子里要清晰的有这个链表图的变化过程

删除排序链表中的重复元素 lc 83

输入: 1->1->2
输出: 1->2

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def deleteDuplicates(self, head: ListNode) -> ListNode:
        dummyNode = ListNode(0)
        cur = dummyNode.next = head
        while cur is not None and cur.next is not None:
            if cur.val != cur.next.val:
                cur = cur.next
            else:
            #下面这个就是在挪动链表的位置
                cur.next = cur.next.next
        return dummyNode.next
  • cur和dummyNode是节点。
  • 充分理解链表移动方法,每一次移动本节点已经串进来了,相等是本值之后的两个挪进来,还是有一个链表图的感觉,建造链表图非常重要。
  • 经常要想到的就是双指针和快慢指针。

找到链表的交叉处 lc 160

class Soultion:
    def getIntersectionNode(self,headA,headB):
        ahead = headA
        bhead = headB
        while ahead!=bhead:
            if ahead is not None:
                ahead = ahead.next
            else:
                ahead = headB
            if bhead is not None:
                bhead = bhead.next
            else:
                bhead = headA
        return ahead
  • 还是老习惯,链表要想到双指针,快慢指针。
  • 把现在的链表形成互为环,相当于对数字进行补齐。最后一定会相等,要不就是找到了,要不就是没有交点,直接是null导致的相等。

链表两两交换 lc 24

在这里插入图片描述三指针,且


class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

class Solution:
    def swapPairs(self, head: ListNode) -> ListNode:
        dummynode = ListNode(0)
        pre = dummynode
        pre.next = head
        while pre.next is not None and pre.next.next is not None:
            cur = pre.next
            future = pre.next.next

            cur.next = future.next
            future.next = cur
            pre.next = future

            pre = cur
        return dummynode.next
  • 这个题目和之前的题目不一样,之前的都是通过next去索引,这个是三指针,且变化了指向后位置自然发生了变化不是通过next,然后移动pre让整个流程一致。
  • 每一次都是一个dummynode的位置去变化下两个节点,让流程变化一致。

两数相加 II lc 445

在这里插入图片描述

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
        stack1,stack2 = [],[]
        carry = 0
        dummyNode = ListNode(0)

        while l1 is not None:
            stack1.append(l1.val)
            l1 = l1.next
        while l2 is not None:
            stack2.append(l2.val)
            l2 = l2.next

        while stack1 or stack2 or carry:
            if stack1:
                val1 = stack1.pop()
            else:
                val1 = 0
            if stack2:
                val2 = stack2.pop()
            else:
                val2 = 0
            sumval = val1+val2+carry
            val = sumval%10
            carry = sumval //10
            node = ListNode(val)
            node.next = dummyNode.next
            dummyNode.next = node
        return dummyNode.next

  • 整个流程先思考一个最简单的模式,比如说因为要从末尾加起来,但是链表是单向我的,所以必然需要遍历到尾部(有的不需要),再反着加起来,所以就遍历过去好了。先不要思考奇技淫巧的,先想简单点的实现。
  • 因为流程是复杂的,所以先思考出一个通解,比如这个val1+val2+carry,每一次都是执行这个操作,这个操作是原子的,先想出原子的操作是什么。
  • 大数相加也是这个操作。
  • 本题目是头插。

lc 面试题 02.05. 链表求和

在这里插入图片描述

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
        flag = 0
        stack = []
        while l1 or l2 or flag:
            res = 0
            if l1 is not None:
                res+=l1.val
                l1 = l1.next
            if l2 is not None:
                res+=l2.val
                l2 = l2.next
            res += flag
            if res>=10:
                res = res %10
                flag = 1
            else:
                flag = 0
            stack.append(res)
        
        head = node = ListNode(stack.pop(0))
        while stack:
            node.next = ListNode(stack.pop(0))
            node = node.next
        return head

分隔链表 lc 725

分割链表,让链表内长度最大值差1,且长的链表在前面。
输入: root = [1, 2, 3], k = 5
输出: [[1],[2],[3],[],[]]

输入:root = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], k = 3
输出: [[1, 2, 3, 4], [5, 6, 7], [8, 9, 10]]

# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def splitListToParts(self, root, k):
        """
        :type root: ListNode
        :type k: int
        :rtype: List[ListNode]
        """
        cur = root
        cnt = 0
        res = []
        while cur:
            cnt+=1
            cur = cur.next
        each_capacity = cnt//k
        rem = cnt%k
		
		#写代码流程复杂第一要思考的问题,先遍历谁
        for _ in range(k):
        	#要重新去组织一个list,所以去取内容
            each = dummyNode = ListNode(0)
            for _ in range(each_capacity):
                #这要阻断,要不一拿就是一串
                each.next = ListNode(root.val)
                each = each.next
                root = root.next
            #没必要比较大小,因为每一次就多一个,所以减到0即可
            if rem:
                each.next = ListNode(root.val)
                root = root.next
                rem-=1
            res.append(dummyNode.next)
        return res
  • 这个题目非常的好,虽然大家都说不难,但是实际开发就是这样的题目,要先想好要遍历谁,再遍历谁。
  • 可能后续要优化,但是开始还是要按照可能性写完。
  • 我卡壳的地方是对谁先进行遍历,其次是怎么每次重新开启一个链表。
  • 这个和其他的不同是这个要断开,其他的不需要断开,因为如果维护两套指针则交替进行,但是维护一个则必须断开,否则就会沿着一个方向走下去。

奇偶链表 lc 328

给定的链表奇数序号排在前面,偶数序号排在后面。

# 这两个是一样的,只不过这是我自己的风格
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def oddEvenList(self, head: ListNode) -> ListNode:
        if not head:
            return head
        dummyodd = oddNode = head
        dummyeven = evenNode = head.next
        while evenNode and evenNode.next:
            oddNode.next = evenNode.next
            oddNode = oddNode.next
            evenNode.next = oddNode.next
            evenNode = evenNode.next
        oddNode.next = dummyeven
        return dummyodd
        
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def oddEvenList(self, head: ListNode) -> ListNode:
        if not head:
            return head
            
        #一定要设置固定的头的位置,如同dummynode
        evenHead = head.next
        odd, even = head, evenHead
        while even and even.next:
            odd.next = even.next
            odd = odd.next
            even.next = odd.next
            even = even.next
        odd.next = evenHead
        return head
  • 难度是这个东西思路不对会产生一堆的指针,指针只能控制最多三个,再多就混乱了,能有两个是最好的。减少不必要的变量能让问题变得简单许多。
  • ==减少的方法是交替的使用指针进行,不是每次都开一个新的内容。==上面两两交换也是交替的用相同的指针。有种DNA解体的画面感。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值