20190524-快慢指针的各种应用

快慢指针

定义

快慢指针就是定义两根指针,移动的速度一快一慢,以此来制造出自己想要的差值。这个差值可以让我们找到链表上相应的节点以及一系列操作

应用

  1. 找出链表的中间节点
  2. 判断链表是否存在环
  3. 删除链表的倒数第n的节点
  4. 将有序链表转换为二叉搜索树

找出链表的中间节点

给定一个带有头结点 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)

常规思路

  1. 拿到链表的长度。而拿到这个长度也比较简单,只需要遍历前设定一个计数count,遍历的时候 count++ ,遍历结束,就拿到单链表的长度了。
  2. 根据链表的长度求出链表的中间值,再次遍历链表获取中间值。由此可知求链表的中间节点值总共遍历了链表2次
  3. 首先定义2个指针,快指针quick,慢指针slow快指针的移动速度是慢指针移动速度的2倍,因此当快指针到达链表尾时,慢指针到达中点。
  4. 程序还要考虑链表结点个数的奇偶数因素,当快指针移动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

使用快慢指针

  1. 首先定义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的节点

题目描述

给定一个链表,删除链表的倒数第 个节点,并且返回链表的头结点。

示例:

给定一个链表: 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)

 

常规思路

  1. 拿到链表的长度。而拿到这个长度也比较简单,只需要遍历前设定一个计数count,遍历的时候 count++ ,遍历结束,就拿到单链表的长度了。
  2. 根据链表的长度拿到倒数第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]
posted @ 2019-05-24 08:22  何发奋  阅读(397)  评论(0编辑  收藏  举报