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

  • 从尾到头打印链表(要求不改变链表结构)
  • 反转链表(要求仅遍历一次链表)
  • 合并两个有序链表(非递归方法)
  • 删除倒数第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;
}
原文地址:https://www.cnblogs.com/flyingrun/p/13417874.html