在单链表中删除倒数第k个节点

题目

  **分别实现两个函数,一个可以删除单链表中倒数第 K 个节点,另一个可以删除双链表中倒数第 K 个节点

要求

  如果链表长度为 N,时间复杂度达到O(N),额外空间复杂度达到O(1)。

思路

  本题较为简单,实现方式也是多种多样的,小编提供一种方法供读者参考。
  先来看看单链表如何调整。如果链表为空或者 K 小于1。这种情况下,参数是无效的直接返回即可,除此之外,让链表从头开始走到尾,每移动一步,就让K的值减1。
  链表: 1 → 2 → 3,K = 4,链表根本不存在倒数第4个节点
  走到的节点: 1 → 2 → 3
  K 变化为: 3 2 1
  链表: 1 → 2 → 3,K = 3,链表倒数第3个节点是1节点
  走到的节点: 1 → 2 → 3
  K 变化为: 2 1 0
  链表: 1 → 2 → 3,K = 2,链表倒数第2个节点是2节点
  链表:走到的节点: 1 → 2 → 3
  K 变化为:1 → 0 → 1
  由以上三种情况可知,让链表从头开始走到尾,没移动一步,就让 K 值减1,当链表走到结尾时,如果值大于0。说明不用调整链表,因为链表根本没有倒数第 K 个节点,此时将原链表直接返回即可;如果 K 值等于0,说明链表倒数第 K 个节点就是头节点,此时直接返回head.next,也就是原链表的第二个节点,让第二个节点作为链表的头节点返回即可,相当于删除头节点;接下来,说明一下如果 K 值小于0,该如何处理。
  先明确一点,如果要删除链表的头节点之后的某个节点,实际上需要找到要删除节点的前个节点,比如: 1 → 2 → 3,如果想除节点2,则需要找到节点1,然后把节点1连到节点3上(1 → 3),以此来达到删除节点2的目的。
  如果 K 值小于0,如何找到要删除节点的前一个节点呢?方法如下:
  1.重新从头节点开始走,每移动一步,就让K的值加1。
  2.当K等于0时,移动停止,移动到的节点就是要删除节点的前一个节点。
  这样做是非常好理解的,因为如果链表长度为N,要删除倒数第K个节点,很明显,倒数第 K 个节点的前一个节点就是第 N - K 个节点。在第一次遍历后,K 的值变为 K - N。第二次遍历时 K 的值不断加1,加到 0 就停止遍历,第二次遍历当然会停到第 N - K个节点的位置。

代码演示

package com.itz.zcy.listQuestion;

/**
 * 分别实现两个函数,一个可以删除单链表中倒数第 K 个节点,另一个可以删除双链表中倒数第 K 个节点
 * 如果链表长度为 N,时间复杂度达到O(N),额外空间复杂度达到O(1)。
 */
public class DeleteListKNode {

    /**
     * 定义节点
     */
    public static class Node {
        public int value;
        public Node next;
        //该变量用来做双链表的指向前区节点,在单链表的时候请读者忽略该节点
        public Node last;

        public Node(int data) {
            this.value = data;
        }
    }

    /**
     * 删除单链表的倒数第k个节点
     * @param head
     * @param lastKth
     * @return
     */
    public static Node removeLastKthNode(Node head, int lastKth) {
//        异常判定
        if (head == null || lastKth < 1) {
            return head;
        }
//        辅助节点
        Node cur = head;
//        遍历一遍节点判断节点在链表的哪儿
        while (cur != null) {
            lastKth--;
            cur = cur.next;
        }
//        如果是删除头节点的情况
        if (lastKth == 0){
            head = head.next;
        }
//        找到要删除的节点
        if (lastKth < 0) {
            cur = head;
            while (++lastKth != 0) {
                cur = cur.next;
            }
//            删除节点
            cur.next = cur.next.next;
        }
        return head;
    }

    /**
     * 删除双链表的倒数第k个节点
     * @param head
     * @param lastKth
     * @return
     */
    public static Node removeLastKthDoubleNode(Node head,int lastKth){
        if (head==null||lastKth<1){
            return head;
        }

        Node cur = head;
        while (cur !=null){
            lastKth--;
            cur = cur.next;
        }
        if (lastKth == 0){
            head = head.next;
            head.last = null;
        }

        if (lastKth<0){
            cur = head;
            while (++lastKth!=0){
                cur = cur.next;
            }
//            删除节点 删除双链表的某个节点
            Node newNext = cur.next.next;
            cur.next = newNext;
            if (newNext != null){
                newNext.last = cur;
            }
        }
        return head;
    }

    public static void main(String[] args) {
        Node node = new Node(2);
        node.next = new Node(4);
        node.next.next = new Node(6);
        node.next.next.next = new Node(8);
        node.next.next.next.next = new Node(3);
        node.next.next.next.next.next = new Node(5);
        node.next.next.next.next.next.next = new Node(7);
        Node head = removeLastKthNode(node, 5);
        while (true){
            if (head!=null){
                if (head.next == null){
                    System.out.print(head.value);
                }else {
                    System.out.print(head.value+" → ");
                }
            }else {
                break;
            }
            head = head.next;
        }

        System.out.println();
        Node node2 = new Node(2);
        node2.next = new Node(4);
        node2.next.last= node2;
        node2.next.next = new Node(6);
        node2.next.next.last = node2.next;
        node2.next.next.next = new Node(8);
        node2.next.next.next.last = node2.next.next;
        node2.next.next.next.next = new Node(3);
        node2.next.next.next.next.last = node2.next.next.next;
        node2.next.next.next.next.next = new Node(5);
        node2.next.next.next.next.next.last = node2.next.next.next.next;
        node2.next.next.next.next.next.next = new Node(7);
        node2.next.next.next.next.next.next.last = node2.next.next.next.next.next;
        Node head2 = removeLastKthDoubleNode(node2, 5);
        while (true){
            if (head2!=null){
                if (head2.next == null){
                    System.out.print(head2.value);
                }else {
                    System.out.print(head2.value+" ⇄ ");
                }
            }else {
                break;
            }
            head2 = head2.next;
        }
    }

//    2 → 4 → 8 → 3 → 5 → 7
//    2 ⇄ 4 ⇄ 8 ⇄ 3 ⇄ 5 ⇄ 7
}

总结

该题目比较简单,方法多种多样;时间复杂度是O(n),空间复杂度O(1)。

文献:左程云著 《程序员代码面试指南IT名企算法与数据结构题目最优解》(第二版)
版权声明:此文版权归作者所有!

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 【题目】分别实现两个函数,一个可以删除单链表中倒数第k个节点,另一个可以删除双链表中倒数第k个节点。【解析】先来分...
    Airycode阅读 3,538评论 0 0
  • 搞懂单链表常见面试题 Hello 继上次的 搞懂基本排序算法,这个一星期,我总结了,我所学习和思考的单链表基础知识...
    醒着的码者阅读 10,052评论 1 45
  • 题目 输入一个链表,输出该链表中倒数第k个节点。 为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第...
    Longshihua阅读 1,924评论 0 2
  • URL字符串中的字符 参考:RFC2396 RFC1738 URL中使用的字符必须来自ASCII的一个固定的子集,...
    HRocky阅读 3,165评论 0 0
  • 我们总是在晚上变的格外敏感。 普通的话语也要思索上好几遍,看电视连续剧情感也容易共鸣,本来没有那么伤心的事情在晚上...
    e5ab6748334f阅读 1,319评论 0 1