链表常见面试题总结

链表常见面试题总结:求两链表交点,链表插删,拼接,逆置…..


1从尾到头打印单链表
2.递归销毁一个链表
3.删除一个无头单链表的非尾节点(不能遍历链表)
4.在无头单链表的一个节点前插入一个节点(不能遍历链表)
5.单链表实现约瑟夫环(JosephCircle)
6.逆置/反转单链表
7.单链表排序(冒泡排序&快速排序)
8.合并两个有序链表,合并后依然有序
9.查找单链表的中间节点,要求只能遍历一次链表
10.查找单链表的倒数第k个节点,要求只能遍历一次链表
11.删除链表的倒数第K个结点
12.判断单链表是否带环?若带环,求环的长度?求环的入口点?并计算每个算法的时间复杂度&空间复杂度。
13.判断两个链表是否相交,若相交,求交点。(假设链表不带环)
14.判断两个链表是否相交,若相交,求交点。(假设链表可能带环)
15.求两个已排序单链表中相同的数据


/**********头文件************/
#pragma once 

typedef int DataType;

typedef struct SListNode{
    DataType _data;
    struct SListNode* _pNext;
}Node,*PNode;

void SListInit(PNode* ppHead);
PNode BuyNode(DataType data);
void SListPushBack(PNode* ppHead, DataType data);
void SlistPushFront(PNode* ppHead, DataType data);
PNode FindNode(PNode pHead, DataType data);
void SListDestroy(PNode* ppHead);
void SListPrint(PNode pHead);
void TestSList();

/////////////////////////////////////////////////////以下是面试题
void SListPrint_Reverse(PNode pHead);
void SListDestroy_R(PNode* ppHead);
void EraseNotTailNode(PNode* pos);
void InsertFrontNode(PNode pos,DataType data);
void JosephCircle(PNode* pHead, size_t n);
     void TestJosephCircle();
void ReverseSList(PNode* ppHead); 
void ReverseSList_New(PNode* ppHead);
void BubbleSortSList(PNode pHead);
PNode MergeSList(PNode pHead1, PNode pHead2);
PNode FindMiddleNode(PNode pHead);
PNode FindBackNode(PNode pHead, size_t K);
int DeleteBackNode(PNode* ppHead, size_t K);
PNode HasCircle(PNode pHead);
int GitCircleLen(PNode pHead);
PNode GitEntranceNode(PNode pHead);
int IsCross(PNode pHead1, PNode pHead2);
PNode GetCrossNode(PNode pHead1, PNode pHead2);
PNode GetCrossIsCircleNode(PNode pHead1, PNode pHead2);
void UnionSList(PNode pHead1, PNode pHead2);


/*****************代码*********************/

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include "SList.h"


void SListInit(PNode* ppHead){
    assert(ppHead);

    *ppHead= NULL;
}

PNode BuyNode(DataType data){
    PNode pNewNode = (PNode)malloc(sizeof(Node));
    pNewNode->_data = data;
    pNewNode->_pNext = NULL;
    if (pNewNode == NULL){
        printf("malloc error !!!\n");
        return NULL;
    }
    return pNewNode;
}

void SListPushBack(PNode* ppHead, DataType data){
    assert(ppHead);

    if (*ppHead == NULL){
        (*ppHead) = BuyNode(data);
    }
    else{
        PNode pCur = *ppHead;
        while (pCur->_pNext){
            pCur = pCur->_pNext;
        }
        pCur->_pNext = BuyNode(data);
    }
}

void SlistPushFront(PNode* ppHead, DataType data){
    assert(ppHead);
    PNode pNewNode = BuyNode(data);
    pNewNode->_pNext = *ppHead;
    *ppHead = pNewNode;
}

PNode FindNode(PNode pHead, DataType data){
    assert(pHead);
    if (NULL == pHead){
        return NULL;
    }
    PNode pCur = pHead;
    while(pCur){
        if (pCur->_data == data){
            return pCur;
        }
        pCur = pCur->_pNext;
    }
    return NULL;
}

void SListDestroy(PNode* ppHead){
    assert(ppHead);
    PNode pCur = *ppHead;
    PNode pTmp = NULL;
    while (pCur){
        pTmp = pCur;
        pCur = pCur->_pNext;
        free(pTmp);
    }
    *ppHead = NULL;
}

void SListPrint(PNode pHead){
    PNode pCur = pHead;
    while (pCur){
        printf("%d --> ", pCur->_data);
        pCur = pCur->_pNext;
    }
    printf("NULL\n");
}


//////////////////////////////////////////////////////////

//1.从尾到头打印单链表
void SListPrint_Reverse(PNode pHead){

    if (pHead){
        SListPrint_Reverse(pHead->_pNext);
        printf("%d --> ", pHead->_data);
    }
}
//2.递归销毁一个链表(从后往前)
void SListDestroy_R(PNode* ppHead){
    assert(ppHead);
    if (*ppHead){
        SListDestroy_R(&(*ppHead)->_pNext);
        free(*ppHead);
        *ppHead = NULL;
    }
}
//3.删除一个无头单链表的非尾节点(不能遍历链表)
void EraseNotTailNode(PNode* pos){
    assert(pos);
    if (pos == NULL)
        return;
    (*pos) = (*pos)->_pNext;
}
//4.在无头单链表的一个节点前插入一个节点(不能遍历链表)
void InsertFrontNode(PNode pos,DataType data){
    PNode pNewNode = NULL;
    if (NULL == pos)
        return;
    pNewNode = BuyNode(pos->_data);
    pNewNode->_pNext = pos->_pNext;
    pos->_pNext = pNewNode;
    pos->_data = data;
}
//5.单链表实现约瑟夫环(JosephCircle)
void JosephCircle(PNode* ppHead, size_t n){
    assert(ppHead);
    PNode pCur = *ppHead;
    PNode pTmp = NULL;

    while (pCur != pCur->_pNext){
        size_t m = n;
        while (--m){
            pCur = pCur->_pNext;
        }
        pTmp = pCur->_pNext;
        pCur->_pNext = pTmp->_pNext;
        pCur->_data = pTmp->_data;
        free(pTmp);
    }
    *ppHead = pCur;
}


//6.逆置/反转单链表
//三指针式
void ReverseSList(PNode* ppHead){
    assert(ppHead);
    if ((*ppHead) == NULL || NULL == (*ppHead)->_pNext){
        return;
    }
    PNode p1 = (*ppHead);
    PNode p2 = p1->_pNext;
    p1->_pNext = NULL;
    PNode p3 = NULL;
    while (p2){
        p3 = p2->_pNext;
        p2->_pNext = p1;
        p1 = p2;
        p2 = p3;
    }
    *ppHead = p1;
}
//头插式
void ReverseSList_New(PNode* ppHead){
    assert(ppHead);
    if (NULL == *ppHead || NULL == (*ppHead)->_pNext){
        return;
    }
    PNode pNewNode = NULL;
    PNode pCur = *ppHead;
    PNode pNext = NULL;
    while (pCur){
        pNext = pCur->_pNext;
        pCur->_pNext = pNewNode;
        pNewNode = pCur;
        pCur = pNext;
    }
    *ppHead = pNewNode;
}

//7.单链表排序(冒泡排序)
void Swap(DataType* a, DataType* b){
    DataType tmp = *a;
    *a = *b;
    *b = tmp;
}
void BubbleSortSList(PNode pHead){
    assert(pHead);
    if (NULL == pHead || NULL == pHead->_pNext){
        return;
    }
    int flag = 1;
    PNode pCur = NULL;
    PNode pTmp = NULL;
    PNode pTail = NULL;
    while (pHead != pTail){
        flag = 1;
        pCur = pHead;
        pTmp = pCur->_pNext;
        while (pTmp != pTail){
            flag = 0;
            if (pCur->_data > pTmp->_data){
                Swap(&(pCur->_data),& (pTmp->_data));
            }
            pCur = pTmp;
            pTmp = pTmp->_pNext;
        }
        if (flag){
            return;
        }
        pTail = pCur;
    }
    return;
}


//8.合并两个有序链表,合并后依然有序
PNode MergeSList(PNode pHead1, PNode pHead2){
    assert(pHead1);
    assert(pHead2);
    if (pHead1 == NULL){
        return pHead2;
    }
    if (pHead2 == NULL){
        return pHead1;
    }
    PNode p1 = pHead1;
    PNode p2 = pHead2;
    PNode pNewNode = NULL;
    PNode pTail = NULL;
    if (p1->_data > p2->_data){
        pNewNode = p2;
        p2 = p2->_pNext;
    }
    else{
        pNewNode = p1;
        p1 = p1->_pNext;
    }
    pTail = pNewNode;
    while (p1 && p2){
        if (p1->_data > p2->_data){
            pTail->_pNext = p2;
            p2 = p2->_pNext;
        }
        else{
            pTail->_pNext = p1;
            p1 = p1->_pNext;
        }
        pTail = pTail->_pNext;
    }
    if (p1 == NULL){
        pTail->_pNext = p2;
    }
    if (p2 == NULL){
        pTail->_pNext = p1;
    }
    return pNewNode;
}

//9.查找单链表的中间节点,要求只能遍历一次链表
PNode FindMiddleNode(PNode pHead){
    PNode pSlow = pHead;
    PNode pFast = pHead;
    while(pFast && pFast->_pNext){
        pSlow = pSlow->_pNext;
        pFast = pFast->_pNext->_pNext;
    }
    return pSlow;
}

//10.查找单链表的倒数第k个节点,要求只能遍历一次链表
PNode FindBackNode(PNode pHead,size_t K){
    assert(pHead);
    if (pHead == NULL || K == 0){
        return NULL;
    }
    PNode pFast = pHead;
    PNode pSlow = pHead;
    while (--K){
        if (pFast == NULL){
            return NULL;
        }
        pFast = pFast->_pNext; 
    }
    while (pFast->_pNext){
        pFast = pFast->_pNext;
        pSlow = pSlow->_pNext;
    }
    return pSlow;
}

//11.删除链表的倒数第K个结点
int DeleteBackNode(PNode* ppHead, size_t K){
    assert(ppHead);
    if (*ppHead == NULL || K == 0){
        return 0;
    }
    PNode pSlow = *ppHead;
    PNode pFast = *ppHead;
    PNode pStr = NULL;
    while (--K){
        if (pFast == NULL){
            return 0;
        }
        pFast = pFast->_pNext;
    }
    while (pFast->_pNext){
        pStr = pSlow;
        pSlow = pSlow->_pNext;
        pFast = pFast->_pNext;
    }
    if (pSlow == *ppHead){
        *ppHead = pSlow->_pNext;
    }
    else{
        pStr->_pNext = pSlow->_pNext;
    }
    free(pSlow);
    return 1;
}

//12.判断单链表是否带环?若带环,求环的长度?求环的入口点?
//   并计算每个算法的时间复杂度&空间复杂度
PNode HasCircle(PNode pHead){
    assert(pHead);
    if (pHead == NULL){
        return NULL;
    }
    PNode pSlow = pHead;
    PNode pFast = pHead;
    while (pFast && pFast->_pNext){
        pSlow = pSlow->_pNext;
        pFast = pFast->_pNext->_pNext;
        if (pSlow == pFast){
            return pSlow;
        }
    }
    return NULL;
}
//求环的长度
int GitCircleLen(PNode pHead){
    PNode pMeet = HasCircle(pHead);
    if (pMeet == NULL){
        return 0;
    }
    int count = 0;
    PNode pCur = pMeet->_pNext;
    while (pCur != pMeet){
        count++;
        pCur = pCur->_pNext;
    }
    count++;
    return count;
}
//找环的入口节点   时间复杂度:O(N)
PNode GitEntranceNode(PNode pHead){
    PNode pMeet = HasCircle(pHead);
    PNode pCur = pHead;
    while (pCur != pMeet){
        pCur = pCur->_pNext;
        pMeet = pMeet->_pNext;
    }
    return pMeet;
}
//13.判断两个链表是否相交,若相交,求交点。(假设链表不带环)
int IsCross(PNode pHead1, PNode pHead2){
    assert(pHead1);
    assert(pHead2);
    if (pHead1 == NULL || NULL == pHead2){
        return 0;
    }
    PNode p1 = pHead1;
    PNode p2 = pHead2;
    while (p1->_pNext){ //最后一个节点
        p1 = p1->_pNext;
    }
    while (p2->_pNext){
        p2 = p2->_pNext;
    }
    if (p1 == p2){
        return 1;
    }
    return 0;
}
//求不带环的交点
PNode GetCrossNode(PNode pHead1, PNode pHead2){
    if (!IsCross(pHead1, pHead2)){
        return NULL;
    }
    PNode p1 = pHead1;
    PNode p2 = pHead2;
    int n1 = 1;
    int n2 = 1;
    int n = 0;
    while (p1->_pNext){
        n1++;
        p1 = p1->_pNext;
    }
    while (p2->_pNext){
        n2++;
        p2 = p2->_pNext;
    }
    n = n1 - n2;
    p1 = pHead1;
    p2 = pHead2;
    if (n > 0){
        while (n--){
            p1 = p1->_pNext;
        }
    }
    else{
        while (n++){
            p2 = p2->_pNext;
        }
    }
    while (p1 != p2){
        p1 = p1->_pNext;
        p2 = p2->_pNext;
    }
    return p1;
}

//14.判断两个链表是否相交,若相交,求交点。(假设链表可能带环)

//此题用到12,13题的部分函数,可分列出来
PNode GetCrossIsCircleNode(PNode pHead1, PNode pHead2){
    assert(pHead1);
    assert(pHead2);
    if (pHead1 == NULL || pHead2 == NULL){
        return NULL;
    }
    PNode p1 = HasCircle(pHead1);//存在环,返回值为快慢指针环相遇值  Meet
    PNode p2 = HasCircle(pHead2);
    PNode pCur = NULL;
    if (p1 == NULL && p2 == NULL){ //两个都不带环
        pCur = GetCrossNode(pHead1, pHead2);
        return pCur;
    }
    else if (p1 && p2){//两个都是带环链表
        PNode pTmp = p1;
        while (p1 != p2){    //若两带环相交,环比为其公共 故可判断p1 p2是否在同一环 判断是否相交
            p1 = p1->_pNext;
            if (p1 == pTmp){  //转过一圈 不存在
                return NULL;
            }
        }//出while 存在
        //存在又分为环内交点和环外交点(环内交点 整个环都是 此处返回两个入口点)
        //通过两个带环链表的入口点是否相等,判断环内还是环外相交
        PNode pMeet1 = GitEntranceNode(pHead1);
        PNode pMeet2 = GitEntranceNode(pHead2);
        if (pMeet1 == pMeet2){
            pTmp = p1->_pNext;//准备破环变链求交点  备份

            //不破环也能求交点 破环更容易 直接调链式相交点函数
            //不破环可将链式求交点函数(GetCrossNode)中的计数两链表长度中的条件NULL改成环上任意一点(p1,p2)

            p1->_pNext = NULL;
            pCur = GetCrossNode(pHead1, pHead2);
            //既然有交点 一定有返回值 若空 只能说太巧破环破在(0,6模型)入换点 

            //破环已经损坏原有链表 此处拿到pCur后,还原链表
            p1->_pNext = pTmp;
            if (pCur){
                return pCur;
            }
            else{//破环点刚好是环入口点 交点  (0 6 模型)
                return pTmp;
            }
        }
        else{
            return pMeet1;//整个环都是交点 随便返回环上一点  建议两个入口点中选
        }
    }//else if
    else{//一环一链肯定无交点
        return NULL;
    }
}

//15.求两个已排序单链表中相同的数据。
void UnionSList(PNode pHead1, PNode pHead2){
    assert(pHead1);
    assert(pHead2);
    if (pHead1 == NULL || pHead2 == NULL){
        return;
    }
    PNode p1 = pHead1;
    PNode p2 = pHead2;
    int tmp = 0;
    int flag = 1;
    while (p1 && p2){
        if (p1->_data < p2->_data){
            p1 = p1->_pNext;
        }
        else if (p1->_data > p2->_data){
            p2 = p2->_pNext;
        }
        else{
            if ((tmp != p1->_data) || flag){
                printf("%d ", p1->_data);
            }
            p1 = p1->_pNext;
            p2 = p2->_pNext;
            flag = 0;                //防止tmp的初始值与两个链表的第一次相同值相等
        }
    }
    printf("\n");
    return;
}




/************************************************************************************/

//测试5题
void TestJosephCircle()
{
    PNode s;
    PNode pTail;
    SListInit(&s);
    SListPushBack(&s, 1);
    SListPushBack(&s, 2);
    SListPushBack(&s, 3);
    SListPushBack(&s, 4);
    SListPushBack(&s, 5);
    SListPushBack(&s, 6);
    SListPushBack(&s, 7);

    pTail = FindNode(s, 7);
    pTail->_pNext = s;
    JosephCircle(&s, 3);
    printf("result = %d\n", s->_data);
}
//测12题
void TestCircle(){

    PNode s;
    SListInit(&s);
    SListPushBack(&s, 1);
    SListPushBack(&s, 2);
    SListPushBack(&s, 3);
    SListPushBack(&s, 4);
    SListPushBack(&s, 5);
    SListPushBack(&s, 6);
    SListPushBack(&s, 7);
    SListPushBack(&s, 8);
    SListPushBack(&s, 9);
    SListPushBack(&s, 0);

    FindNode(s, 0)->_pNext = FindNode(s, 4);
    //FindNode(s, 0)->_pNext = s;
    printf("Meet = %d \n", HasCircle(s)->_data);
    printf("Len = %d\n", GitCircleLen(s));
    printf("Entrance Node = %d\n", GitEntranceNode(s)->_data);
}

//测13题
void TestCross(){

    PNode s;
    SListInit(&s);
    SListPushBack(&s, 4);
    SListPushBack(&s, 6);
    SListPushBack(&s, 1);
    SListPushBack(&s, 5);
    SListPushBack(&s, 3);
    SListPushBack(&s, 2);

    PNode L;
    SListInit(&L);
    SListPushBack(&L, 0);
    SListPushBack(&L, 1);
    SListPushBack(&L, 6);
    SListPushBack(&L, 4);
    SListPushBack(&L, 7);
    SListPushBack(&L, 8);
    SListPushBack(&L, 9);

    FindNode(s, 2)->_pNext = FindNode(L, 6);

    printf("IsCross = %d\n", IsCross(L, s));

    //printf("CrossNode = %d\n", GetCrossNode(L, s)->_data);

    //printf("CrossNode = %d\n", GetCrossIsCircleNode(L, s)->_data);//测14 双链


}
//测14题
void TestNotKnowCircle(){
    //造环
    PNode s;
    SListInit(&s);
    SListPushBack(&s, 1);
    SListPushBack(&s, 2);
    SListPushBack(&s, 3);
    SListPushBack(&s, 4);
    SListPushBack(&s, 5);
    SListPushBack(&s, 6);
    SListPushBack(&s, 7);
    SListPushBack(&s, 8);

    PNode L;
    SListInit(&L);
    SListPushBack(&L, 0);
    SListPushBack(&L, 1);
    PNode pCur = NULL;

    //结束

    FindNode(s, 8)->_pNext = FindNode(s, 4);
    FindNode(L, 1)->_pNext = FindNode(s, 2);
    pCur = GetCrossIsCircleNode(s, L);//双环(6,6)模型

    //FindNode(s, 8)->_pNext = FindNode(s, 4);
    //pCur = GetCrossIsCircleNode(s, FindNode(s, 4));//双环(0,6)模型

    if (pCur){
        printf("Circle = %d\n", pCur->_data);
    }
    else{
        printf("不存在!!!\n");
    }
}

//测15题
void TestUnion(){

    PNode s;
    SListInit(&s);
    SListPushBack(&s, 1);
    SListPushBack(&s, 1);
    SListPushBack(&s, 2);
    SListPushBack(&s, 4);
    SListPushBack(&s, 5);
    SListPushBack(&s, 8);

    PNode L;
    SListInit(&L);
    SListPushBack(&L, 1);
    SListPushBack(&L, 1);
    SListPushBack(&L, 3);
    SListPushBack(&L, 4);
    SListPushBack(&L, 7);
    SListPushBack(&L, 8);
    SListPushBack(&L, 9);

    UnionSList(s, L);
}

//主测试

void TestSList(){

    PNode s;
    SListInit(&s);
    SListPushBack(&s, 4);
    SListPushBack(&s, 6);
    SListPushBack(&s, 1);
    SListPushBack(&s, 5);
    SListPushBack(&s, 3);
    SListPushBack(&s, 2);

    PNode L;
    SListInit(&L);
    SListPushBack(&L, 0);
    SListPushBack(&L, 1);
    SListPushBack(&L, 1);
    SListPushBack(&L, 4);
    SListPushBack(&L, 7);
    SListPushBack(&L, 8);
    SListPushBack(&L, 9);

    //SListPrint(s);

    //SlistPushFront(&s,0);
    //SListPrint(s);

    //printf("Find = %d\n",FindNode(s,6)->_data);

    //SListPrint_Reverse(s);

    //EraseNotTailNode(&(s->_pNext->_pNext->_pNext));

    //SListDestroy(&s);

    //SListPrint(s);

    //InsertFrontNode((s->_pNext),9);

    //SListPrint(s);

    //TestJosephCircle();

    //ReverseSList(&s);
    //SListPrint(s);

    //ReverseSList_New(&s);
    SListPrint(s);

    BubbleSortSList(s);
    SListPrint(s);

    printf("Middle = %d\n", FindMiddleNode(s)->_data);

    printf("BackNode = %d\n",FindBackNode(s, 3)->_data);

    printf("IsDelete = %d\n", DeleteBackNode(&s, 3));
    SListPrint(s);

    //SListPrint(MergeSList(s,L));

    //TestCircle();

    //TestCross();

    //TestNotKnowCircle();
    TestUnion();

    SListDestroy_R(&s);
}

int main(){

    TestSList();

    return 0;
}


带头节点的双向链表:
https://blog.csdn.net/Romantic_C/article/details/79991268
带头节点的单链表:
https://blog.csdn.net/Romantic_C/article/details/79919836

猜你喜欢

转载自blog.csdn.net/Romantic_C/article/details/81395660