• 链表常考笔试面试题(常备)


    • 从尾到头打印链表(要求不改变链表结构)
    • 反转链表(要求仅遍历一次链表)
    • 合并两个有序链表(非递归方法)
    • 删除倒数第k个节点
    • 链表中环的入口节点(包括判断环的存在,确定环中节点个数等问题)
    • 求链表中间节点

    ListNode.h

    #pragma once
    #include <iostream>
    using namespace std;
    
    struct ListNode{
        int val;
        ListNode* next;
        // 构造函数
        ListNode(int val):val(val), next(nullptr){}
    };
    
    // 写链表的习惯:一般会用ListNode* pNode = pHead;
    
    // 创建一个链表节点
    ListNode* createListNode(int val){
        ListNode* pNode = new ListNode(val);
        pNode->val = val;
        pNode->next = nullptr;
        return pNode;
    }
    
    // 链接两个节点
    void connectListNodes(ListNode* pCurrent, ListNode* pNext){
        if(pCurrent == nullptr)
            cout<<"Error to connect two nodes."<<endl;
        else
            pCurrent->next = pNext;
    }
    
    // 打印某个节点的值
    void PrintListNode(ListNode* pNode){
        if(pNode == nullptr)
            cout<<"error to print a node."<<endl;
        else
            cout<<pNode->val<<endl;
    }
    
    // 打印pHead之后的链表(只要有下一个节点就接着打印)
    void PrintListNodes(ListNode* pHead){
        if(pHead == nullptr)
            cout<<"error to print nodes."<<endl;
        else{
            ListNode* pNode = pHead;
            while(pNode != nullptr){
                cout<<pNode->val<<endl;
                pNode = pNode->next;
            }
        }
    }
    
    // 销毁从pHead开始之后的链表
    void destroyListNodes(ListNode* pHead){
        if(pHead == nullptr)
            cout<<"error to destroy nodes."<<endl;
        else{
            ListNode* pNode = pHead;
            while(pNode != nullptr){
                pHead = pNode->next;
                delete pNode;
                pNode = nullptr;
            }
        }
    }
    
    // 往链表的尾部添加一个节点(注意可能此时链表为空,添加节点会修改头指针,所以使用二级指针ListNode** pHead)
    void addToTail(ListNode** pHead, int val){
        ListNode* pNew = new ListNode(val);
        pNew->val = val;
        pNew->next = nullptr;
        // 如果头指针为空,则添加的节点为头指针
        if(*pHead == nullptr){
            *pHead = pNew;
        }
        else{
            ListNode* pNode = *pHead;
            // 这里应该写成pNode->next != null,如果写成pNode != null,那么pNode循环结束就是空了,下面的pNode->next = pNew就是错的
            while(pNode->next != nullptr){
                pNode = pNode->next;
            }
            pNode->next = pNew;
        }
    }
    
    // 找到第一个含有某值的节点并删除该节点,也有可能修改头指针,所以也要写成二级指针ListNode** pHead
    void removeNode(ListNode** pHead, int val){
        if(*pHead == nullptr)
            return ;
        // 如果头指针的值就是val,那么要删除头指针,并将头指针指向下一个节点
        ListNode* pToBeDelete = nullptr;
        if((*pHead)->val == val){
            pToBeDelete = *pHead;
            *pHead = (*pHead)->next;
        }
        // 如果是中间节点(注意,也有可能是最后一个节点)
        else{
            ListNode* pNode = *pHead;
            while(pNode->next != nullptr && pNode->next->val != val)
                pNode = pNode->next;
            if(pNode->next != nullptr && pNode->next->val == val){
                pToBeDelete = pNode->next;
                // 注意,最后一个节点也可以删除,并使最后一个节点的前一个节点指向空(pNode->next->next就是空)
                pNode->next = pNode->next->next;
            }
        }
        // 如果是尾节点
        if(pToBeDelete != nullptr){
            delete pToBeDelete;
            pToBeDelete = nullptr;
        }
    }
    
    
    // 迭代法反转链表
    ListNode* reverseList(ListNode* pHead){
        if(pHead == nullptr)
            return pHead;
        ListNode* pNode = pHead;
        ListNode* reverseNode = nullptr;
        ListNode* pPrev = nullptr;
        while(pNode != nullptr){
            ListNode* pNext = pNode->next;
            if(pNext == nullptr)
                reverseNode = pNode;
            pNode->next = pPrev;
            pPrev = pNode;
            pNode = pNext;
        }
        return reverseNode;
    }
    
    // 递归法反转单链表
    ListNode* reverseListPlus(ListNode* pHead){
        if(pHead == nullptr || pHead->next == nullptr)
            return pHead;
    
        ListNode* reverseNode = reverseListPlus(pHead->next);
        pHead->next->next = pHead;
        pHead->next = nullptr;
    
        return reverseNode;
    }
    

    main.cpp

    #include <iostream>
    #include <vector>
    #include <stack>
    #include <cstring>
    #include <string>
    #include <queue>
    #include <algorithm>
    #include "TreeNode.h"
    #include "ListNode.h"
    using namespace std;
    
    // 面试题6
    // 从尾到头打印链表
    void printListReverse(ListNode* pHead){
        stack<int> stack1;
        //
        ListNode* pNode = pHead;
        while(pNode != nullptr){
            stack1.push(pNode->val);
            pNode = pNode->next;
        }
        cout<<endl;
        while(!stack1.empty()){
            cout<<stack1.top();
            stack1.pop();
        }
    }
    
    // 面试题24
    // 反转链表
    ListNode* reverseList(ListNode* pHead){
        if(pHead == nullptr)
            return pHead;
        ListNode* pNode = pHead;
        ListNode* pPrev = nullptr;
        ListNode* pReverseHead = nullptr;
        while(pNode != nullptr){
            ListNode* pNext = pNode->next;
            if(pNext == nullptr)
                pReverseHead = pNode;
            pNode->next = pPrev;
            pPrev = pNode;
            pNode = pNext;
        }
        return pReverseHead;
    }
    
    // 合并两个有序链表(非递归方法)
    ListNode* mergeList(ListNode* pHead1, ListNode* pHead2){
        if(pHead1 == nullptr)
            return pHead2;
        if(pHead2 == nullptr)
            return pHead1;
        ListNode* pMergeHead = nullptr;
        // 先确定头指针
        if(pHead1->val < pHead2->val){
            pMergeHead = pHead1;
            pHead1 = pHead1->next;
        }
        else{
            pMergeHead = pHead2;
            pHead2 = pHead2->next;
        }
        ListNode* pNode = pMergeHead;
        while(pHead1 != nullptr || pHead2 != nullptr){
            if(pHead1 != nullptr && pHead2 != nullptr){
                if(pHead1->val < pHead2->val){
                    pNode->next = pHead1;
                    pHead1 = pHead1->next;
                }
                else{
                    pNode->next = pHead2;
                    pHead2 = pHead2->next;
                }
            }
            else if(pHead1 != nullptr){
                pNode->next = pHead1;
                pHead1 = pHead1->next;
            }
            else{
                pNode->next = pHead2;
                pHead2 = pHead2->next;
            }
            pNode = pNode->next;
        }
        return pMergeHead;
    }
    
    // 面试题22:删除倒数第k个节点(有可能删除头节点,所以也是二级指针)
    ListNode* findKthNode(ListNode** pHead, int k){
        if(*pHead == nullptr || pHead == nullptr || k == 0)
            return nullptr;
        ListNode* pNodeFirst = *pHead;
        ListNode* pNodeSecond = *pHead;
        ListNode* pPrev = *pHead;
    
        // 第一个指针先走k-1步
        for(int i = 1; i <= k-1; i++){
            if(pNodeFirst->next != nullptr)
                pNodeFirst = pNodeFirst->next;
            // 链表的节点数小于k-1个
            else
                return nullptr;
        }
        while(pNodeFirst->next != nullptr){
            pNodeFirst = pNodeFirst->next;
            pPrev = pNodeSecond;
            pNodeSecond = pNodeSecond->next;
        }
    
        // 删除节点
        // 如果删除的是头指针(或者只有一个节点)
        if(pNodeSecond ==  *pHead){
            ListNode* deleteNode = *pHead;
            *pHead = (*pHead)->next;
            delete deleteNode;
            deleteNode = nullptr;
        }
        else{
            pPrev->next = pPrev->next->next;
            delete pNodeSecond;
            pNodeSecond = nullptr;
        }
        return *pHead;
    }
    
    // 面试题23:链表中环的入口节点(这个题目有3个考点:1、链表中环的检测(返回相遇节点);2、环中节点个数;3、根据前两个解,找到入口节点)
    // 链表中环的检测(定义两个指针,一个指针一次走两步,一个指针一次走一步,
    // 如果有环,那么总有个时候两个指针会相遇,如果走的快的走到末尾仍然没有与慢指针相遇,那么没有
    
    // 判断环中两个指针的相遇节点
    ListNode* meetingNode(ListNode* pHead){
        if(pHead == nullptr)
            return nullptr;
        ListNode* pSlow = pHead->next;
        // 只有一个节点
        if(pSlow == nullptr)
            return nullptr;
    
        ListNode* pFast = pSlow->next;
        while(pFast != nullptr && pSlow != nullptr){
            if(pFast == pSlow){
                cout<<pFast->val<<endl;
                return pFast;
            }
    
            pSlow = pSlow->next;
    
            pFast = pFast->next;
            if(pFast != nullptr)
                pFast = pFast->next;
        }
        return nullptr;
    }
    
    // 寻找环中入口节点
    ListNode* entryNode(ListNode* pHead){
        if(pHead == nullptr)
            return nullptr;
        ListNode* meetingnode = meetingNode(pHead);
        if(meetingnode == nullptr)
            return nullptr;
        // 先找到环中节点的个数
        int loopNode = 1;
        ListNode* meet = meetingnode;
        while(meet->next != meetingnode){
            meet = meet->next;
            loopNode++;
        }
    
        // 定义两个指针
        ListNode* pFirst = pHead;
        ListNode* pSecond = pHead;
        while(loopNode--)
            pFirst = pFirst->next;
    
        while(pFirst != pSecond){
            pFirst = pFirst->next;
            pSecond = pSecond->next;
        }
        cout<<pFirst->val<<endl;
        return pFirst;
    }
    
    // 面试题xx:求链表中间节点
    ListNode* middleNode(ListNode* pHead){
        if(pHead == nullptr)
            return nullptr;
        ListNode* pSlow = pHead;
        // 只有一个节点
        if(pSlow->next == nullptr)
            return pSlow;
        // 只有两个节点
        if(pSlow->next->next == nullptr)
            return pSlow;
        pSlow = pSlow->next;
        ListNode* pFast = pSlow->next;
    
        // 当快节点走过最后一个节点
        while(pFast->next != nullptr){
            pFast = pFast->next;
            // 偶数个节点时,慢指针在最后不走
            if(pFast->next != nullptr){
                pFast = pFast->next;
                pSlow = pSlow->next;
            }
        }
        cout<<pSlow->val<<endl;
        return pSlow;
    }
    
    int main(int argc, char* argv[]){
        ListNode* pNode1 = createListNode(1);
        ListNode* pNode2 = createListNode(2);
        ListNode* pNode3 = createListNode(3);
        ListNode* pNode4 = createListNode(4);
        ListNode* pNode5 = createListNode(5);
    
        connectListNodes(pNode1, pNode2);
        connectListNodes(pNode2, pNode3);
        connectListNodes(pNode3, pNode4);
        connectListNodes(pNode4, pNode5);
        //connectListNodes(pNode5, pNode1);
        middleNode(pNode1);
        //PrintListNodes(entryNode(pNode1));
        return 0;
    }
    
  • 相关阅读:
    NOI-OJ 2.2 ID:6261 汉诺塔
    NOI-OJ 2.2 ID:1696 逆波兰表达式
    NOI-OJ 2.2 ID:8758 2的幂次方表示
    NOI-OJ 2.2 ID:3089 爬楼梯
    NOI-OJ 1.13 ID:34 确定进制
    NOI-OJ 1.7 ID:21 单词替换
    NOI-OJ 1.13 ID:23 区间内的真素数
    【模板】欧拉筛法
    [SHOI2002]滑雪
    【Luogu1217】[USACO1.5]数字三角形(数塔问题) ——动态规划基础
  • 原文地址:https://www.cnblogs.com/flyingrun/p/13417874.html
Copyright © 2020-2023  润新知