链表中的快满指针

219 阅读2分钟

 小知识,大挑战!本文正在参与“程序员必备小知识”创作活动链表的中间节点

给定一个头结点为 **head** 的非空单链表,返回链表的中间结点。

如果有两个中间结点,则返回第二个中间结点。

输入:[1,2,3,4,5,6]

输出:此列表中的结点 4

由于该列表有两个中间结点,值分别为 3 和 4,我们返回第二个结点。

示例节点

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */

因为是这种节点形式,我们不能直接的去调用方法,需要我们自己进行编写

首先,我们用最简单的做法去做一下这道题,我们直接将链表转化成数组,取中间值输出即可

    public ListNode middleNode(ListNode head) {
         ListNode [] A=new ListNode[100];
        int a=0;
        while (head!=null){
            A[a++]=head;
            head=head.next;
        }
        return A[a/2];
    }

优化1

当我们利用数组做完这道题的时候,我们再仔细地去想一想我们可以省略数组,我们进行两边遍历,第一次统计元素个数N,第二次遍历到N/2是时我们输出即可。

public ListNode middleNode(ListNode head) {
        int n = 0;
        ListNode list = head;
        while (list != null) {
            n++;
            list = list.next;
        }
        int k = 0;
        list = head;
        while (k < n / 2) {
            k++;
            list = list.next;
        }
        return list;
    }

优化2

但这一次优化我们进行了两遍遍历,我们找链表的点是一个比较特殊的一个点,在二分之一处,使用双指针中的快慢指针,一前一后,当快指针到达链表尾部时,满指针一定在链表中间位置

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

删除链表倒数第n个节点

示例节点

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */

因为经过上面那道题之后,思路十分明确就是先进行一遍遍历之后确定链表长度,之后在进行一遍遍历之后,删除节点,所以这道问题很简单的就解决了

//删除节点函数
public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode d=new ListNode(0,head);
        int lenght=get(head);
        ListNode c=d;
        for (int i=1;i<lenght-n+1;i++){
            c=c.next;
        }
        c.next=c.next.next;
        return d.next;
    }
//获取链表长度函数
    public int get(ListNode head){
        int lenght=0;
        while (head!=null){
            lenght++;
            head=head.next;
        }
        return lenght;
    }

优化

相较于前一道题,前一道是找到中间位置,本题是指定位置,我们还是可以利用双指针,只不过之前属于定比,此题属于定差,我们只需要保证指针的距离为n,当前指针到达链表尾部是,后指针就处于n的位置

public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode dummy = new ListNode(0, head);
        ListNode first = head;
        ListNode second = dummy;
        for (int i = 0; i < n; ++i) {
            first = first.next;
        }
        while (first != null) {
            first = first.next;
            second = second.next;
        }
        second.next = second.next.next;
        ListNode ans = dummy.next;
        return ans;
    }