算法~删除链表的倒数第N个元素

264 阅读2分钟

1. 问题描述

  • 给你一个链表,删除链表的倒数第K个结点,并且返回链表的头结点
  • 实例1:输入: head = [1,2,3,4,5],n = 2;输出: [1,2,3,5]
  • 实例2:输入: head = [1],n = 1;输出: []
  • 实例3:输入: head = [1,2],n = 1;输出: [1]

2. 解法

2.1 相隔为N的前后双指针

  • 思路:使用前后两个指针,前指针先移动,当移动次数为 N 时再让后指针从头节点开始向后移动,这样前后双指针刚好相隔N,后续前后指针同步向后移动,直至前指针移动到尾节点,后指针指针所处节点便刚好是倒数第N个节点
  • 图解image.png
  • 复杂度时间复杂度O(n)空间复杂度O(1)
  • Java实现
/**
 * 前后指针一次遍历获取链表的倒数第 n个结点,时间复杂度:O(n) 空间复杂度:O(1)
 *
 * @param head 链表
 * @param n    倒数第 N 个结点
 * @return 新链表
 */
public static ListNode removeNthFromEnd(ListNode head, int n) {
    // 双指针相隔N,进行遍历
    ListNode pre = head, after = head, newListNode = new ListNode(0, head);
    int index = 0;
    while (pre.next != null) {
        pre = pre.next;
        index++;
        if (index > n) {
            // 节点当前的位置大于间隔值K时前指针也开始遍历
            after = after.next;
        }
    }
    // 将后节点的下一个节点指向下下个节点
    after.next = after.next.next;
    return newListNode.next;
}

2.2 获取长度遍历两次

  • 思路:第一次遍历获取整个链表的长度length、第二次遍历寻找第length-k位置进行删除
  • 复杂度时间复杂度O(n)空间复杂度O(1)
  • Java实现
/**
 * 获取链表的长度
 *
 * @param head 链表
 * @return 链表的长度
 */
private static int getLength(ListNode head) {
    int length = 0;
    while (head != null) {
        head = head.next;
        // node不为空时,需+1再返回
        ++length;
    }
    return length;
}

/**
 * 删除链表的倒数第N个结点,时间复杂度:O(n) 空间复杂度:O(1)
 *
 * @param head 链表
 * @param n    倒数第N个结点
 * @return 新链表
 */
private static ListNode removeNthFromEnd(ListNode head, int n) {
    // 创建一个新节点对象,使不受head移位的影响
    // 这个0不重要,主要是为了添加一个next为head的新节点
    ListNode newListNode = new ListNode(0, head);
    // 不断移动的节点
    ListNode cur = head;
    for (int i = 0; i < getLength(head) - n - 1; i++) {
        cur = cur.next;
    }
    // 到达倒数第n-1个节点,让该节点的next跳过下一个节点指向下下个节点
    cur.next = cur.next.next;
    return newListNode.next;
}

2.3 借助栈

  • 思路:遍历所有节点先后加入栈,利用栈后进先出的特点,再依次出栈,出的第N个节点即是倒数第N个节点,删除该节点即可
  • 复杂度时间复杂度O(n)、需要借助栈,空间复杂度O(n)
  • Java实现
/**
 * 借助栈来获取链表的倒数第 n个结点,时间复杂度:O(n) 空间复杂度:O(1)
 *
 * @param head 链表
 * @param n    倒数第 N 个结点
 * @return 新链表
 */
public static ListNode removeNthFromEnd(ListNode head, int n) {
    ListNode newListNode = new ListNode(0, head);
    // 实现栈有三种类,包括Stack、ArrayDeque以及Stack,ArrayDeque相对效率更优
    ArrayDeque<ListNode> stack = new ArrayDeque<>();
    while (head != null) {
        stack.push(head);
        head = head.next;
    }
    for (int i = 0; i < n; i++) {
        stack.pop();
    }
    // 现在栈最上方的节点刚好就是倒数第N+1个节点
    ListNode temp = stack.peek();
    // 将倒数第N+1个节点的下一个节点指向倒数第N-1个节点即达到了删除倒数第 N 个几点的目的
    temp.next = temp.next.next;
    return newListNode.next;
}