20190524-快慢指针的各种应用
快慢指针
定义
快慢指针就是定义两根指针,移动的速度一快一慢,以此来制造出自己想要的差值。这个差值可以让我们找到链表上相应的节点以及一系列操作
应用
- 找出链表的中间节点
- 判断链表是否存在环
- 删除链表的倒数第n的节点
- 将有序链表转换为二叉搜索树
找出链表的中间节点
给定一个带有头结点 head 的非空单链表,返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。
题目描述
示例 1:
输入:[1,2,3,4,5]
输出:此列表中的结点 3 (序列化形式:[3,4,5]),返回的结点值为 3 。 (测评系统对该结点序列化表述是 [3,4,5])。
注意,我们返回了一个 ListNode 类型的对象 ans,这样:ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL.
示例 2:
输入:[1,2,3,4,5,6]
输出:此列表中的结点 4 (序列化形式:[4,5,6])
由于该列表有两个中间结点,值分别为 3 和 4,我们返回第二个结点。
示例1图示:下图为一个链表,每个方格表示一个节点,每个节点都指向下一个节点
1 |
2 |
3 |
4 |
5 |
↑中间节点
1 |
2 |
3 |
4 |
5 |
6 |
↑中间节点
测试数据
class Node(): '''定义一个链表类''' def __init__(self,val,next =None): self.val = val self.next = next #构造测试数据[1,2,3,4,5],其中1,2,3,4,5,均为Node节点 head = None for i in range(5,0,-1): head = Node(i,head) #构造测试数据[1,2,3,4,5,6],其中1,2,3,4,5,6,均为Node节点 head1 = None for i in range(6,0,-1): head1 = Node(i,head1) |
常规思路
- 拿到链表的长度。而拿到这个长度也比较简单,只需要遍历前设定一个计数count,遍历的时候 count++ ,遍历结束,就拿到单链表的长度了。
- 根据链表的长度求出链表的中间值,再次遍历链表获取中间值。由此可知求链表的中间节点值总共遍历了链表2次
- 首先定义2个指针,快指针quick,慢指针slow快指针的移动速度是慢指针移动速度的2倍,因此当快指针到达链表尾时,慢指针到达中点。
- 程序还要考虑链表结点个数的奇偶数因素,当快指针移动x次后到达表尾(1+2x),说明链表有奇数个结点,直接返回慢指针指向的数据即可。以示例链表[1,2,3,4,5,6]如下图:
代码
####################链表的中间节点值常规思路######################## #第一步:求链表的长度 def get_NodeLength(head): '''求链表的长度''' length = 0 while head: length+=1 head = head.next return length print(get_NodeLength(head))#输出5 print(get_NodeLength(head1))#输出6 #第二步:根据链表的长度求链表的中间节点 def get_ModemidValue(head): '''根据链表的长度求链表的中间节点''' length = get_NodeLength(head)#调用求链表长度函数,获取链表的长度 mid = length//2#中间节点是链表长度的一半 while mid >0: mid -=1 head = head.next return head#返回的是一个Node #检查函数返回的正确性:遍历返回的Node和其后面节点的查看结果 new_head = get_ModemidValue(head) while new_head: print(new_head.val) new_head = new_head.next #输出3,4,5 new_head1 = get_ModemidValue(head1) while new_head1: print(new_head1.val) new_head1 = new_head1.next #输出4,5,6 |
使用快慢指针思路
代码
####################使用快慢指针求链表的中间节点######################## def get_Nodemidvalue_with2point(head): '''使用快慢指针求链表的中间节点''' quick = head#快指针,每次走两步 slow = head#慢指针,每次走一步 while quick and quick.next:#循环结束条件为快指或者快指针的下一个节点不为空 quick = quick.next.next slow = slow.next return slow#返回的是一个Node #检查函数返回的正确性:遍历返回的Node和其后面节点的查看结果 new_head_with2point = get_Nodemidvalue_with2point(head) while new_head_with2point: print(new_head_with2point.val) new_head_with2point = new_head_with2point.next #输出3,4,5 new_head_with2point1 = get_Nodemidvalue_with2point(head1) while new_head_with2point1: print(new_head_with2point1.val) new_head_with2point1 = new_head_with2point1.next |
判断链表是否存在环
题目描述
给定一个链表,判断链表中是否有环。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
输入:head = [1], pos = -1
输出:false
解释:链表中没有环。
测试数据
class Node(): '''定义一个链表类''' def __init__(self,val,next =None): self.val = val self.next = next #构造测试数据[1,2,3,4,5],其中1,2,3,4,5,均为Node节点,无环 head = None for i in range(5,0,-1): head = Node(i,head) #构造测试数据[1,2,3,4,5,6,7...],其中1,2,3,4,5,6,7均为Node节点,有环 head1 = Node(7) temp = head1#temp用于构建环 for i in range(6,0,-1): head1 = Node(i,head1) temp.next = head1 |
常规思路
遍历链表,使用一个列表存储已经遍历过的节点,当链表节点指向None时候证明无环,当链表遍历的时候链表已在result中,则证明有环。
代码
def hasCycle(head): result =[] while head: if head in result: return True#有环 result.append(result) head = head.next return False#无环 print(hasCycle(head))#输出False print(hasCycle(head1))#输出True |
使用快慢指针
- 首先定义2个指针,快指针quick,慢指针slow快指针的移动速度是慢指针移动速度的2倍,如果快指针到达NULL,说明链表以NULL为结尾,没有环。如果快指针追上慢指针,则表示有环。
代码
####################使用快慢指针链表的是否有环######################## def hasCycle(head): quick = head slow = head while quick and quick.next: quick = quick.next.next slow = slow.next if quick ==slow: return True#有环 return False#无环 print(hasCycle(head))#输出False print(hasCycle(head1))#输出True |
删除链表的倒数第n的节点
题目描述
给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。
示例:
给定一个链表: 1->2->3->4->5, 和 n = 2.
当删除了倒数第二个节点后,链表变为 1->2->3->5.
说明:
给定的 n 保证是有效的。
测试数据
class Node(): '''定义一个链表类''' def __init__(self,val,next =None): self.val = val self.next = next #构造测试数据[1,2,3,4,5],其中1,2,3,4,5,均为Node节点 head = None for i in range(5,0,-1): head = Node(i,head) |
常规思路
- 拿到链表的长度。而拿到这个长度也比较简单,只需要遍历前设定一个计数count,遍历的时候 count++ ,遍历结束,就拿到单链表的长度了。
- 根据链表的长度拿到倒数第n个节点所在位置,进行删除
代码
使用快慢指针
使用2个指针,一个是quick一个是slow。先让quick走n-1步,然后再让quick和slow同时往前走,当quick走到头时,slow即是倒数第n个节点了
图示-以链表1->2->3->4->5, 和 n = 2为例
代码
def removeNthFromEnd(head,n): quick = head#quick指针 slow = head#slow指针 pre = None while quick: if quick.next is None:#当quick指向队尾的时候,slow指向的是倒数第n个节点 if pre is not None:#pre指向的是倒数第n-1个节点 pre.next = slow.next#pre非空,则删除第n个节点 else:#pre空,head = slow.next head = slow.next quick = quick.next n-=1 if n<=0:#当n<=0的时候slow开始移动 pre = slow slow = slow.next return head result = removeNthFromEnd(head,2) while result: print(result.val) result=result.next #输出结果1,2,3,5 |
将有序链表转换为二叉搜索树
题目描述
给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树。
本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。
示例:
给定的有序链表: [-10, -3, 0, 5, 9],
一个可能的答案是:[0, -3, 9, -10, null, 5], 它可以表示下面这个高度平衡二叉搜索树:
0
/ \
-3 9
/ /
-10 5
测试数据
class Node(): '''定义一个链表类''' def __init__(self,val,next =None): self.val = val self.next = next #构造测试数据[-10, -3, 0, 5, 9],其中-10, -3, 0, 5, 9均为Node节点 head = None for i in [9,5,0,-3,-10]: head = Node(i,head) class TreeNode(object): def __init__(self, x): self.val = x self.left = None self.right = None |
使用快慢指针
代码
def get_midNode(head):#获取链表的中间节点 '''获取链表的中间节点''' sp = head qp = head pre = None while qp and qp.next: pre = sp sp = sp.next qp = qp.next.next if pre is not None: pre.next = None else: head = None return head,sp def sortedListToBST(head):#转换为二叉搜索树 '''将链表的中间节点作为树的根节点,递归的方式,左子链表去构建左子树,右子链表构建右子树''' new_head,second_head = get_midNode(head) if head is None: return None result = TreeNode(second_head.val) result.left = self.sortedListToBST(new_head) result.right = self.sortedListToBST(second_head.next) return result #检验二叉树构建是否成功,如果成功,二叉树的中序遍历结果应该是升序 treenode = sortedListToBST(head) print(treenode) def treenode_inorder_traversal(TreeNode): if TreeNode is None: return [] center = [TreeNode.val] left = treenode_inorder_traversal(TreeNode.left) right = treenode_inorder_traversal(TreeNode.right) return left+center+right print(treenode_inorder_traversal(treenode))#输出:[-10, -3, 0, 5, 9] |