编程导航算法通关村第一关 | 白银:链表双指针使用合集

这一期简单介绍一下双指针的使用,使用它可以简单高效解决很多问题。

链表结构如下:

public class ListNode {
    Integer val;
    ListNode next;

    public ListNode(Integer val) {
        this.val = val;
    }


    public String toString(ListNode head) {
        StringBuilder builder = new StringBuilder();
        while (head != null){
            builder.append(head.val + "\t");
            head = head.next;
        }
        return builder.toString();
    }
}

1.寻找链表的中间节点

给定一个链表的头节点,要求返回链表的中间节点。如果链表节点为双数,则返回第二个中间节点

一般思路: 

一般能想到的是,将链表遍历一遍得到它的长度len,然后 len/2 向下取整得到 size,然后再将头指针往后拨 size步,得到的就是中间节点。可行,但感觉太常规。这里看一下双指针的解法

双指针:

给两个指针: fast 和 slow,fast每次走两步,slow每次走一步,当 fast 走到尾节点(链表长度为单数),或者尾节点指向的 null(链表长度为复数)时,slow指向的就是中间节点,代码如下:

//寻找中间节点
    public static ListNode findMiddleNode(ListNode head) {
        ListNode fast = head;
        ListNode slow = head;
        //第一个条件针对聊表个数为复数时跳出循环,第二个条件链表个数为单数跳出循环
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
        }
        return slow;
    }

2.寻找链表的倒数第K个元素

输入一个链表,输出该链表中倒数第k个节点。本题从1开始计数,即链表的尾节点是倒数第1个节点。

这里一般思路大概也是先遍历一遍链表得到长度 len,len 减去 k 得到 size,然后让指针走 size 步得到节点,但是用双指针,遍历一遍链表就能解决:

给两个指针: fast 和 slow,fast 指针先走 k 步,然后快慢指针一起走,快指针走到表尾的next,也就是null时,慢指针指向的就是倒数第 k 个节点。 

因为k有可能比链表长度还大,所以加一个fast不为空的判断:

//寻找倒数第k个元素
    public static ListNode findKthFromEnd(ListNode head, int k) {
        ListNode fast = head;
        ListNode slow = head;
        //逻辑是先让快指针先走K步,然后快慢指针一起走,快指针走到表尾的next,也就是null时,慢指针指向的就是倒数第 k 个节点
        //因为k有可能比链表长度还大,所以加一个fast不为空的判断
        while (fast != null && k > 0) {
            fast = fast.next;
            k--;
        }

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

        return slow;
    }

 3.将链表右移K个位置

编程导航算法通关村第一关 | 白银:链表双指针使用合集_第1张图片

编程导航算法通关村第一关 | 白银:链表双指针使用合集_第2张图片

这里观察一下题目,分析一下,后面的部分右移旋转到前面,右移k个位置。新链表尾部时原链表尾部的倒数第 (k-1) 个节点,而且右移两个位置后,节点4 还是在节点5前面,次序不变。

也就是说,右移 2 个位置,就是在倒数第2个节点 (节点4) 前面断开,然后将原尾节点(结点5)的next指向原头结点,新链表就形成了。

那么现在问题就简化成了寻找倒数 (k-1)个节点,变量保存它的next 节点作为新的头节点,并且将原尾节点指向原头节点,返回新的头节点,代码如下:

/**
     * @param head
     * @param k    右移k个位置
     * @return
     */
    //向右移旋转链表
    public static ListNode rotateRight(ListNode head, int k) {
        ListNode fast = head;
        ListNode slow = head;
        //k可能比链表长度还长,要先计算出链表长度,然后给k取模
        int len = 0;
        ListNode cur = head;
        while (cur != null) {
            len++;
            cur = cur.next;
        }
        if (len == 0) {
            return head;
        }
        k = k % len;

        //快指针先走k步
        while (k!=0){
            fast = fast.next;
            k--;
        }
        //快指针走到原表表尾,慢指针指向右移完成后的表尾元素
        while (fast.next != null){
            fast = fast.next;
            slow = slow.next;
        }
        ListNode newHead = slow.next;
        fast.next = head;
        slow.next = null;

        return newHead;
    }

简单介绍双指针三种用法,可以看到共同点都是寻找链表中的某个点。在想清题目的逻辑后,使用双指针就可以快速地定位到某个点。不必先遍历一遍得出长度,再计算出头节点要走地步数。在更复杂的题里,如果两个指针不够,还可以再增加指针~

目录

1.寻找链表的中间节点

2.寻找链表的倒数第K个元素

 3.将链表右移K个位置


你可能感兴趣的:(链表,数据结构)