01-從尾到頭打印鏈表
輸入一個鏈表,按鏈表值從尾到頭的順序返回一個ArrayList。
問題:1 ,如何輸入一個鏈表?—字典
2. 如何獲得鏈表值 --字典映射 ?指針 數值
3. 如何從頭到尾順序返回?---- for i in range ()返回 總體來說就是將字典轉化爲列表 (簡單說就是把這個鏈表上的節點一個個讀到列表中,然後用[::-1]切片的方法實現逆序)
b = a[i:j] 表示複製a[i]到a[j-1],以生成新的list對象補充
> a = [0,1,2,3,4,5,6,7,8,9]
b = a[i:j] # [1,2]
當i缺省時,默認爲0,即 a[:3]相當於a[0:3] ;
當j缺省時,默認爲len(alist), 即a[1:]相當於a[1:10]
當i,j都存在時,表示複製i到j的數據
當i,j都缺省時,a[:]就相當於完整複製一份ab = a[i:j:s]表示:i,j與上面的一樣,但s表示步進,
缺省爲1. 所以a[i:j:1]相當於a[i:j]
當s<0時,i缺省時,默認爲-1.
j缺省時,默認爲-len(a)-1 所以a[::-1]相當於 a[-1:-len(a)-1:-1],
也就是從最後一個元素到第一個元素複製一遍,即倒序。
http://www.cnblogs.com/mxh1099/p/5804064.html
# 實現一個鏈表類,只有一個值val和一個指向下一個節點的next'指針'
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
class Solution:
# 返回從尾部到頭部的列表值序列,例如[1,2,3]
def printListFromTailToHead(self, listNode):
# @listNode: 頭結點
# write code here
l = []
while listNode:
l.append(listNode.val)
listNode = listNode.next
return l[::-1]
# 實例化
# 創建鏈表 a->b->c
a = ListNode(1)
b = ListNode(2)
c = ListNode(3)
a.next = b
b.next = c
if __name__=='__main__':
demo = Solution()
print(demo.printListFromTailToHead(a))
02–鏈表中倒數第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
03–反轉鏈表
輸入一個鏈表,反轉鏈表後,輸出新鏈表的表頭。
思考:反轉後新鏈表的表頭是不是是原始鏈表的最後一個,先給定一個空的鏈表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
04–合併兩個排序鏈表
輸入兩個單調遞增的鏈表,輸出兩個鏈表合成後的鏈表,當然我們需要合成後的鏈表滿足單調不減規則。
思路> 同步遍歷,先找到鏈表中頭結點比較小的作爲頭結點 每一次遍歷要比較節點大小
還要判斷兩個節點長短不一致,
# -*- 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
05-複雜鏈表的複製
輸入一個複雜鏈表(每個節點中有節點值,以及兩個指針,一個指向下一個節點,另一個特殊指針指向任意一個節點),返回結果爲複製後複雜鏈表的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
06–二叉搜索樹與雙向鏈表
輸入一棵二叉搜索樹,將該二叉搜索樹轉換成一個排序的雙向鏈表。要求不能創建任何新的結點,只能調整樹中結點指針的指向。
題目描述
輸入一棵二叉搜索樹,將該二叉搜索樹轉換成一個排序的雙向鏈表。要求不能創建任何新的結點,只能調整樹中結點指針的指向。
題目分析: 二叉搜索樹 根節點大於左節點, 根節點小於右節點
比如將二元查找樹
轉換成雙向鏈表(雙向鏈表也叫雙鏈表,是鏈表的一種,它的每個數據結點中都有兩個指針,分別指向直接後繼和直接前驅。所以,從雙向鏈表中的任意一個結點開始,都可以很方便地訪問它的前驅結點和後繼結點。一般我們都構造雙向循環鏈表。)下面是數組,鏈表的一些定義操作
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
# -*- 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
07–兩個鏈表的第一個公共節點
輸入兩個鏈表,找出它們的第一個公共結點。
次將鏈表中的元素壓入兩個棧中,然後每次從兩個棧中拋出一個元素,直到拋出的結點相同時返回
後面的元素都是公共的
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
09–刪除鏈表中重複的節點
在一個排序的鏈表中,存在重複的結點,請刪除該鏈表中重複的結點,重複的結點不保留,返回鏈表頭指針。 例如,鏈表1->2->3->3->4->4->5 處理後爲 1->2->5
首先添加一個頭節點,以方便碰到第一個,第二個節點就相同的情況
2.設置 pre ,last 指針, pre指針指向當前確定不重複的那個節點,而last指針相當於工作指針,一直往後面搜索。使用3個指針,一個指向前一個節點last,一個指向當前節點p,一個指向下一個節點p->next,噹噹前節點跟後一個節點相等時,不斷往後遍歷,找到第一個不等於當前節點的節點;然後用last
指向它;噹噹前節點跟後一個不相等時,將last 後移指向p,p後移一位
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
鏈接:https://www.nowcoder.com/questionTerminal/fc533c45b73a41b0b44ccba763f866ef?f=discussion
來源:牛客網
先不管三七二十一把所有節點的值放到一個列表中,再篩選出值數量爲1的值。
再新建一個鏈表返回即可。很暴力。
class Solution:
def deleteDuplication(self, pHead):
res = []
while pHead:
res.append(pHead.val)
pHead = pHead.next
res = list(filter(lambda c: res.count(c) == 1, res))
dummy = ListNode(0)
pre = dummy
for i in res:
node = ListNode(i)
pre.next = node
pre = pre.next
return dummy.next
09-反轉鏈表
題目描述
輸入一個鏈表,反轉鏈表後,輸出新鏈表的表頭。
鏈表是通過一個個節點組成的,每個節點都包含了稱爲cargo的基本單元,它也是一種遞歸的數據結構。它能保持數據之間的邏輯順序,但存儲空間不必按照順序存儲。
如圖:
鏈表的基本元素有:
節點:每個節點有兩個部分,左邊部分稱爲值域,用來存放用戶數據;右邊部分稱爲指針域,用來存放指向下一個元素的指針。
head:head節點永遠指向第一個節點 tail: tail永遠指向最後一個節點 None:鏈表中最後一個節點的指針域爲None值
思路1:
因爲鏈表是有head和tail,而他們是有一個方向的,因此我們想直接進行類似list[::-1]這種反轉就不是太方便。在下面的算法中,我們通過將鏈表截斷,而後再拼接的方式進行反轉。
以{1,2,3}鏈表作爲例子,來說明下面算法中while迭代的流程(手工debug…)
1.將{2,3}的地址指向給tmp
2.將last=None指向pHead.next,這個時候pHead鏈表就被截斷了,pHead只剩下了1,因爲他的下一步指向了None
3.將此時的pHead,也就是1指向給last,這時候last爲{1}
4.將tmp={2,3}指向給pHead,此時這輪迭代結束,開啓下一輪. 這時,pHead是{2,3},last是{1}
5.將{3}指向給tmp
6.將{1}指向給pHead.next,也就是2的下一步值,因此這時候pHead就變成了{2,1}
7.將{2,1}指向給last
8.將{3}指向給pHead,此時這輪迭代結束,開啓下一輪. 這時,pHead是{3},last是{2,1}
9.將None指向了tmp
10.將{2,1}指向給pHead.next,也就是{3}的下一步值,這個時候pHead就變成了{3,2,1}
11.將{3,2,1}指向給last
12.將None指向給pHead,此時這輪迭代結束,while迭代結束。 這時pHead是{None},last是{3,2,1}
https://www.jianshu.com/p/e385d9c06672
根據下圖,先給定一個空的鏈表newList,然後判斷傳入的鏈表head是不是空鏈表或者鏈表元素只有一個,如果是,直接返回就可以。如果不是,則對鏈表進行迭代,然後給一個臨時變量temp存儲head.next,然後改變head.next的指向newList,然後把head賦值給newList,接着讓head等於臨時變量temp,就這樣一直迭代完整個鏈表,返回newList就可以
class Solution:
# 返回ListNode
def ReverseList(self, pHead):
if not pHead or not pHead.next:
return pHead
last =None
while pHead:
tmp = pHead.next#將下一步的地址指向給tmp
pHead.next=last#將一個新的鏈表指向給舊鏈表pHead,這個時候就把pHead截斷了,只剩下前面的鏈表值
last=pHead#將舊鏈表的地址指向給新鏈表
pHead=tmp#將舊鏈表原來的下一步只指向給pHead
return last
思路2:–遞歸
思路:假設鏈表爲[1,2,3,4,5]先迭代到鏈表末尾5,然後從5開始依次反轉整個鏈表
如下圖所示,先迭代待最後一位5,並且設置一個新的節點newList作爲反轉後鏈表的頭結點,由於整個鏈表反轉後的頭就是最後一個數,所以newList存放的一直是反轉後的頭結點的地址,將head指向的地址賦值給head->next->next指針,並且一定要記得讓head->next
=NULL,也就是斷開現在指針的鏈接,否則新的鏈表形成了環,下一層head->next->next賦值的時候會覆蓋後續的值。依次反轉。
class Solution:
# 返回ListNode
def ReverseList(self, head):
if head==None or head.next==None:
return head
newlist=ReverseList(head.next)
head.next.next=head
head.next=None
return newlist
兩個鏈表的第一個公共節點
題目描述
輸入兩個鏈表,找出它們的第一個公共結點。
題目分析:
鏈表兩個長度十分相等
鏈接:https://www.nowcoder.com/questionTerminal/6ab1d9a29e88450685099d45c9e31e46
來源:牛客網
依次將鏈表中的元素壓入兩個棧中,然後每次從兩個棧中拋出一個元素,直到拋出的結點相同時返回
後面的元素都是公共的
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