• 1. 数据结构---链表


    一、链表

    双指针”法解决链表问题

    背景:单链表问题由于顺序遍历的特性,有时候执行一些操作的时候会出现问题看似需要多次遍历才能获取数据。

    使用双指针法能在一次遍历中获取更多的数据,也可以节约更多的额外控件。“双指针”就是用一个快指针一个慢指针同时进行单链表的顺序扫描。

    如此就可以使用快指针的时间差给慢指针提供更多的操作信息。

    下面是两个LeetCode下的习题:

    (1)给定一个链表,删除链表的倒数第 个节点并返回头结点。

    思路:构建先导指针,快于后续指针n-1步,先导指针指向链表尾部时候,慢指针就指向倒数第n个节点

    (2)给定一个链表,判断链表中否有环。

    传统思路:找个容器把出现的点存起来,出现重复点就判断成环(缺点很明显:额外内存占用)

    双指针思路:快指针比慢指针先走,如果快指针被慢指针追上,说明进入闭环

    二、算法

    1.链表反转

    思路:

    1.头插法

    2.递归

    # -*- coding:utf-8 -*-
    class ListNode:
        def __init__(self, x):
            self.val = x
            self.next = None
    class Solution:
        # 带头结点返回ListNode
        def ReverseList(self, pHead):
            if pHead == None or pHead.next == None:
                return pHead
            cur = pHead.next.next
            pHead.next.next = None
            while cur:
                post = cur.next
                cur.next = pHead.next
                pHead.next = cur
                cur = post
            return pHead
    
        # 不带头结点,要先创建头结点
        def ReverseList1(self, pHead):
            if pHead == None or pHead.next == None:
                return pHead
            p = ListNode(0)
            p.next = pHead
            cur = p.next.next
            p.next.next = None
            while cur:
                post = cur.next
                cur.next = p.next
                p.next = cur
                cur = post
            return p.next
    
        def ReverseList2(self,pHead):
            '''
            递归,时间复杂度O(N),空间复杂度O(1)
            从后向前反转
            :param pHead:
            :return:
            '''
            if pHead == None or pHead.next == None:
                return pHead
    
            rhead = self.ReverseList2(pHead.next) #
            pHead.next.next = pHead # 把pHead节点放在尾部
            pHead.next = None
            return rhead
    

     

    2.删除排序链表中重复的节点

    题目:一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5

    # -*- coding:utf-8 -*-
    class ListNode:
        def __init__(self, x):
            self.val = x
            self.next = None
    
    class Solution:
        def deleteDuplication1(self, pHead):
            '''
            删除重复的,并且保留1个
            :param pHead:
            :return:
            '''
            if pHead == None or pHead.next == None: # 空或者只有一个节点
                return
            first,end = pHead,pHead.next
            while end != None:
                while first.val == end.val:
                    end = end.next
                first.next = end
                first = first.next
                end = end.next
            return pHead
    
        def deleteDuplication2(self, pHead):
            '''
            删除重复的,不保留
            :param pHead:
            :return:
            '''
            if pHead == None or pHead.next == None: # 空或者只有一个节点
                return pHead
            dummy = ListNode(None)
            dummy.next = pHead
            first,end = dummy,dummy.next
            flag = 0
            while end != None:
                while end.next and (end.val == end.next.val):
                    end = end.next
                    flag = 1
                if flag == 1: # 有重复
                    first.next = end.next # 删除重复的所有,end指向重复的最后一个
                    flag = 0
                else: # 无重复
                    first = first.next
                end = end.next
            return dummy.next
    # 1,2,3,3,4,4,5
    listnode1 = ListNode(1)
    listnode2 = ListNode(2)
    listnode3 = ListNode(3)
    listnode4 = ListNode(3)
    listnode5 = ListNode(4)
    listnode6 = ListNode(4)
    listnode7 = ListNode(5)
    
    listnode1.next = listnode2
    listnode2.next = listnode3
    listnode3.next = listnode4
    listnode4.next = listnode5
    listnode5.next = listnode6
    listnode6.next = listnode7
    
    
    solution = Solution()
    res = solution.deleteDuplication2(listnode1)
    while res != None:
        print(res.val)
        res = res.next
    

      

    3.两个链表的第一个公共节点

    两个单链表相交分为三种情况:

      (1)一个有环,一个没环,两个链表不可能相交

      (2)两个都没有环,求相交点或者不相交

      (3)两个都有环,求相交点或者不相交

    思路1:利用两个栈分布记录两个链表,然后一起弹出栈顶,比较是否相等,如果相等,就继续一起弹出,直到不相等为止,返回最后一次弹出的节点,即为第一个公共节点,时间复杂度O(m+n),空间复杂度O(m+n)

    思路2:headA的长度为A+C(公共长度),headB的长度为B+C,所以当headA走完A+C时,再转过来走B;headB走完B+C时,再转过来走A; 当两个节点都走了A+B+C时,两个节点相遇,返回该节点 时间复杂度O(m+n),空间复杂度O(1)

    class Solution:
        def FindFirstCommonNode(self, pHead1, pHead2):
            p1 = pHead1
            p2 = pHead2
            while (p1 != p2):
                if p1 == None:
                    p1 = pHead2
                else:
                    p1 = p1.next
                if p2 == None:
                    p2 = pHead1
                else:
                    p2 = p2.next
            return p1
    

      

    4.从尾到头打印链表

    题目:输入一个链表,按链表从尾到头的顺序返回一个ArrayList。

    # -*- coding:utf-8 -*-
    class ListNode:
        def __init__(self, x):
            self.val = x
            self.next = None
    
    class Solution:
        def __init__(self):
            self.arr = []
    
        def printListFromTailToHead(self, listNode):
            '''
            思路: 用栈存储遍历过的节点
            缺点:当链表非常长的时候,就会导致栈溢出
            :param listNode:
            :return:
            '''
            result = []
            if listNode is None:
                return []
            while listNode:
                result.append(listNode.val)
                listNode = listNode.next
            return result[::-1]
    
        def printListFromTailToHead2(self,listNode):
            '''
            思路:递归的本质是栈
            :param listNode:
            :return:
            '''
            if listNode == None:
                return []
            if listNode.next != None:
                self.printListFromTailToHead2(listNode.next)
            # print(listNode.val,end=' ')
            self.arr.append(listNode.val)
            return self.arr
    
    L1 = ListNode(1)
    L2 = ListNode(2)
    L3 = ListNode(3)
    L4 = ListNode(4)
    L5 = ListNode(5)
    L6 = ListNode(6)
    
    # head.next = L1
    L1.next = L2
    L2.next = L3
    L3.next = L4
    L4.next = L5
    L5.next = L6
    
    S = Solution()
    res = S.printListFromTailToHead2(L1)
    print(res)
    

      

    5.合并两个有序链表

    时间复杂度O(N),空间复杂度O(1)

    思路:

      1)首先将两个链表第一个值比较小的链表作为头结点,

      2)然后依次遍历两个链表的值,比较大小

      3)再把次链表的直接接到主链表上

    class ListNode:
        def __init__(self, x):
            self.val = x
            self.next = None
    
    class Solution:
        def mergeTwoLists(self, l1, l2):
            if l1 is None and l2 is None:
                return None
            if l1 is None:
                return l2
            if l2 is None:
                return l1
    
            head = None
            ptr1 = None
            ptr2 = None
            if l1.val < l2.val:
                head = l1
                ptr1 = l1
                ptr2 = l2
            else:
                head = l2
                ptr1 = l2
                ptr2 = l1
    
            while ptr1.next is not None and ptr2 is not None:
                if ptr1.val <= ptr2.val and ptr1.next.val >= ptr2.val:
                    temp = ptr1.next
                    ptr1.next = ptr2
                    ptr2 = ptr2.next
                    ptr1.next.next = temp
    
                ptr1 = ptr1.next
    
            if ptr1.next is None:
                ptr1.next = ptr2
            return head
    
        # 递归
        def merge2LinkList(self,l1,l2):
            if l1 ==  None:
                return l2
            if l2 == None:
                return l1
            if l1.val < l2.val:
                l1.next = self.merge2LinkList(l1.next,l2)
                return l1
            else:
                l2.next = self.merge2LinkList(l1,l2.next)
                return l2
    

      

    6.复杂链表的复制

    题目:输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)

    思路1:第一步复制主链节点;第二步复制设置每个节点的m_pSibling节点 时间复杂度O(N2)

    思路2:第一步复制主链节点,把<N,N'>配对信息放到哈希表中;第二步设置复制链表上的每个节点的m_pSibling,时间复杂度O(N),空间复杂度O(N)

    思路3:第一步复制主链节点,把N'链接到N的后面;第二步设置复制出来的节点的m_pSibling;第三步长链表拆分成两个两个链表,空间复杂度O(1),时间复杂度O(N)

    class Solution:
        def Clone(self, pHead):
            '''
            思路3
            :param pHead:
            :return:
            '''
            self.CloneNodes(pHead)
            self.ConnectSiblingNodes(pHead)
            return self.ReconnectNodes(pHead)
    
        def CloneNodes(self,pHead):
            '''
            复制每个节点,并插在被复制节点的后面
            :param pHead:
            :return:
            '''
            p = pHead
            while p:
                newNode = RandomListNode(None)
                newNode.label = p.label
                newNode.random = None # 不保存随机下一跳
                newNode.next = p.next
                p.next = newNode
                p = newNode.next
            return pHead
    
        def ConnectSiblingNodes(self,pHead):
            '''
            设置复制出来的节点的m_pSibling
            :param pHead:
            :return:
            '''
            p = pHead
            while p:
                newNode = p.next
                if p.random != None:
                    newNode.random = p.random.next
                p = newNode.next
            return pHead
    
        def ReconnectNodes(self,pHead):
            '''
            拆分两个链表,把奇数位置的节点用next链接起来就是原始链表,把偶数位置的节点用next链接起来就是复制链表
            :param pHead:
            :return:
            '''
            p = pHead
            pClonedHead = None
            pClonedNode = None
    
            if p:
                pClonedHead = pClonedNode = p.next
                p.next = pClonedNode.next
                p = p.next
    
            while p:
                pClonedNode.next = p.next
                pClonedNode = pClonedNode.next
                p.next = pClonedNode.next
                p = p.next
    
            return pClonedHead
    
    
    
    L1 = RandomListNode(1)
    L2 = RandomListNode(2)
    L3 = RandomListNode(3)
    L4 = RandomListNode(4)
    
    L1.next = L2
    L2.next = L3
    L3.next = L4
    
    L1.random = L3
    L2.random = L1
    s = Solution()
    res = s.Clone(L1)
    while res:
        print(res.label)
        if res.random != None:
            print(res.random)
        res = res.next
    

      

    7.链表中倒数第k个节点

    思路:快慢指针,第一个指针先走k步,然后第二个指针开始走,直到第一个指针下一跳为空,第二个指针所在的位置就是倒数第k个节点。相当于制造了一个k长度的尺子,把尺子从头往后移动,当尺子的右端与链表的末尾对齐的时候,尺子左端所在的结点就是倒数第k个结点

    class Solution:
        def FindKthToTail(self, head, k):
            if head == None or k <= 0:
                return None
            slow = fast = head
            while k > 1:
                if fast.next != None:
                    fast = fast.next
                    k -= 1
                else:
                    return None
            while fast.next != None:
                slow = slow.next
                fast = fast.next
            return slow
    

      

    8.链表中环的入口节点

    思路:

      1)先找快慢指针相遇的节点,这个节点肯定在环中;

      2)然后通过这个节点,找到环的长度;

      3)环的长度已知,可以设置快指针从头结点出发,先走环的长度的距离,再用慢指针,从头结点开始走,同时,快指针也开始走,两个指针相遇的地方就是环的入口位置

        def MeetingNode(self, head):#有头结点
            '''
            :param head:
            :return: 返回快慢指针相遇的节点
            '''
            if head == None or head.next == None:
                return None
            slow = fast = head
            try:
                while slow.next and fast.next.next:
                    slow = slow.next
                    fast = fast.next.next
                    if slow == fast:
                        return slow
            except Exception as e:
                pass
            return None
    
        def EntryNodeOfLoop(self,pHead):#没有头结点
            '''
            :param pHead:
            :return:
            '''
            meetingNode = self.MeetingNode(pHead)
            # print('meetingNode',meetingNode.val)
            if meetingNode == None:
                return None
    
            # 得到环中节点的数目
            pNode1 = meetingNode
            count = 0
            while pNode1.next != meetingNode:
                count += 1
                pNode1 = pNode1.next
    
            # print('count',count)
            pNode2,pNode3 = pHead,pHead
            for i in range(count+1): # 先走count步
                pNode2 = pNode2.next
            # print('pNode2',pNode2.val)
            while pNode2 != pNode3:
                pNode2 = pNode2.next
                pNode3 = pNode3.next
            return pNode2
    
    s = Solution()
    # head = ListNode(None)
    L1 = ListNode(1)
    L2 = ListNode(2)
    L3 = ListNode(3)
    L4 = ListNode(4)
    L5 = ListNode(5)
    L6 = ListNode(6)
    
    # head.next = L1
    L1.next = L2
    L2.next = L3
    L3.next = L4
    L4.next = L5
    L5.next = L6
    L6.next = L3
    
    res = s.EntryNodeOfLoop(L1)
    if res != None:
        print(res.val)
    

      

    参考:

    【1】Java实现单向链表

  • 相关阅读:
    H5 使用 jssdk 出现的问题 错误码:63002 签名错误
    vue 使用 scss
    JS之常用字符串处理类
    hive数仓中两个维度表如果想合并纬度产生新自增ID方法
    叉乘,判线段相交,凸包
    二分查找
    大整数运算
    判定最小生成树是否唯一
    Prim算法
    Boruvka算法
  • 原文地址:https://www.cnblogs.com/nxf-rabbit75/p/10008297.html
Copyright © 2020-2023  润新知