美文网首页
1.数据结构-链表问题

1.数据结构-链表问题

作者: 做一只有趣的芦苇 | 来源:发表于2020-10-04 21:50 被阅读0次

    链表相关问题

    1. 删除节点
    2. 链表去重
    3. 有环链表
    4. 反转链表
    5. 链表排序
    6. 链表相交
    7. 其他问题

    面试题 02.03. 删除中间节点

    1. 把b的值换成c的值 b.val = c.val
    2. 让b的后面节点变成b的后面的后面 b.next = b.next.next

    删除链表倒数第n个节点

    之前太轻视这个问题了,百度面试的时候也没有一次性写出 bug free的代码,再好好过一下 。要注意虽然双指针是很容易想到的一个思路,但是要确保写出bug free的代码

      def removeNthFromEnd(self , head , n ):
            # write code here
            p1 = p2 = head # 这个写法是没有问题的
            for _ in range(n): # 用for比while 要简洁得多,第一时间为什么想的是while
                p1 = p1.next 
             # 这里是最关键的一个部分,
             # 题目虽然说保证n是有效的,但是当倒数第N个元素是头结点的时候,
             # 那也是有效的,这是一个特殊情况,这时要返回 head.next
            if not p1:
                return head.next 
            
            while(p1.next):
                p1 = p1.next
                p2 = p2.next
               
            # 这里也要注意,p2.next.next可能不存在
            if p2.next.next:
                p2.next = p2.next.next
            else:
                p2.next = None
                
            return head
    

    这里有个类似的问题,不是删除是返回倒数第K个节点

    面试题 02.02. 返回倒数第 k 个节点

    1. 双指针
    def kthToLast(self, head: ListNode, k: int) -> int:
        a=head
        b=head
        for i in range(k):
            b=b.next
        while b:
            a=a.next
            b=b.next
        return a.val        
    
    1. 进行一次遍历放到数组中
    def kthToLast(self, head: ListNode, k: int) -> int:
        arr = []
        while(head):
            arr.append(head.val)
            head = head.next
        return arr[-k]
    

    删除链表中重复的元素

    初始思路:用两个while循环,内层while循环是一直循环到和前面的元素不相等的元素,效率较低
    第二个是双指针法, 要画画草图

    def deleteDuplicates(self, head: ListNode) -> ListNode:
            if not head: return []
            p1 = head.next
            p2 = head
            while p1:
                if p1.val == p2.val:
                    p1 = p1.next
                    if not p1:
                        p2.next = None
                else:
                    p2.next = p1
                    p2 = p1
                    p1 = p1.next
            return head
    

    判断链表是否有环

    1. 双指针法
    2. 让后面的节点的next都指向head, 然后当next==head时,说明环已经出现
    def hasCycle(self, head: ListNode) -> bool:
            if not head: return False
    
            # method 1 双指针法
            # slow = fast = head
            # while(fast and fast.next): #注意这里要判断 fast and fast.next
            #     fast = fast.next.next
            #     slow = slow.next
            #     if(slow == fast):
            #         return True
            # return False
    
            # method 2 
            p = head.next
            while(p):
                nex = p.next
                if(nex == head):
                    return True
                else:
                    p.next = head
                    p = nex
            return False
    

    衍生题目->返回环的入口位置 力扣142题

    def detectCycle(self, head: ListNode) -> ListNode:
            slow = fast = head
            while fast and fast.next:
                fast, slow = fast.next.next, slow.next
                if fast == slow: break
            else: return # 注意这里要加else 因为比如只有一个节点的情况 fast没有next,要直接返回的
            fast = head
            while fast != slow:
                fast, slow = fast.next, slow.next
            return fast
    

    反转链表

    1. 反转单链表
    def reverserLink(head):
            pre = None
            cur = head
            while cur:
                tmp = cur.next
                cur.next = pre
                pre = cur
                cur = tmp
            return pre
    
    1. K个一组反转-字节
    def reverseKGroup(self , head , k ):
            # 基本功:反转一个单链表, 这里唯一的区别是知道了tail
            def helper(head, tail):
                pre = tail.next #区别
                cur = head
                while pre!=tail: #头指针迭代到末尾:
                    tem = cur.next
                    cur.next = pre
                    pre = cur
                    cur = tem
                return tail, head
             
            hair = ListNode(0)
            hair.next = head
            pre = hair
             
            while head:
                tail = pre
                for _ in range(k):
                    tail = tail.next
                    if not tail:
                        return hair.next
                tem = tail.next
                head, tail = helper(head, tail)
                #连接回去
                pre.next = head
                tail.next = tem
                #更新状态量
                pre = tail
                head = tail.next
            return hair.next
    

    链表排序

    对于链表我们可以递归地将当前链表分为两段,然后merge,分两段的方法是使用双指针法,p1指针每次走两步,p2指针每次走一步,直到p1走到末尾,这时p2所在位置就是中间位置,这样就分成了两段。

        def sortList(self, head: ListNode) -> ListNode:
            if not head: return head
            return self.mergeSort(head)
    
        def mergeSort(self, node: ListNode) -> ListNode:
            if not node.next: return node #递归结束的标准:node后面没有节点了,直接返回node
            p1 = p2 = node
            cute = None
            while(p1 and p1.next):
                cute = p2 # 因为循环结束时 p2已经跑到后面一条链去了 所以要记录p2之前的node
                p2 = p2.next
                p1 = p1.next.next
            cute.next = None # 分开的前半条链最后一个Node的next是空
            l1 = self.mergeSort(node)
            l2 = self.mergeSort(p2)
            return self.mergeTwoLists(l1,l2)
    
        def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
            dummy = ListNode(-1)
            tmp = dummy
            while(l1 and l2):
                if(l1.val<l2.val):
                    tmp.next = l1
                    l1 = l1.next
                else:
                    tmp.next = l2
                    l2 = l2.next
                tmp = tmp.next
    
            if l1: #注意对于链表 直接if 不需要while
                tmp.next = l1
                #l1 = l1.next 注意对于链表直接接上去就ok l1无需继续往后走
            if l2:
                tmp.next = l2
                # l2 = l2.next
    
            return dummy.next
    

    143. 重排链表

    给定一个单链表 L:L0→L1→…→Ln-1→Ln ,
    将其重新排列后变为: L0→Ln→L1→Ln-1→L2→Ln-2→…

    def reorderList(self, head: ListNode) -> None:
            if not head: return None
            vec=[]
            p = head
            while(p):
                vec.append(p)
                p=p.next
            i,j=0,len(vec)-1
            while(i<j):#这一段有点意思 注意顺序
                vec[i].next=vec[j]
                i+=1#先加1为了vec[j]有next
                if(i==j): #如果相等就中止,是中间点
                    break
                vec[j].next=vec[i]
                j-=1
            vec[i].next=None #最后记得中间点next是None
    

    链表相交 160题 字节

    考虑以下两个链表: A={1,3,5,7,9,11} 和 B={2,4,9,11},相交于结点 9。 由于 B.length (=4) < A.length (=6),pB 比 pA 少经过 2 个结点,会先到达尾部。将 pB 重定向到 A 的头结点,pA 重定向到 B 的头结点后,pB 要比 pA 多走 2 个结点。因此,它们会同时到达交点。
    如果两个链表存在相交,它们末尾的结点必然相同。因此当 pA/pB 到达链表结尾时,记录下链表 A/B 对应的元素。若最后元素不相同,则两个链表不相交。

    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
            pa,pb = headA,headB
            while(pa != pb):
                pa = pa.next if pa else headB
                pb = pb.next if pb else headA
            return pa
    

    其他链表题目

    2. 两数相加

    我觉得自己的思路是没有问题的,虽然代码看上去冗余了一些,但是节省了内存空间,当然在有进位的情况下会对原来的链表造成修改

        def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
            carry = 0 
            pre = hair = ListNode()
            while(l1 and l2):
                pre.next = ListNode((l1.val + l2.val + carry)%10)
                carry = (l1.val + l2.val + carry)//10
                pre = pre.next
                l1,l2 = l1.next,l2.next
            if l1:
                pre.next = l1
                while(carry==1):
                    s, carry = (l1.val + carry)%10,(l1.val + carry)//10
                    l1.val = s
                    if l1.next: l1 = l1.next
                    elif carry == 1:
                        l1.next = ListNode(1,None)
                        break  #这里注意写程序的基本sense 什么时候跳出循环,有时只靠while后面跟的条件是不够的,判断新加了一个节点之后就可以结束了
            elif l2:
                pre.next = l2
                while(carry==1):
                    s,carry = (l2.val + carry)%10, (l2.val + carry)//10
                    l2.val = s
                    if l2.next: l2 = l2.next
                    elif carry == 1:
                        l2.next = ListNode(1,None)
                        break
            elif carry == 1:
                    pre.next = ListNode(1,None)
            return hair.next
    

    23. 合并K个升序链表

        # 假设一共有k个链表 平均长度为n 一共有nk个元素
        # 每次从k个链表头中选最小,考虑用堆
        def mergeKLists(self, lists: List[ListNode]) -> ListNode:
            import heapq
            hair = ListNode(0)
            p = hair
            heads = []
            # 用所有链表头初始化堆 时间复杂度 O(k)
            for i in range(len(lists)):
                if lists[i]:
                    heapq.heappush(heads, (lists[i].val, i)) 
                    lists[i] = lists[i].next
            # print(heads)
            while heads: # 每次用大小为k的最小堆 过滤nk个元素 时间复杂度 O(nk*log(k))
                val, idx = heapq.heappop(heads) # 弹出堆中最小值 时间复杂度为 O(log(k))
                p.next = ListNode(val)
                p = p.next
                if lists[idx]: # 将弹出节点的后续节点依次入堆
                    heapq.heappush(heads, (lists[idx].val, idx))
                    lists[idx] = lists[idx].next
            return hair.next
    

    147. 对链表进行插入排序

    def insertionSortList(self, head: ListNode) -> ListNode:
            if not head : return None # 判空
    
            dummy = ListNode(0) # 创建dummy节点
            dummy.next = head
            pivot = head # 已经完成排序的最后一个节点
            cur = head.next # 待插入节点
    
            while cur:
                if pivot.val <= cur.val: # 已经满足升序 pivot和cur均向后移动一步
                    pivot = pivot.next
                else: # 从头遍历 寻找插入位置 注意连接指针的先后顺序
                    pre = dummy 
                    while pre.next.val <= cur.val:
                        pre = pre.next
                    pivot.next = cur.next
                    cur.next = pre.next
                    pre.next = cur
                cur = pivot.next # 移动到下一个位置
            return dummy.next
    

    相关文章

      网友评论

          本文标题:1.数据结构-链表问题

          本文链接:https://www.haomeiwen.com/subject/ulcouktx.html