快慢指针该如何操作?本文带你认识快慢指针常见的三种用法及在链表中的实战

简介: 快慢指针该如何操作?本文带你认识快慢指针常见的三种用法及在链表中的实战



1.简述快慢指针

(1)快慢指针只是一种说法,不是直接定义两个指针;在Java中就没有指针这个概念

(2)快慢指针定义两个引用,一般慢指针定义为slow,快指针定义为fast

(3)快慢指针常见的思想:

1.一般快指针所指向的对象需要满足某个条件,慢指针才能继续向前走

2.快慢指针一起走,但是每次快指针走的距离都比慢指针多

3.快指针先走n步,然后快慢指针再一起走

(4)常应用于数组和链表中

2.快慢指针实战讲解

下面带你一步一步学会快慢指针在链表中的应用,它是怎么运用的和应用

1.求链表的中间结点

(1)这是一道leetcode上面的题目,下面附上链接

力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

不想点进去的同学也不要慌张,下面我展示题目:

(2)理解题目

第一、这是单链表的题目

第二、本题给的例子有两个:第一个是结点数是奇数的时候,第二个是结点数是偶数的时候,用数学公式表示就是:中间结点=结点个数/2(规定第一个结点下标为0)

第三、题目要求:找到中间的结点,并返回中间结点

下面讲解这种题的解法

(3)暴力解法(不推荐)

很多同学是这么想的,要求中间结点,首先要知道中间结点的位置不就好了;然后先遍历一遍链表,记下结点的个数,算出中间结点的位置;再遍历第二遍,遍历到中间位置就停下。

缺点:缺点很明显,时间复杂度太大,如果题目要求时间复杂度为O(n),也就是只遍历一遍链表,就找出中间结点,同学该如何应对。

下面介绍本节的重点算法思想:快慢指针

(4)利用快慢指针巧解(重点)

我们再看一下题目:下面分为五步去讲解

第一步:定义两个引用指向头结点

ListNode slow = head;//慢指针
ListNode fast = head;//快指针

第二步:怎么操作?

我们规定每次,快指针走两步,慢指针走一步,当快指针停下来的时候,慢指针指向的结点就是中间结点

原理剖析:

第三步:代码演示

ListNode slow = head;
ListNode fast = head;
while(fast != null && fast.next != null) {
       fast = fast.next.next;
       slow = slow.next;
}

第四步:快指针走完的判断条件

fast != null && fast.next != null

看条件有两种,就是对应结点数是奇数或偶数的情况

奇数(条件一:fast.next != null)

偶数(条件二:fast != null)

第五步:判断特殊情况

当一个头结点都没有的时候,也就是head==null,那还找个鸡毛,直接返回就好

if(head == null) {
     return null;
 }

完整代码:

public ListNode middleNode(ListNode head) {
        if(head == null) {
              return null;
        }  
        ListNode slow = head;
        ListNode fast = head;
        while(fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
        }
        return slow;
    }

可见,使用快慢指针的效率是多么的快

2.链表中倒数第k个结点

(1)这是一道牛客网上面的题目,下面附上链接

链表中倒数第k个结点_牛客题霸_牛客网

下面附上题目:

(2)理解题目

第一、要求我们返回倒数某个结点,最后一个标号为倒数第一;

第二、题目在时间和空间上面都有限制,并且知识点里面有双指针的标签

(3)暴力求解(不推荐)

依旧是同样的套路,先遍历一遍链表,记录下结点的总数;利用结点总数-倒数某个结点,再遍历第二次链表就可以知道遍历到哪个位置便利用停下来。

这种方法的时间限制可能会超出题目的要求,也是不推荐的,下面介绍快慢指针的解法

(4)快慢指针思想(重点推荐)

第一步:定义一个fast和slow的引用,开始都指向头节点。

ListNode slow = head;
ListNode fast = head;

第二步:求倒数第K个结点,那就先让快指针(fast)先走(k-1)步

int count = k-1;//走k-1步
while(count > 0) {
    if(fast.next!=null) {
        fast = fast.next;
       count--;
   }else {
       return null;
   }
}

第三步:fast走完k-1步后,fast和slow一起走,当fast停下的时候,slow的位置就是所求的结点位置

while(fast.next != null) {
            fast = fast.next;
            slow = slow.next;
        }

原理及终止条件:

特殊情况的考虑:

第四步:特殊情况考虑

当头结点为空时,也就是一个结点都没有的时候,直接返回null

if(head == null) {
    return null;
}

当k的取值不合法时,比如是非正数,直接返回就好

if(k<=0) {
    return null;
}

完整代码:

public ListNode FindKthToTail(ListNode head,int k) {
        if(head == null) {
            return null;
        }
        if(k<=0) {
            return null;
        }
        ListNode slow = head;
        ListNode fast = head;
        int count = k-1;
        while(count > 0) {
           if(fast.next!=null) {
             fast = fast.next;
            count--;
           }else {
            return null;
           }
        }
        while(fast.next != null) {
            fast = fast.next;
            slow = slow.next;
        }
        return slow;
    }

3.删除排序链表中的所有重复元素

(1)这也是一道力扣上面的题目,下面是连接

力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

题目样貌:

(2)理解题目

第一、这是一个已经排好序的单链表。

第二、使得这个单链表的每个值只出现一次,需要把重复出现的结点删掉。

(3)使用快慢指针求解

本题,我们依然选择一个快慢指针的思想,也就是前后指针,可以做到时间复杂度为O(n),也就是只遍历一遍链表就完成

核心思想:我们让快指针先走,如果快指针指向的值和慢指针指向的一样,那就继续往下走,直到指向的值不一样,再修改慢指针的指向,完成重复结点的删除。

第一步:定义快慢指针

ListNode slow = head;
ListNode fast = head.next;

第二步:开始遍历链表

while(fast != null) {
           while(fast != null && fast.val == slow.val) {
                fast = fast.next;
           }
           if(fast != null) {
                slow.next = fast;
           slow = fast;
           fast = fast.next; 
           }else {
               slow.next = null;
           }
        }
        return head;

代码分析:

while(fast != null && fast.val == slow.val) {
        fast = fast.next;
  }

这个while循环是控制快指针,满足条件就继续往下走;出了这个while循环,有两种情况。

第一种:因为fast.val != slow.val而结束,并且没走完链表

if(fast != null) {
     slow.next = fast;
     slow = fast;
     fast = fast.next; 
}

这种情况就需要连接快慢指针,从而达到删除重复结点的目的

第二种:fast == null,链表走完了

else {
   slow.next = null;
}

快指针走完了都没满足条件,说明slow后面的结点都是重复的结点

第三步:分析特殊情况

当结点是空的时候,不需要删除;当只有一个结点的时候,也不存在重复结点的情况

if(head == null) {
      return null;
 }
 if(head.next == null) {
      return head;
  }

在时间效率上面,直接超越百分百


还有一道力扣题和这题的思路很相似,下面是链接,同学们可以先自己尝试

力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

题目要求为:删除所有的key结点

3.快慢指针的小总结

(1)以上是快慢指针的三种常规用法,有的时候,也称为前后指针

(2)当题目中要求只遍历一遍链表,就应该想到快慢指针,一般只需要定义两个变量即可(快慢指针)

(3)当题目的要求有对链表的两头进行操作时,考虑求中间的结点

(4)使用快慢指针,要重点考虑它们的线性关系(分别走多少步)和结束条件


以上就是本节的全部内容了,同学们快去练手吧!

相关文章
|
23天前
|
算法 索引
单链表题+数组题(快慢指针和左右指针)
单链表题+数组题(快慢指针和左右指针)
28 1
|
6月前
|
存储 C语言
用指针处理链表
用指针处理链表
60 3
|
1月前
|
存储
一篇文章了解区分指针数组,数组指针,函数指针,链表。
一篇文章了解区分指针数组,数组指针,函数指针,链表。
18 0
|
2月前
|
存储 算法 C语言
C语言手撕实战代码_循环单链表和循环双链表
本文档详细介绍了用C语言实现循环单链表和循环双链表的相关算法。包括循环单链表的建立、逆转、左移、拆分及合并等操作;以及双链表的建立、遍历、排序和循环双链表的重组。通过具体示例和代码片段,展示了每种算法的实现思路与步骤,帮助读者深入理解并掌握这些数据结构的基本操作方法。
|
3月前
|
存储 算法
链表经典操作与实战
文章深入探讨了链表的基本概念、分类和经典操作,通过LeetCode上的实战题目展示了如何应用虚拟头节点、双指针法、定位前驱节点等技巧来解决链表相关问题,并强调了算法在软件开发中的重要性。
链表经典操作与实战
|
4月前
【数据结构OJ题】复制带随机指针的链表
力扣题目——复制带随机指针的链表
51 1
【数据结构OJ题】复制带随机指针的链表
|
3月前
|
Python
【Leetcode刷题Python】138. 复制带随机指针的链表
LeetCode上题目“138. 复制带随机指针的链表”的Python解决方案,包括两种方法:一种是在每个节点后复制一个新节点然后再分离出来形成新链表;另一种是构建一个字典来跟踪原始节点与其副本之间的映射关系,从而处理新链表的构建。
21 1
|
3月前
|
存储 算法 数据处理
指针与链表
指针与链表
65 0
|
4月前
|
存储 数据管理 C语言
C语言实战 | 使用链表完成“贪吃蛇”游戏
【7月更文挑战第1天】整体思维,即系统思维,强调以整体视角理解事物。在编程中,结构体体现这种思想,将相关变量打包处理。示例展示了如何用链表而非数组实现“贪吃蛇”游戏,链表提供了更灵活的动态数据管理。一系列代码图片详细描绘了链表结构体在游戏中的应用,包括节点定义、移动、碰撞检测等,凸显了使用链表的优势和代码的清晰组织。
45 0
C语言实战 | 使用链表完成“贪吃蛇”游戏
|
5月前
|
测试技术 C++ 开发者
智慧指针是什么以及具体用法
智慧指针是什么以及具体用法
39 2