1.从尾到头打印链表
问题描述
输入一个链表,从尾到头打印链表每个节点的值。
考察点:链表反转
思路
- 确认是否可以改变链表结构?即链表中所有指针反向。
- 链表逆序输出,即后进先出,想到栈结构,实则就是递归。先递归输出它后面的节点,再输出该节点自身。
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution1:
# 返回从尾部到头部的列表值序列,例如[1,2,3]
def printListFromTailToHead(self, listNode):
# write code here
l = []
head = listNode
while head:
l.insert(0, head.val)
head = head.next
return l
2.链表中倒数第K个节点
问题描述
输入一个链表,输出该链表中倒数第k个结点。
考察点:链表索引,鲁棒性
思路
- 思路1:遍历2遍列表。倒数第K个,即正数n-k+1。遍历2遍列表,第一遍获取链表长度,第二遍获取n-k+1的值。
- 思路2:
- 遍历1遍列表。设置两个指针a,b,第一个指针a先走到k-1的位置;
- 两个指针再同时走,那么当第一个指针a到达链尾,那么第二个指针b到达n-(k-1)的位置。
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def FindKthToTail(self, head, k):
# write code here
#链为空,或head==None
if not head:
return head
if k <= 0:
return ListNode(0).next
p1 = head
p2 = head
#让p1向前移动k-1,若链表长度小于k,返回链表最后一个元素
for i in range(k - 1):
if p1.next:
p1 = p1.next
else:
return p1.next
#p1从(k-1)开始,p2从头开始,两者一起后移。
while p1.next:
p1 = p1.next
p2 = p2.next
return p2
3.反转链表
问题描述
输入一个链表,反转链表后,输出链表的所有元素。
考察点:链表反转,鲁棒性
常见错误:1.链表为空,或者只有一个节点时,程序崩溃。2.反转后链表出现断裂。3.反转后的头结点非原始的尾结点。
思路
设置3个指针,当前结点,前一个节点,后一个节点。
# -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
# 返回ListNode
def ReverseList(self, pHead):
# check if the linked_list is null or with only one node.
if (not pHead) or (not pHead.next):
return pHead
else:
# last saves the left node of the current node.
last = None
while pHead:
# tmp saves the right node of the current node.
tmp = pHead.next
# phead points to the left node.
pHead.next = last
# left node moves to the next
last = pHead
# current node moves to next
pHead = tmp
return last
4.合并两个排序的链表
问题描述
输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
考察点:链表排序、扫描
常见错误:1.合并失败,过程没想清楚 2.鲁棒性问题(空链表)
思路
两个指针分别指向链表的头结点,比较大小,小的赋给合并后的链,指针后移,递归。(注意链表的时刻判空)
# -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def ChooseHead(self,pHead1,pHead2):
if pHead1.val>=pHead2:
pHead2=pHead2.next
return pHead2
else:
pHead1 = pHead1.next
return pHead1
# 返回合并后列表
def Merge1(self, pHead1, pHead2):
# 代码错误,两个链判空不该放在后面,随着指针后移,随时可能为空
if pHead1==None:
if pHead2==None:
return None
else:
return pHead2
elif pHead2==None:
return pHead1
else:
comlink = self.ChooseHead(pHead1, pHead2)
while(pHead2 or pHead1):
comlink.next=self.ChooseHead(pHead1,pHead2)
comlink=comlink.next
return comlink
def Merge(self,pHead1,pHead2):
mergeHead = ListNode(1) #随机初始化链表
p = mergeHead
while pHead1 and pHead2:#时刻判空
if pHead1.val <= pHead2.val:
mergeHead.next = pHead1
pHead1 = pHead1.next
else:
mergeHead.next = pHead2
pHead2 = pHead2.next
mergeHead = mergeHead.next
if pHead1:
mergeHead.next = pHead1
elif pHead2:
mergeHead.next = pHead2
return p.next #从第二个节点开始,因为第一个节点为初始化的值
5.复杂链表的复制
问题描述
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
考察点:链表复制
链表遍历
思路1:牺牲O(N)空间
- 访问一个结点,就创建一个结点。
- 递归传入下一个结点。
思路2:牺牲O(N)空间 ,使复杂度降为O(N)
在线测试,内存超过限制
1. 遍历链表,创建结点及next指针,另外建立“
思路3:无需额外空间,复杂度为O(N)
- 在每个节点A后创建新节点A’
- 创建random指针
- 拆分:奇数节点用next相连,形成原链表;偶数节点用next相连,形成新链表
# 思路1:递归
# -*- 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
newnode = RandomListNode(pHead.label)
newnode.next = pHead.next
newnode.random = pHead.random
newnode.next = self.Clone(pHead.next)
return newnode
class Solution:
# 返回 RandomListNode
def Clone(self, pHead):
# write code here
head = pHead
ohead = None
newhead = None
random_dict = {} #键:新地址,值:新结点
hashdict = {} #键:原地址,值:新地址
while head:
# 复制头结点
pnode = RandomListNode(head.label)
pnode.random = head.random
# id()获取内存地址
hashdict[id(head)] = id(pnode)
random_dict[id(pnode)] = pnode
if newhead:
newhead.next = pnode
newhead = newhead.next
else:
newhead = pnode
ohead = pnode
newhead = ohead
while newhead:
if newhead.random != None:
newhead.random = random_dict[hashdict[id(newhead.random)]]
newhead = newhead.next
return ohead
# 思路:3
# -*- 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
pcur = pHead
# 遍历链表,在原结点后,复制新的结点
while pcur != None:
# 注意区分结点的创建,跟头指针pcur的创建
pnode = RandomListNode(pcur.label)
pnode.next = pcur.next
pcur.next = pnode
pcur = pnode.next
# 给新创建的结点,添加random指针
pcur = pHead
while pcur != None:
# 注意判空
if pcur.random!=None:
pcur.next.random = pcur.random
pcur = pcur.next.next
# 创建新链表的头结点、拆分链表
newHead = None
newCur = None
pcur = pHead
if pcur != None:
newHead = newCur = pcur.next
pcur.next = newCur.next
pcur = pcur.next
while pcur != None:
newCur.next = pcur.next
newCur = newCur.next
pcur.next = newCur.next
pcur = pcur.next
return newHead
6.二叉搜索树与双向链表
问题描述
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。
考察点:
二叉搜索树,双向链表
思路1:递归
- 将根的左子树转为双链表;若链表不空,则找到双链表的最后一位元素,与根结点拼接。
- 将根的右子树转为双链表;若链表不空,则将双链表第一位元素,与根结点拼接。
思路2:非递归
- 中序遍历搜索树(核心)
- 修改当前结点及前一结点的指针指向
# 思路1:递归
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def Convert(self, pRootOfTree):
# write code here
if not pRootOfTree:
return pRootOfTree
if not pRootOfTree.left and not pRootOfTree.right:
return pRootOfTree
left = self.Convert(pRootOfTree.left)
p = left
# 找到左子树链表的最后一个结点
while p and p.right:
p = p.right
if left:
p.right = pRootOfTree
pRootOfTree.left = p
right = self.Convert(pRootOfTree.right)
if right:
right.left = pRootOfTree
pRootOfTree.right =right
if left:
return left
else:
return pRootOfTree
# -*- coding:utf-8 -*-
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
class Solution:
def Convert(self, pRootOfTree):
# write code here
if not pRootOfTree:
return pRootOfTree
pcur = pRootOfTree
ppre = None
isFirst = True
midstack = []
count = 1
while pcur or midstack:
print 'round:%d'%count
while pcur:
midstack.append(pcur)
pcur = pcur.left
for i in midstack:
print i.val
pcur = midstack.pop()
print 'pop:',pcur.val
if isFirst:
#将中序遍历序列中的第一个结点设为链表的首
pRootOfTree = pcur
ppre = pRootOfTree
isFirst = False
else:
# 构建双向指针
ppre.right = pcur
pcur.left = ppre
# ppre向下移动
ppre = pcur
pcur = pcur.right
if ppre:
print 'ppre:',ppre.val
if pcur:
print 'pcur:',pcur.val
count+=1
print '======================='
return pRootOfTree
a = TreeNode(9)
b = TreeNode(5)
c = TreeNode(13)
d = TreeNode(4)
e = TreeNode(6)
f = TreeNode(12)
g = TreeNode(15)
a.left = b
a.right = c
b.left = d
b.right = e
c.left = f
c.right = g
s = Solution()
s.Convert(a)
round:1
9
5
4
pop: 4
ppre: 4
=======================
round:2
9
5
pop: 5
ppre: 5
pcur: 6
=======================
round:3
9
6
pop: 6
ppre: 6
=======================
round:4
9
pop: 9
ppre: 9
pcur: 13
=======================
round:5
13
12
pop: 12
ppre: 12
=======================
round:6
13
pop: 13
ppre: 13
pcur: 15
=======================
round:7
15
pop: 15
ppre: 15
=======================
<__main__.TreeNode instance at 0x0000000003E20948>
7.两个链表的第一个公共节点
问题描述
输入两个链表,找出它们的第一个公共结点。
考察点:
链表查找
思路1:存储其中一个链表,遍历另一个。空间:O(n或m),时间O(nm)
- 用一个数组存储其中一个链表中的所有元素
- 顺序遍历另一条链表,每访问一个元素,看数组中是否有该元素in,若有,返回该节点。
思路2(较优):分析两条链表结构——Y型。时间0(n+m)
- 遍历,得到两个链表的长度,找到长链表
- 拿到长链表,先走若干步,再同时在两个链表上遍历,找到第一个相同点即停止。
class Solution:
def FindFirstCommonNode(self, head1, head2):
# write code here
list1 = []
list2 = []
node1 = head1
node2 = head2
while node1:
list1.append(node1.val)
node1 = node1.next
while node2:
if node2.val in list1:
return node2
else:
node2 = node2.next
# -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def FindFirstCommonNode(self, pHead1, pHead2):
# write code here
length1 = self.GetListLength(pHead1)
length2 = self.GetListLength(pHead2)
lengthDif = abs(length1 - length2)
if length1 > length2:
pListHeadLong = pHead1
pListHeadShort = pHead2
#lengthDif = length1 - length2
else:
pListHeadLong = pHead2
pListHeadShort = pHead1
#lengthDif = length2 - length1
for i in range(lengthDif):
pListHeadLong = pListHeadLong.next
while (pListHeadLong!=None) and (pListHeadShort !=None) and (pListHeadLong!=pListHeadShort):
pListHeadLong = pListHeadLong.next
pListHeadShort = pListHeadShort.next
pFirstCommonNode = pListHeadLong
return pFirstCommonNode
def GetListLength(self,pHead):
length = 0
pNode = pHead
while pNode != None:
length += 1
pNode = pNode.next
return length
# 与上面方法相比:代码简洁,但不容易理解,运行慢一些。
class Solution:
def FindFirstCommonNode(self, pHead1, pHead2):
# write code here
p1 = pHead1
p2 = pHead2
while(p1!=p2):
p1 = pHead2 if p1==None else p1.next
p2 = pHead1 if p2==None else p2.next
return p1
8.链表中环的入口结点
问题描述
给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
思路
第一个重复出现的点,即环的入口
# -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def __init__(self):
self.mlist = []
def EntryNodeOfLoop(self, pHead):
# write code here
if not pHead:
return None
pcur = pHead
while pcur:
if not (pcur in self.mlist):
self.mlist.append(pcur)
else:
return pcur
pcur = pcur.next
return None
# 牛课上Python最好方法,比前面的方法较快。
class Solution:
def EntryNodeOfLoop(self, pHead):
# write code here
#遍历链表,环的存在,遍历遇见的第一个重复的即为入口节点
tempList = []
p = pHead
while p:
if p in tempList:
return p
else:
tempList.append(p)
p = p.next
return None
9.删除链表中重复的结点
问题描述
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5
思路
- 遍历链表,用列表temp存储所有的值。
- 遍历temp,若当前元素唯一,则创建结点并加入新的链表中。
class Solution:
def deleteDuplication(self, pHead):
temp = []
head = pHead
# 存储链表中所有结点的值
while head:
temp.append(head.val)
head = head.next
res = ListNode(None)
head = res
# 若当前结点唯一,则加入新链表中
for i in temp:
if temp.count(i) == 1:
head.next = ListNode(i)
head = head.next
return res.next