经典链表问题之双指针

双指针的解题思路在做链表问题中真的是屡试不爽!让我用几道简单的算法题来带大家了解一下什么是双指针。

先看一个简单的问题:寻找一个链表中的中间节点。

正常我们的解题思路是怎样的?首先我们要获得链表的长度,再遍历链表到链表长度的一半,最终获得链表中间的节点。

看看用双指针是如何解决的。首先我们先定义一个快指针(fast)和一个慢指针(slow),他们最开始指向的都是链表的头结点,然后开始遍历头结点。不同的是每次循环后我们让慢指针移动一个,即slow=slow.next。而让快指针每次移动两个,fast=fast.next.next。这样处理有什么好处呢?就是当你的fast到达了终点时你的slow才只跑了半程,此时slow所在的节点不就是中间节点吗?浅浅画个图:

看看代码可能你就懂了。

public ListNode middleNode(ListNode head){
	ListNode slow = head; //慢指针
	ListNode fast = head; //快指针
	while (fast !null &fast.next !null){
		slow slow.next;
		fast fast.next.next;
	}
	return slow;
}

如果你认为双指针只能做这些那你就错啦!让我们想想链表的缺点,相比于数组他没法直接拿到一个节点的数据,要想拿到一个特定节点,你必须要从头结点开始遍历。那一个数据还好,我要多拿几个节点岂不是没次都要遍历一次。想想双指针的思想是不是能不能帮到我们来优化这个操作。

再看一题:删除链表中倒数第n个节点。

正常思路:我们肯定要先拿到n前面那个节点吧,再将他的next指向n的next。要想拿到n前面的节点要算链表的长度吧,要遍历链表吧。

双指针:我们想得到n前面那个节点,也就是希望当fast跑到终点以后是,slow能停在n前面那个节点上,对吧!怎么实现?让fast先跑n+1个节点不就得了。

这次直接看代码

public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode temp = new ListNode(-1);
        temp.next=head;
        //快慢指针
        ListNode slow =temp;
        ListNode fast =head;
        //先让快指针领先慢指针n个
        while(fast!=null && n>0){
            fast=fast.next;
            n--;
        }
        while(fast!=null){
            slow=slow.next;
            fast=fast.next;
        }
        slow.next=slow.next.next;
        ListNode ans = temp.next;
        return ans ;
    }
}

有人发现了,怎么跟上一题不一样啦?之前不是slow和fast都是head,这个slow怎么等于temp,并且temp.next还等于head了?思考一下为什么?嘿嘿!看看题,我们是要是要删除节点的呀,如果链表中就只有一个节点(头节点),要删掉头结点怎么办,把slow和fast都是head的话头结点还能删掉吗?所以必须在头结点前再插入一个元素,要是头结点被删了直接temp.next=null,不就妥啦!这里需要注意哦!!

再看看代码,发现通过我们这样的赋值ListNode slow =temp;ListNode fast =head;slow已经比fast慢了一步啦!不过想想好像正好,我们想要的是n前面的那个节点,此时我在让fast先跑n个位置是不是就解决啦。画个图(假设要删除倒数第3个节点) 

(注:fast为啥会跑出去?因为根据我们的代码fast=null循环才会停止,fast最后停止的节点是尾节点的下一个节点也就是null)

通过这个题的思路我们能想到什么?我们是不是可以通过slow和fast指针的关系能通过双指针能直接获得某个节点的信息,再也不用遍历两次链表啦。

同样类似的题如:删除重复的节点,删除特定的节点,获得倒数第N个节点等都可以用双针来解决,或许他并不是最优解法但也能为我们拓宽思路,不是吗?

在来最后一题拓展题:合并两个链表。

这道题很简单!但是你可以试试用双指针能不能做呢?

下面是我的解法:

public ListNode mergeInBetween(ListNode list1, int a, int b, ListNode list2) {
        ListNode temp=new ListNode(0);
        temp.next = list1;
        //双指针
        ListNode slow =temp;
        ListNode fast =list1;
        //让fast领先(b-a)
        int len=b-a;
        while(fast!=null && len>0){
            fast=fast.next;
            len--;
        }
        while(a>0){
            slow=slow.next;
            fast=fast.next;
            a--;
        }
        slow.next = list2;
        while(list2.next!=null){
            list2=list2.next;
        }
        list2.next=fast.next;

        return temp.next;
    }

“双指针解决链表问题好方便啊!”这是我在学习完双指针后的感触!双指针不仅在链表中有,数组中同样也有。通过双指针的思想我是不是可以大胆的扩展三指针?四指针?来通过一次遍历获得链表中的多个节点呢?算法最终要学的是他的思想,如何解题,如何想到这个方法的,不断的拓宽我们的思维。

希望大家看完我这三道算法题的讲解后能对你有所帮助,有所收获。当你熟练掌握双指针的话,我相信他一定能成为你在解决链表问题的大杀器!!拜拜!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值