zoukankan      html  css  js  c++  java
  • 算法刷题之七链表

    链表

    题目:

    1. 链表删除某一个节点
    2. 删除倒数第N个节点
    3. 链表逆序
    4. 回文链表
    5. 判断链表是否有环路
    6. 找出环路链表的入口
    7. 链表排序
    8. 相交链表
    9. 两个连表生成相加链表

    链表的数据格式就是如下:

    需要注意的是:链表尾部的next为None。所以判断链表结束时,这是遍历一个链表最常用的结束方式。使用的代码:

    while p != None:
        p = p.next
    

    其实是当p已经指向了next区域,这时next为空,所以能够退出

    当使用p.next != None时,这时p指向的最有一个节点,而不是节点的next。

    while p.next != None:
        p = p.next
    

    链表删除某一个节点:

    删除节点两种方式:

    1. 当前节点指向下下个节点

    2. 将下一个节点赋值给当前节点,然后删除下一个节点

    # Definition for singly-linked list.
    # class ListNode:
    #     def __init__(self, x):
    #         self.val = x
    #         self.next = None
    
    class Solution:
        def deleteNode(self, node):
            """
            :type node: ListNode
            :rtype: void Do not return anything, modify node in-place instead.
            """
            node.val,node.next.val = node.next.val,node.val
            node.next = node.next.next
    

    删除倒数第N个节点

    有两种方式:

    1. 算出倒数N的正数num,然后移动到该节点前一个,删除节点
    2. 设置双指针,指针的间距是倒数N。当快指针到链尾时,慢指针当好到待删除的前一个。
    3. 删除节点需要考虑节点为头结点的情况。这时指针指向头结点,不易删除。解决办法是给头结点增加一个前行节点。将指针指向该前行节点。
    # Definition for singly-linked list.
    # class ListNode:
    #     def __init__(self, x):
    #         self.val = x
    #         self.next = None
    
    class Solution:
        def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
            
            # 设置头结点
            re = ListNode(-1)
    
            re.next = head
            
            slow = re
            fast = slow
            while n + 1> 0:
                fast =  fast.next
                n -= 1
    
            while fast != None:
                fast = fast.next
                slow = slow.next
    
            slow.next = slow.next.next
            return re.next
    

    链表逆序

    使用双指针可以将链表逆序。

    class Solution:
        def reverseList(self, head: ListNode) -> ListNode:
            pre = None
            cur = head
            while cur:
                temp = cur.next   # 先把原来cur.next位置存起来
                cur.next = pre
                pre = cur
                cur = temp
            return pre
    

    需要注意结束条件。当cur == None时,pre刚好在链尾的位置。返回pre就是返回了新的链表

    回文链表

    请判断一个链表是否为回文链表。
    
    示例 1:
    
    输入: 1->2
    输出: false
    示例 2:
    
    输入: 1->2->2->1
    输出: true
    进阶:
    你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?
    
    作者:力扣 (LeetCode)
    链接:https://leetcode-cn.com/leetbook/read/top-interview-questions-easy/xnv1oc/
    

    回文最常用的判断方式是:

    1. 逆序,然后比较逆序之后和逆序之前是否相同。如果相同就是回文,不同就不是回文
    2. 将前一半保存,逆序或者入栈,和后一半比较。如果都相同则是回文。

    链表回文有多种方式:

    1. 将链表中所有数据保存到列表中,使用列表的逆序来判断
    2. 利用快慢指针,前一半链表的值入栈,然后出栈和后一半比较判断
    3. 利用快慢指针,逆序前一半链表,和后一半比较
    class Solution:
        def isPalindrome(self, head: ListNode) -> bool:
            if not head or not head.next:
                return True
     
            slow = head
            fast = head
            mystack = []
    
            while fast and fast.next:
               mystack.append(slow.val)
               fast = fast.next.next
               slow = slow.next
            
            # 奇数状态,需要跨过中间的元素
            if fast:
                slow = slow.next
            
            while slow and slow.val == mystack.pop():
                slow = slow.next
    

    判断链表是否有环路

    判断链表是否有环路有两个解决方法,
    第一:快慢指针,如果存在环路,快指针一定会追上慢指针
    第二:字典。将遍历过的节点存入到字典中,每次遍历时在字典中查找,如果存在则表明有环路。

    
    
    class Node():
    
        def __init__(self, data):
            self.data = data
            self.next = None
    
    
    
    node1 = Node(100)
    node2 = Node(200)
    node3 = Node(300)
    node4 = Node(300)
    node5 = Node(200)
    node6 = Node(100)
    
    node1.next = node2
    node2.next = node3
    node3.next = node4
    node4.next = node5
    node5.next = node6
    node6.next = node1
    
    
    def cycle_link(head):
    
        fast = head
        slow = head
    
        while fast and fast.next:
            fast = fast.next.next
            slow = slow.next
            if fast == slow:
                return True  
        return False
    
    res = cycle_link(node1)
    print(res)
    
    class Solution:
        def hasCycle(self, head: ListNode) -> bool:
    
            if not head:
                return False
            
            Hash = {}
            temp = head
            while temp != None:
                if Hash.get(temp):
                    return True
                else:
                    Hash[temp] = True
                temp = temp.next
            return False
    

    找出环路链表的入口

    通过计算可以知道,快指针走的距离是慢指针距离的两倍,同时快指针比慢指针多走了一个圆的距离。所以在快慢指针相遇的地方,两个指针以相同的数据在走下去,相遇的地方就是环的入口

    class Solution:
        def detectCycle(self, head: ListNode) -> ListNode:
    
            first = head
            second = head
    
            while first and first.next:
                first = first.next.next
                second = second.next
                if first == second:
                    first = head
                    while True:
                        if first == second:
                            return first
                        first = first.next
                        second = second.next
                        
            
            return None
    

    链表排序

    使用链表这样数据结构实现排序,归并排序的实现如下:
    先分开,后合并

    def sortList(self, head: ListNode) -> ListNode:
            if not head or not head.next: 
                return head
            slow = head
            fast = head
            # 用快慢指针分成两部分
            while fast.next and fast.next.next:
                slow = slow.next
                fast = fast.next.next
            # 找到左右部分, 把左部分最后置空
            mid = slow.next
            slow.next = None
            # 递归下去
            left = self.sortList(head)
            right = self.sortList(mid)
            # 合并
            return self.merge(left, right)
    
        def merge(self, left, right):
            dummy = ListNode(0)
            p = dummy
            l = left
            r = right
    
            while l and r:
                if l.val < r.val:
                    p.next = l
                    l = l.next
                    p = p.next
                else:
                    p.next = r
                    r = r.next
                    p = p.next
            if l:
                p.next = l
            if r:
                p.next = r
            return dummy.next
    

    相交链表

    编写一个程序,找到两个单链表相交的起始节点。

    在节点 c1 开始相交。
    原理:两个链表是否有相交的地方,可以通过如下方法测试出来:
    走完A之后,从B起始点开始走,同样,走完B之后,从A开始走,这样如果有相交的位置,那么A和B到相交的位置就刚好一致。

    class Solution:
        def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
    
            if not(headA and headB):
                return None
            nodea = headA
            nodeb = headB
    
            while nodea != nodeb:
                
                if nodea is None:
                    nodea = headB
                else:
                    nodea = nodea.next
    
                if nodeb is None:
                    nodeb = headA
                else:
                    nodeb = nodeb.next
    
            return nodea
    

    两两交换链表中的节点

    给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。
    你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

    输入:head = [1,2,3,4]
    输出:[2,1,4,3]

    # Definition for singly-linked list.
    # class ListNode:
    #     def __init__(self, val=0, next=None):
    #         self.val = val
    #         self.next = next
    class Solution:
        def swapPairs(self, head: ListNode) -> ListNode:
           
            pre_head = ListNode(-1)
            pre_head.next = head
           
            pre_h = pre_head
            p = head
    
            while p and p.next:
                next_p = p.next
                p.next = next_p.next
                pre_h.next = next_p
                next_p.next = p
    
                pre_h = p
                p = p.next
    
            return pre_head.next
    

    两个连表生成相加链表

    假设链表中每一个节点的值都在 0 - 9 之间,那么链表整体就可以代表一个整数。
    给定两个这种链表,请生成代表两个整数相加值的结果链表。
    例如:链表 1 为 9->3->7,链表 2 为 6->3,最后生成新的结果链表为 1->0->0->0。

    class Solution:
        def addInList(self , head1 , head2 ):
            def reverse_fun(head):
                pre = None
                p = head
                while p:
                    temp = p.next
                    p.next = pre 
                    pre = p 
                    p = temp 
                return pre 
            re_head1 = reverse_fun(head1)
            re_head2 = reverse_fun(head2)
            d = ListNode(0)
            p = d 
            c = 0
            while re_head1 or re_head2 or c:
                if re_head1:
                    c += re_head1.val
                    re_head1 = re_head1.next
                if re_head2:
                    c += re_head2.val
                    re_head2 = re_head2.next
                p.next = ListNode(c % 10)
                p = p.next
                c = c // 10
            return reverse_fun(d.next)
    

    链表解题总结

    链表是我喜欢的数据结构,因为链表没有太多复杂操作。通常是遍历链表一直到尾节点,然后一边遍历一边配合指针操作。在链表中有几个小技巧可以好好总结一下:

    1. 使用快慢指针可以找到链表的中间位置。
      一个指针是快指针,每次走两步,一个指针是慢指针,每次走一步。当快指针到末尾时,慢指针刚好在链表的中间位置。使用场景:回文链表,归并排序
    while first and first.next:
        first = first.next.next
        second = second.next
    
    1. 带头结点的指针更好操作。在函数中,创建一个带头结点指针更方便操作。
      node = ListNode(None)
      使用场景:删除节点,翻转节点

    2. 链表取奇偶很简单,next的next就能保证奇偶

  • 相关阅读:
    tomcat的安装以及环境配置
    MySQL日期/时间函数
    docker部署tomcat
    Lambda的高级查询
    Linq的使用
    多线程编程
    反射
    匿名类型
    委托和事件
    泛型特性
  • 原文地址:https://www.cnblogs.com/goldsunshine/p/14930723.html
Copyright © 2011-2022 走看看