简单的算法思想 - 利用快慢指针解决问题 - 寻找链表中的中间节点,回文序列,倒数第k个节点 - 详解

✨✨✨学习的道路很枯燥,希望我们能并肩走下来!

本文通过寻找链表中的中间节点,链表的回文序列,链表中倒数第k个节点OJ题问题,来进一步提高双指针在题中的使用,更好的扩展思路,锻炼思维;


1. 寻找链表中倒数第K个节点

题目描述

输入一个链表,输出该链表中倒数第k个结点。
输入:
1,{1,2,3,4,5}
返回值:
{5}

1.1. 思路分析

利用双指针:寻找倒数第k个节点,倒数第k个节点就会距离尾节点是k-1个单位,(尾节点的next==null),k-1这段距离是不变的 ;
可以通过 平移思想,让快指针fast先走k-1个单位,慢指针slow就会距离fast k-1个单位;
保持两节点距离不变再平移两节点(同时移动一步或几步),当快的指针指向尾节点时,slow指针对应的就是倒数第k个节点;

1.2 代码实现

代码实现具体操作:

① 可以定义两个指针fast,slow并初始化为第一个节点位置
② 让fast指针先走k-1步,这样fast与slow指针就相距k-1个位置;
③ 再让两指针一起一步一步走;(两指针之间距离保持相对静止)
④ 当fast.next == null 时,走到尾节点,此时slow所对应的位置就是倒数第K个位置;

在这里插入图片描述

代码实现

 public ListNode FindKthToTail(ListNode head,int k) {
        //k小于0或者没有节点
        if(k <= 0 || head == null) {
            return null;
        }
        ListNode fast = head;
        ListNode slow = head;
        //让fast先走k-1步
        while(k-1 != 0) {
            fast = fast.next;
            //fast不合法,超出节点个数:例如一共4个节点,要求寻找倒数第6个的位置 - err
            if(fast == null) {
                return null;
            }
            k--;
        }
        //两指针一起一步一步走;
        while(fast.next != null) {
            fast = fast.next;
            slow = slow.next;
        }
        return slow;
    }

2. 寻找链表中的中间结点

题目描述:

给定一个头结点为 head 的非空单链表,返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。
示例:
输入:[1,2,3,4,5]
输出:此列表中的结点 3 (序列化形式:[3,4,5])
输入:[1,2,3,4,5,6]
输出:此列表中的结点 4 (序列化形式:[4,5,6])

题目分析
对于奇数结点,返回中间结点即可;对于偶数结点,返回中间两个的第二个结点

2.1 思路概述

使用快慢双指针,让快指针每次走两步,慢指针每次走一步,当快指针为null时,慢指针指向的就是中间结点位置;

为什么让快指针一次走两步呢?
快指针一次走两步,慢指针一次走一步,他们的相对位移是一步,中间不会有跳过;如果快指针一次走三步,他们中间的相对位移就是二,会有遗漏的;对于上述例子第二个,如果快指针一次走三步,返回的结点是3而不是4了所以会有遗漏;

2.2 代码实现

【注】代码考注意事项
① 考虑特殊情况,没有结点或者只有一个结点
② 定义双指针fast,slow
快指针结束标志:fast == null (偶数个结点) || fast.next == null (奇数个结点)
④ 返回slow

public ListNode middleNode(ListNode head) {
        //快慢指针:快的走两步,慢的走一步
        //没有结点
        if(head == null) {
            return null;
        }
        //只有一共结点
        if(head.next == null) {
            return head;
        }
        ListNode fast = head;
        ListNode slow = head;
        while(fast != null && fast.next != null) {//当找到slow位置时,fast结束条件
            fast = fast.next.next; //走两步
            slow = slow.next;
        }       
        return slow;
    }

3. 链表的回文结构

题目描述:

对于一个链表,请设计一个时间复杂度为O(n),额外空间复杂度为O(1)的算法,判断其是否为回文结构。
给定一个链表的头指针A,请返回一个bool值,代表其是否为回文结构。保证链表长度小于等于900。
测试样例:
1->2->2->1
返回:true

3.1 思路分析

根据题目,可知对于回文就是从左往右,和从右往左多是一样;
可以定义快慢指针先找到中间结点,从中间结点开始翻转中间结点之后的结点,这样从两头开始向中间比较值,如果都一样,就是回文序列;

奇数图示:

在这里插入图片描述

偶数图示:

在这里插入图片描述

3.2 代码实现

① 定义双指针fast,slow
② 寻找中间结点 (上述例题中有,不理解可以往前翻)
快指针走两步,慢指针走一步
③ 记录翻转结点的位置 cur
④ 翻转结点:翻转cur结点之前,需要记录cur后一个结点的位置,如果之间翻转,就会找不到之前的位置而出错( 翻转结点之前一定要记录后一个结点curNext
⑤ 比较翻转完后前后结点的值: 两个指针都向中间结点靠近,循环结束条件结束head != slow

【注】 (可参考上述图例)
对于偶数个结点情况:head.next == slow;偶数情况如果不这样使用,会发生空指针异常而出错;
对于奇数个结点情况是:head != slow

 public boolean chkPalindrome(ListNode A) {
        // write code here
        //寻找中间节点
        ListNode fast = A;
        ListNode slow = A;
        while(fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
        }
        //slow跳出来就是之间结点位置
        //记录翻转节点起始位置
        ListNode cur = slow.next;
        while(cur != null) {
            ListNode curNext = cur.next;//记录cur后面结点的位置
            cur.next = slow;// 结点翻转
            //slow,cur后移动准备下一次翻转
            slow = cur;
            cur = curNext;
        }
        //翻转完节点后,slow位于末端
        //比较前后节点的值
        while(A != slow) {
            if(A.val != slow.val) {
                return false;
            }
            //对于偶数情况:A.next == slow
            //对于奇数情况是:A != slow
            if(A.next == slow) {//偶数情况
                return true;
            }
            //A,slow向中间移动
            A = A.next;
            slow = slow.next;
        }
        //跳出循环,说明链表前后值相等
        return true;
    }

总结

✨✨✨各位读友,本篇分享到内容如果对你有帮助给个👍赞鼓励一下吧!!
感谢每一位一起走到这的伙伴,我们可以一起交流进步!!!一起加油吧!!!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值