题目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