[程序员面试题精选100题]9.链表中倒数第k个结点

简介:

题目

输入一个单向链表,输出该链表中倒数第k个结点。链表的倒数第0个结点为链表的尾指针。

思路一

因为是单向链表,只有从前往后的指针而没有从后往前的指针。因此我们不能倒序遍历链表,只能正序遍历。假设整个链表有n个结点,那么倒数第k个结点是从头结点开始的第n-k-1个结点(从0开始计数)。我们只需要得到链表中结点的个数n,那我们只要从头结点开始往后走n-k-1步就可以了。
因此这种方法需要遍历链表两次。第一次得到链表中结点个数n,第二次得到从头结点开始的第n-k-1个结点即倒数第k个结点。时间复杂度为O(n)。

代码

    /*------------------------------------
    *   日期:2015-02-08
    *   作者:SJF0115
    *   题目: 9.链表中倒数第k个结点
    *   来源:程序员面试题精选100题
    ---------------------------------------*/
    #include <iostream>
    #include <cstring>
    #include <vector>
    #include <queue>
    using namespace std;

    struct ListNode{
        int val;
        ListNode *next;
        ListNode(int x):val(x),next(NULL){}
    };

    class Solution {
    public:
        ListNode* FindKthTailNode(ListNode* head,int k) {
            if(head == nullptr || k < 0){
                return nullptr;
            }//if
            // 统计链表个数
            int count = 0;
            ListNode *p = head;
            while(p){
                p = p->next;
                ++count;
            }//while
            // 不足K个
            if(count < k){
                return nullptr;
            }//if
            // 倒数第K个节点
            int pos = count - k;
            p = head;
            for(int i = 0;i < pos;++i){
                p = p->next;
            }//for
            return p;
        }
    };

    int main(){
        Solution s;
        ListNode *head = new ListNode(1);
        ListNode *node;
        for(int i = 8;i >= 2;--i){
            node = new ListNode(i);
            node->next = head->next;
            head->next = node;
        }//for

        ListNode *result = s.FindKthTailNode(head,3);

        // 输出
        if(result == nullptr){
            cout<<"nullptr"<<endl;
        }//if
        else{
            cout<<result->val<<endl;
        }//else
        return 0;
    }

思路二

上面那种思路需要两次遍历,如何才能只需一次遍历呢?
如果我们在遍历时维持两个指针,第一个指针从链表的头指针开始遍历,在第k-1步之前,第二个指针保持不动, k-1 步开始,第二个指针也开始从链表的头指针开始遍历,两个指针齐头并进。由于两个指针的距离保持在k-1;当第一个(走在前面的)指针到达链表的尾结点时,第二个指针(走在后面的)指针正好是倒数第
K个节点。

代码二

    /*------------------------------------
    *   日期:2015-02-08
    *   作者:SJF0115
    *   题目: 9.链表中倒数第k个结点
    *   来源:程序员面试题精选100题
    ---------------------------------------*/
    #include <iostream>
    #include <cstring>
    #include <vector>
    #include <queue>
    using namespace std;

    struct ListNode{
        int val;
        ListNode *next;
        ListNode(int x):val(x),next(NULL){}
    };

    class Solution {
    public:
        ListNode* FindKthTailNode(ListNode* head,int k) {
            if(head == nullptr || k < 0){
                return nullptr;
            }//if
            ListNode *p = head,*q = head;
            // 指针p移动k-1步
            int index = 1;
            while(index < k && p != nullptr){
                p = p->next;
                ++index;
            }//while
            // 不够K个
            if(p == nullptr){
                return nullptr;
            }//if
            // 同时移动
            while(p->next){
                p = p->next;
                q = q->next;
            }//while
            return q;
        }
    };

    int main(){
        Solution s;
        ListNode *head = new ListNode(1);
        ListNode *node;
        for(int i = 8;i >= 2;--i){
            node = new ListNode(i);
            node->next = head->next;
            head->next = node;
        }//for

        ListNode *result = s.FindKthTailNode(head,8);

        // 输出
        if(result == nullptr){
            cout<<"nullptr"<<endl;
        }//if
        else{
            cout<<result->val<<endl;
        }//else
        return 0;
    }

拓展

输入一个单向链表。如果该链表的结点数为奇数,输出中间的结点;如果链表结点数为偶数,输出中间两个结点前面的一个节点。

代码

    /*------------------------------------
    *   日期:2015-02-08
    *   作者:SJF0115
    *   题目: 9.2链表中间结点
    *   来源:程序员面试题精选100题
    ---------------------------------------*/
    #include <iostream>
    #include <cstring>
    #include <vector>
    #include <queue>
    using namespace std;

    struct ListNode{
        int val;
        ListNode *next;
        ListNode(int x):val(x),next(NULL){}
    };

    class Solution {
    public:
        ListNode* FindMidNode(ListNode* head) {
            if(head == nullptr){
                return nullptr;
            }//if
            ListNode *slow = head,*fast = head;
            while(fast->next != nullptr && fast->next->next != nullptr){
                slow = slow->next;
                fast = fast->next->next;
            }//while
            return slow;
        }
    };

    int main(){
        Solution s;
        ListNode *head = new ListNode(1);
        ListNode *node;
        for(int i = 8;i >= 2;--i){
            node = new ListNode(i);
            node->next = head->next;
            head->next = node;
        }//for

        ListNode *result = s.FindMidNode(head);

        // 输出
        if(result == nullptr){
            cout<<"nullptr"<<endl;
        }//if
        else{
            cout<<result->val<<endl;
        }//else
        return 0;
    }
目录
相关文章
|
算法
【❤️算法笔记❤️】-每日一刷-19、删除链表的倒数第 N个结点
【❤️算法笔记❤️】-每日一刷-19、删除链表的倒数第 N个结点
358 1
链表的中间结点
链表的中间结点
498 57
|
存储 算法 搜索推荐
链表的中间结点
【10月更文挑战第24天】链表的中间结点是链表操作中的一个重要概念,通过快慢指针法等方法可以高效地找到它。中间结点在数据分割、平衡检测、算法应用等方面都有着重要的意义。在实际编程中,理解和掌握寻找中间结点的方法对于解决链表相关问题具有重要价值。
462 1
|
算法 程序员 Go
PHP 程序员学会了 Go 语言就能唬住面试官吗?
【9月更文挑战第8天】学会Go语言可提升PHP程序员的面试印象,但不足以 solely “唬住” 面试官。学习新语言能展现学习能力、拓宽技术视野,并增加就业机会。然而,实际项目经验、深入理解语言特性和综合能力更为关键。全面展示这些方面才能真正提升面试成功率。
278 10
LeetCode第19题删除链表的倒数第 N 个结点
该文章介绍了 LeetCode 第 19 题删除链表的倒数第 N 个结点的解法,通过使用快慢双指针,先将快指针移动 n 步,然后快慢指针一起遍历,直到快指针到达链尾,从而找到倒数第 N 个结点的前一个结点进行删除,同时总结了快慢指针可减少链表遍历次数的特点。
LeetCode第19题删除链表的倒数第 N 个结点
【LeetCode 09】19 删除链表的倒数第 N 个结点
【LeetCode 09】19 删除链表的倒数第 N 个结点
288 0
|
存储 Java
【IO面试题 四】、介绍一下Java的序列化与反序列化
Java的序列化与反序列化允许对象通过实现Serializable接口转换成字节序列并存储或传输,之后可以通过ObjectInputStream和ObjectOutputStream的方法将这些字节序列恢复成对象。
|
存储 算法 Java
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
本文详解自旋锁的概念、优缺点、使用场景及Java实现。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
|
存储 缓存 算法
面试官:单核 CPU 支持 Java 多线程吗?为什么?被问懵了!
本文介绍了多线程环境下的几个关键概念,包括时间片、超线程、上下文切换及其影响因素,以及线程调度的两种方式——抢占式调度和协同式调度。文章还讨论了减少上下文切换次数以提高多线程程序效率的方法,如无锁并发编程、使用CAS算法等,并提出了合理的线程数量配置策略,以平衡CPU利用率和线程切换开销。
面试官:单核 CPU 支持 Java 多线程吗?为什么?被问懵了!
|
存储 缓存 Java
大厂面试必看!Java基本数据类型和包装类的那些坑
本文介绍了Java中的基本数据类型和包装类,包括整数类型、浮点数类型、字符类型和布尔类型。详细讲解了每种类型的特性和应用场景,并探讨了包装类的引入原因、装箱与拆箱机制以及缓存机制。最后总结了面试中常见的相关考点,帮助读者更好地理解和应对面试中的问题。
462 4