在单链表中删除倒数第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名企算法与数据结构题目最优解》(第二版)
版权声明:此文版权归作者所有!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,905评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,140评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,791评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,483评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,476评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,516评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,905评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,560评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,778评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,557评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,635评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,338评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,925评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,898评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,142评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,818评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,347评论 2 342

推荐阅读更多精彩内容

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