劍指offer python---鏈表

題目1–從頭到尾打印鏈表*

輸入一個鏈表,按鏈表值從尾到頭的順序返回一個ArrayList。
思路: 建立一個空列表,然後把數值一個一個加進去,然後反向輸出

# -*- coding:utf-8 -*-
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    # 返回從尾部到頭部的列表值序列,例如[1,2,3]
    def printListFromTailToHead(self, listNode):
        list1=[]
        while listNode:
            list1.append(listNode.val)
            listNode=listNode.next
        return list1[::-1]

題目2–鏈表中倒數第K個結點

輸入一個鏈表,輸出該鏈表中倒數第k個結點。
思路: 先計算出所有的節點n,倒數第k個節點即是從前往後第n-k+1個節點

class Solution:
    def FindKthToTail(self, head, k):
        if  k<=0 or head==None:
            return None
        count=0
        p=head
        while p!=None:
            count+=1 # 算出鏈表節點數
            p=p.next
        if k>count:
            return None
        number=count-k+1  #需要走的步數
        cnt=0
        p=head
        while p!=None:
            cnt=cnt+1
            if cnt==number:
                return p
            p=p.next

思路2爲了能夠只遍歷一次就能找到倒數第k個節點,可以定義兩個指針:

(1)第一個指針從鏈表的頭指針開始遍歷向前走k-1,第二個指針保持不動;

(2)從第k步開始,第二個指針也開始從鏈表的頭指針開始遍歷;

(3)由於兩個指針的距離保持在k-1,當第一個(走在前面的)指針到達鏈表的尾結點時,第二個指針(走在後面的)指針正好是倒數第k個結點。

# -*- coding:utf-8 -*-
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def FindKthToTail(self, head, k):
        if k<=0 or head==None:
            return None
        else:
            count=0
            p=head
            mark=False
            ans=head #第二個指針
            while p!=None:
                count=count+1
                if count>k:
                    ans=ans.next
                p=p.next
            if count<k:
                ans=None
            return ans

題目3–反轉鏈表

輸入一個鏈表,反轉鏈表後,輸出新鏈表的表頭。
思考:反轉後新鏈表的表頭是不是是原始鏈表的最後一個,先給定一個空的鏈表newList,然後判斷傳入的鏈表head是不是空鏈表或者鏈表元素只有一個,如果是,直接返回就可以。如果不是,則對鏈表進行迭代,然後給一個臨時變量temp存儲head.next,然後改變head.next的指向newList,然後把head賦值給newList,接着讓head等於臨時變量temp,就這樣一直迭代完整個鏈表,返回newList就可以
在這裏插入圖片描述

# -*- coding:utf-8 -*-
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:
    # 返回ListNode
    def ReverseList(self, head):
        if not head or not head.next:
            return head
        last=None
        while head:
            tmp=head.next
            head.next=last
            last=head
            head=tmp
        return last

題目4–合併兩個排序鏈表

輸入兩個單調遞增的鏈表,輸出兩個鏈表合成後的鏈表,當然我們需要合成後的鏈表滿足單調不減規則。
思路> 同步遍歷,先找到鏈表中頭結點比較小的作爲頭結點 每一次遍歷要比較節點大小
還要判斷兩個節點長短不一致,

在這裏插入圖片描述

# -*- coding:utf-8 -*-
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:
    # 返回合併後列表
    def Merge(self, pHead1, pHead2):
        dummy=ListNode(0)
        pHead=dummy
        while pHead1 and pHead2:
            if pHead1.val>=pHead2.val:
                dummy.next=pHead2
                pHead2=pHead2.next
            else:
                dummy.next=pHead1
                pHead1=pHead1.next
            dummy=dummy.next
        if pHead1:
            dummy.next=pHead1
        else:
            dummy.next=pHead2
        return pHead.next
        # write code here

題目5-複雜鏈表的複製

輸入一個複雜鏈表(每個節點中有節點值,以及兩個指針,一個指向下一個節點,另一個特殊指針指向任意一個節點),返回結果爲複製後複雜鏈表的head。(注意,輸出結果中請不要返回參數中的節點引用,否則判題程序會直接返回空)
題目描述
輸入一個複雜鏈表(每個節點中有節點值,以及兩個指針,一個指向下一個節點,另一個特殊指針指向任意一個節點),返回結果爲複製後複雜鏈表的head。(注意,輸出結果中請不要返回參數中的節點引用,否則判題程序會直接返回空)
題目分析:

1.如果鏈表爲空鏈表,則返回本身即可
2.如果非空 需要進行復制操作,如果沒有特殊指針,只需要複製next我相信大家都能很快做出來,但是加上特殊指針這就需要一定技巧,因爲特殊指針隨便指,而你每次找特殊指針所指的節點都需要從頭開始遍歷找起,這顯然複雜度高達O(n²)

方法1:

在不使用輔助空間的情況下實現O(N)的時間效率。

  1. 把複製的結點鏈接在原始鏈表的每一對應結點後面

  2. 把複製的結點的random指針指向被複制結點的random指針的下一個結點

  3. 拆分成兩個鏈表,奇數位置爲原鏈表,偶數位置爲複製鏈表,注意複製鏈表的最後一個結點的next指針不能跟原鏈表指向同一個空結點None,next指針要重新賦值None(判定程序會認定你沒有完成複製)

原文:https://blog.csdn.net/jiangjiang_jian/article/details/81490693
https://blog.csdn.net/qq_33431368/article/details/79296360

如圖
首先第一步 複製原來的鏈表,順次連接形成新鏈表

在這裏插入圖片描述

 cloNode = pHead
        while cloNode:
            #完成第一步的核心操作
            node = RandomListNode(cloNode.label)
            node.next = cloNode.next
            cloNode.next = node
              cloNode = node.next #下一次操作

第二步,利用原節點的random指向,來用複製的相應節點的random

  cloNode = pHead
        while cloNode:
            node = cloNode.next #指向複製的結點
            if cloNode.random: #如果原節點有特殊指針
                node.random = cloNode.random.next #則複製的節點的特殊指針指向原節點的特殊指針指向的下一個值  看圖更好理解一些
            cloNode = node.next

最後一步,將複製好的鏈表拆分出來,或者說將 偶數位的節點重新拆分合成新的鏈表,得到的就是複製的鏈表

在這裏插入圖片描述

 cloNode = pHead
        pHead = pHead.next
        while cloNode.next:
            #完成第三步的核心操作 此時節點指向隔了一個節點的節點
            node = cloNode.next
            cloNode.next = node.next
            
            cloNode = node #下一個節點的操作

這個操作其實就是將兩個鏈表順次全都拆分出來,一個很關鍵的步驟 pHead = pHead.next 如果沒有這句話,最後得到的pHead就是原鏈表的開頭了。

總程序如下:

# -*- coding:utf-8 -*-
# class RandomListNode:
#     def __init__(self, x):
#         self.label = x
#         self.next = None
#         self.random = None
class Solution:
    # 返回 RandomListNode
    def Clone(self, pHead):
        # write code here
        if not pHead:
            return pHead
        cloNode = pHead
        while cloNode:
            node = RandomListNode(cloNode.label)
            node.next = cloNode.next
            cloNode.next = node
            cloNode = node.next
        cloNode = pHead
        while cloNode:
            node = cloNode.next
            if cloNode.random:
                node.random = cloNode.random.next
            cloNode = node.next
        cloNode = pHead
        pHead = pHead.next
        while cloNode.next:
            node = cloNode.next
            cloNode.next = node.next
            cloNode = node
        return pHead

方法2:遞歸

class Solution:
    def Clone(self, head):
        if not head: return
        newNode = RandomListNode(head.label)
        newNode.random = head.random
        newNode.next = self.Clone(head.next)
        return newNode

方法3:

實際上我們可以通過空間換取時間,將原始鏈表和複製鏈表的結點通過哈希表對應起來,這樣查找的時間就從O(N)變爲O(1)。具體如下:

複製原始鏈表上的每個結點N創建N’,然後把這些創建出來的結點用pNext連接起來。同時把<N,N’>的配對信息方法一個哈希表中;然後設置複製鏈表中的每個結點的pSibling指針,如果原始鏈表中結點N的pSibling指向結點S,那麼在複製鏈表中,對應的N’應該指向S’。

時間複雜度:O(N)

class Solution:
    def Clone(self, head):
        nodeList = []     #存放各個節點
        randomList = []   #存放各個節點指向的random節點。沒有則爲None
        labelList = []    #存放各個節點的值
 
        while head:
            randomList.append(head.random)
            nodeList.append(head)
            labelList.append(head.label)
            head = head.next
        #random節點的索引,如果沒有則爲1   
        labelIndexList = map(lambda c: nodeList.index(c) if c else -1, randomList)
 
        dummy = RandomListNode(0)
        pre = dummy
        #節點列表,只要把這些節點的random設置好,順序串起來就ok了。
        nodeList=map(lambda c:RandomListNode(c),labelList)
        #把每個節點的random綁定好,根據對應的index來綁定
        for i in range(len(nodeList)):
            if labelIndexList[i]!=-1:
                nodeList[i].random=nodeList[labelIndexList[i]]
        for i in nodeList:
            pre.next=i
            pre=pre.next
        return dummy.next

題目6–二叉搜索樹與雙向鏈表

輸入一棵二叉搜索樹,將該二叉搜索樹轉換成一個排序的雙向鏈表。要求不能創建任何新的結點,只能調整樹中結點指針的指向。

題目描述
輸入一棵二叉搜索樹,將該二叉搜索樹轉換成一個排序的雙向鏈表。要求不能創建任何新的結點,只能調整樹中結點指針的指向。

題目分析: 二叉搜索樹 根節點大於左節點, 根節點小於右節點
比如將二元查找樹


轉換成雙向鏈表(雙向鏈表也叫雙鏈表,是鏈表的一種,它的每個數據結點中都有兩個指針,分別指向直接後繼和直接前驅。所以,從雙向鏈表中的任意一個結點開始,都可以很方便地訪問它的前驅結點和後繼結點。一般我們都構造雙向循環鏈表。)下面是數組,鏈表的一些定義操作
https://www.cnblogs.com/skywang12345/p/3561803.html

在這裏插入圖片描述
思路:
1.二叉樹中序遍歷的結果與鏈表的順序一致,所以可以採用中序遍歷的方法來修改二叉樹的指針

2.該題的關鍵是,如何將左子樹的最大值與右子樹的最小值通過根root連接起來,比如題目的8和12,這也是細節部分

3.寫遞歸程序最重要的是弄明白遞歸進入的條件、遞歸返回的狀態,如果遞歸進入時改變了環境,返回時應當恢復環境,就像棧的操作一樣

4.使用指針變量時,要記得初始化

5.該算法沒有返回鏈表頭,而是返回了root。
在這裏插入圖片描述

用中序遍歷和遞歸,且以這張圖爲第一步,後續的左右子樹均按照圖示的思路來做,並且前後不用任何中間節點。但慢慢發現,如果用遞歸,在不新建任何中間節點的情況下,我只能實現到:46810161412,並且圖示提供的思路也不夠精確。
遂求助網絡,但得到的解法均需要新建輔助節點,且絕大多數代碼不夠簡練。在牛客網該題下大家的討論中倒是有不錯的思路,只是沒有配文的情況下還需要點時間理解。鑑於自己沒查到該題精簡且詳細的解題思路,我就露個拙,實現Python解法,並配上我的理解。

思路

核心算法依舊是中序遍歷 不是從根節點開始,而是從中序遍歷得到的第一個節點開始
定義兩個輔助節點listHead(鏈表頭節點)、listTail(鏈表尾節點)。事實上,二叉樹只是換了種形式的鏈表;listHead用於記錄鏈表的頭節點,用於最後算法的返回;listTail用於定位當前需要更改指向的節點。瞭解了listHead和listTail的作用,代碼理解起來至少順暢80%。
提供我畫的算法的過程圖,有點醜,但有助於理解(幫你們畫了,你們就不用畫啦),另外圖中右上角步驟三應該是“2”標紅,“2”和“1”中間的連接爲單線黑~~~

在這裏插入圖片描述
https://blog.csdn.net/jiangjiang_jian/article/details/81637574

class Solution:
    def __init__(self):
        self.listHead = None
        self.listTail = None
    def Convert(self, pRootOfTree):
        if pRootOfTree==None:
            return
        self.Convert(pRootOfTree.left)
        if self.listHead==None:
            self.listHead = pRootOfTree
            self.listTail = pRootOfTree
        else:
            self.listTail.right = pRootOfTree
            pRootOfTree.left = self.listTail
            self.listTail = pRootOfTree
        self.Convert(pRootOfTree.right)
        return self.listHead
        # write code here

在這裏插入圖片描述在這裏插入圖片描述

鏈接:https://www.nowcoder.com/questionTerminal/947f6eb80d944a84850b0538bf0ec3a5
來源:牛客網

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def Convert(self, pRootOfTree):
        if not pRootOfTree:
            return None
         
        p = pRootOfTree
         
        stack = []
        resStack = []
         
        while p or stack:
            if p:
                stack.append(p)
                p = p.left
            else:
                node = stack.pop()
                resStack.append(node)
                p = node.right
             
        resP = resStack[0]
        while resStack:
            top = resStack.pop(0)
            if resStack:
                top.right = resStack[0]
                resStack[0].left = top
         
        return resP
遞歸版本

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def Convert(self, root):
        if not root:
            return None
        if not root.left and not root.right:
            return root
         
        # 將左子樹構建成雙鏈表,返回鏈表頭
        left = self.Convert(root.left)
        p = left
         
        # 定位至左子樹的最右的一個結點
        while left and p.right:
            p = p.right
         
        # 如果左子樹不爲空,將當前root加到左子樹鏈表
        if left:
            p.right = root
            root.left = p
         
        # 將右子樹構造成雙鏈表,返回鏈表頭
        right = self.Convert(root.right)
        # 如果右子樹不爲空,將該鏈表追加到root結點之後
        if right:
            right.left = root
            root.right = right
             
        return left if left else root

題目7–兩個鏈表的第一個公共節點

輸入兩個鏈表,找出它們的第一個公共結點。
次將鏈表中的元素壓入兩個棧中,然後每次從兩個棧中拋出一個元素,直到拋出的結點相同時返回
後面的元素都是公共的
class Solution:
def FindFirstCommonNode(self, pHead1, pHead2):
# write code here
lst1 = []
lst2 = []
result = []

    if not pHead1 or not pHead2:
        return None

    p1 = pHead1
    p2 = pHead2

    while p1:
        lst1.append(p1)
        p1 = p1.next
    while p2:
        lst2.append(p2)
        p2 = p2.next

    while lst1 and lst2:
        node1 = lst1.pop()
        node2 = lst2.pop()
        if node1 == node2:
            result.append(node1)
     
    if result:
        node = result.pop()
        return node
思路2:
直接把第一個鏈表丟到set裏面,然後遍歷第二個鏈表,找到第一個一樣的節點,時間O(M+N)

# -*- coding:utf-8 -*-
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:
    def FindFirstCommonNode(self, pHead1, pHead2):
        result_set = set()
        while pHead1:
            result_set.add(pHead1)
            pHead1 = pHead1.next
        while pHead2:
            if pHead2 in result_set:
                return pHead2
            pHead2 = pHead2.next

``

題目8–鏈表中環的入口結點

給一個鏈表,若其中包含環,請找出該鏈表的環的入口結點,否則,輸出null。
思路: 若包含環,即尾結點的下一個是頭結點
本題也是一個使用快慢雙指針解決的經典問題。受之前的題目【求倒數第k個結點】的啓發,我們假設原題中鏈表尾結點的next不是爲空,而是指向我們找到的倒數第k個結點,形成一個有k個結點的環,那麼這個結點其實也就是本題中要求的環的入口結點。也就是說只要我們知道本題中環中結點個數,就可以沿用上一題的做法解決問題。

假設有p,q兩指針,p每步跨一個結點,q每步跨兩個結點。那麼經過k步之後q比p多走過的結點數爲k。如果鏈表中沒有環,那麼q永遠在p的前面,兩指針不會相遇。如果鏈表有環,且環中結點個數爲r,那麼q會進入環內繞圈。如果兩指針會在相遇,一定是在環內,並且q比p多繞n圈;也就是說一定存在k,只要滿足k=n * r,就能使兩指針相遇。

如下圖所示,假設鏈表起點爲a,入口結點爲b,相遇點爲c,ab段長度爲m,bc段長度爲n,環長度爲r。經過上面的證明,我們知道c點一定存在,並且根據兩指針走過的路程,存在等式2(m+n+hr)= m+n+gr,這樣我們得到m=(g-2h)*r-n,也就是說ac段長度是環長度的整數倍。如果p指針重新從鏈表頭出發,q指針從相遇點c出發,步伐一致,那麼當p走到b點,q也正好走到b。
在這裏插入圖片描述

//先說個定理:兩個指針一個fast、一個slow同時從一個鏈表的頭部出發
//fast一次走2步,slow一次走一步,如果該鏈表有環,兩個指針必然在環內相遇
//此時只需要把其中的一個指針重新指向鏈表頭部,另一個不變(還在環內),
//這次兩個指針一次走一步,相遇的地方就是入口節點。

原文:https://blog.csdn.net/qq_20141867/article/details/80931915
在這裏插入圖片描述

# -*- coding:utf-8 -*-
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:
    def EntryNodeOfLoop(self, pHead):
        # write code here
        if not pHead or not pHead.next or not pHead.next.next:
            return None
        slow = pHead.next
        fast = slow.next
        # 找到相遇點
        while fast != slow and fast.next:
            slow = slow.next
            fast = fast.next.next
        if slow == fast:
            # 慢指針回到表頭,快指針留在相遇點,二者同步往前直到相遇在入口結點
            slow = pHead
            while slow != fast:
                fast = fast.next
                slow = slow.next
            return slow
        return None

就是leetcode的142. Linked List Cycle II題目,使用快慢指針,如果相遇了,那麼把一個指針調整到頭部,重新開始再相遇即可。

題目9–刪除鏈表中重複的節點

在一個排序的鏈表中,存在重複的結點,請刪除該鏈表中重複的結點,重複的結點不保留,返回鏈表頭指針。 例如,鏈表1->2->3->3->4->4->5 處理後爲 1->2->5
首先添加一個頭節點,以方便碰到第一個,第二個節點就相同的情況

2.設置 pre ,last 指針, pre指針指向當前確定不重複的那個節點,而last指針相當於工作指針,一直往後面搜索。使用3個指針,一個指向前一個節點last,一個指向當前節點p,一個指向下一個節點p->next,噹噹前節點跟後一個節點相等時,不斷往後遍歷,找到第一個不等於當前節點的節點;然後用last 指向它;噹噹前節點跟後一個不相等時,將last 後移指向p,p後移一位

鏈接:https://www.nowcoder.com/questionTerminal/fc533c45b73a41b0b44ccba763f866ef
來源:牛客網

# -*- coding:utf-8 -*-
'''
題目:在一個排序的鏈表中,存在重複的結點,請刪除該鏈表中重複的結點,重複的結點不保留,返回鏈表頭指針。 
例如,鏈表1->2->3->3->4->4->5 處理後爲 1->2->5
'''
class ListNode:
    def __init__(self, x):
        self.val = x
        self.next = None
class Solution:
    def deleteDuplication(self, pHead):
        # write code here
        if pHead == None or pHead.next == None:
            return pHead
        new_head = ListNode(-1)
        new_head.next = pHead# # 因爲需要兩個指針,一個指着重複結點上一個結點,一個指着重複結點後一個值。
        pre = new_head
        p = pHead
        nex = None
        while p != None and p.next != None:
            nex = p.next
            if p.val == nex.val:
                while nex != None and nex.val == p.val:
                    nex = nex.next
                pre.next = nex
                p = nex
            else:
                pre = p
                p = p.next
        return new_head.next
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章