算法通关村第一关--链表经典问题之双指针笔记

引言:以下内容均只是个人看法,并无严格经过核对,如有异议,欢迎交流,发现错误,欢迎您的及时指正,感谢!本文所示代码均为java代码


本文中使用的结点定义语句如下

public class listNode {
    public int data;
    public listNode next;
    public listNode(int data) {
        this.data = data;
        this.next=null;
    }
}

单链表的遍历一般从第一个结点依次遍历整个链表,所以第一个结点十分重要,在本文中头节点指的是第一个存放数据的结点,与其他相关文章中关于它的定义可能不同,无需深究,理解相关操作的逻辑原理即可,本质上无较大差别。


1.双指针寻找中间节点

leetcode876,给出一个带有头节点为head的非空链表,返回位于中间位置的节点,如有两个中间节点即返回靠后的一个

示例
输入 1,2,3,4
输出 3
输入 1,2,3,4,5
输出 3

思路设置fast和low指针,fast步长为2,low步长为1,当fast到达终点时,low刚好处于中间位置,设置low和fast初始均等于head,当链表长度为奇数时,fast.next==null为截止条件,当链表长度为偶数时,fast==null为截止条件

注意:当fast和low的起始地址不为head,例如是指向head的一个虚拟节点,那么fast指针停止后,low指针的位置可能和下面的情况不一样。

    /**
     * 
     * @param head 需要查找中间节点的链表的头节点
     * @return
     */
    public listNode middleNode(listNode head){
        listNode fast=head,low=head;
        //当链表长度为奇数或者偶数时,例如(1,2,3,4)或(1,2,3)从head开始,fast最后在第一个链表的4停止,在第二个链表的3.next停止
        while(fast!=null&&fast.next!=null){
            fast=fast.next.next;
            low=low.next.next;
        }
        return low;
    }

2.寻找倒数第k个元素

输入一个链表,输出该链表中倒数第k个节点。
本题从1开始计数,即链表的尾节点是倒数第1个节点。 
示例 给定一个链表: 1->2->3->4->5, 和 k = 2. 
返回链表 4

思路:使用两个指针low和fast,先让fast指针向后走k步,注意可能k可能大于链表长度,所以在fast向后移动时需要判断fast是否为空,移动结束后同时移动low和fast,当fast遍历完整个链表后,low恰好在倒数第k个节点的位置上

代码如下

    /**
     *
     * @param head 实现操作的链表的头节点
     * @param k 倒数第k个节点
     * @return
     */
    //寻找倒数第k个元素
    public listNode getKeythFromEnd(listNode head,int k){
        listNode fast=head;
        listNode low=head;
        //让fast比low先走k步,这里判断fast是否为空,是为了防止k比链表长度大的情况
        while(k>0&&fast!=null){
            fast=fast.next;
            k--;
        }
        //因为low比fast慢k步,所以当fast到达终点时,low即为倒数第k个节点
        while(fast!=null){
            fast=fast.next;
            low=low.next;
        }
        return low;
    }

3.旋转链表

leetcode61,给出一个链表的头节点,让链表的每个节点都向右移动k个位置

示例:

给出链表是1->2->3->4->5 k=2

思路一:我们观察输入和输出,相当于将链表成环,不断改变起始结点的位置,我们根据题目容易知道当k大于n时,相当于移动的位置为k%n,当k%n==0时,不需要修改头节点的指向,在head的基础上向后遍历n-k%n个结点(包括head处的结点)后断开环。

思路二:观察结果,相当于把链表分为123和45两条链表,把123放到45后面,这个思路的关键是找到断开的地方(这里对于断开位置的查找可以用双指针找到,或者结合k与断开位置的关系),比如样例中是断开3与4之间,将4作为新的头结点,然后把123排到5后面

思路三:将整个链表反转,变成54321,然后将前k个节点和剩下的n-k个节点分别反转变成45123

(关于反转的思想在下一大关会进行汇总)

下面的代码仅用了双指针来实现

    /**
     * 利用双指针完成链表的旋转
     * @param head
     * @param k
     * @return
     */
    public static listNode rotateRight(listNode head, int k) {
    if(head==null||k==0){
        return head;
    }
    listNode node=head;
    listNode fast=head;
    listNode slow=head;
    int len=0;
    while(node!=null){
        len++;
        node=node.next;
    }
    int move=k%len;
    for(int i=0;i<move;i++){
        fast=fast.next;
    }
    //让fast遍历到倒数第一个节点,slow到达倒数第k+1个节点,这样slow.next就是倒数第k个节点
    while(fast.next!=null){
        slow=slow.next;
        fast=fast.next;
    }
    listNode res=slow.next;
    slow.next=null;
    fast.next=head;
    return res;
    }

  • 11
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值