題目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)的時間效率。
把複製的結點鏈接在原始鏈表的每一對應結點後面
把複製的結點的random指針指向被複制結點的random指針的下一個結點
拆分成兩個鏈表,奇數位置爲原鏈表,偶數位置爲複製鏈表,注意複製鏈表的最後一個結點的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