单链表常见笔试面试题

本文章主要介绍关于链表的常见笔试面试题:1.链表逆序打印;2.不遍历链表删除一个无头非尾节点;3.不遍历链表在无头单链表的一个节点前插入一个节点;4.单链表实现约瑟夫环;5.逆置/反转单链表;6.单链表冒泡排序;7.合并两个有序链表为一个有序链表;8.查找链表的中间节点,要求之遍历链表一次;9.查找链表的倒数第k个节点,要求只遍历一次链表;10.删除链表的第k个节点;11.判断单链表是否有环?若带环则求环长度?环的入口点?12.判断两个不带环的链表是否相交?若相交求交点;13.判断两个可能带环的链表是否相交?若相交求交点;14.求两个已排序链表中的相同的数据;15.复杂链表的复制。

1.首先做准备工作:

建立需要的头文件、对单链表初始化、打印单链表、链表节点的创建与销毁以及链表的插入操作。

1.1头文件linklist.h

    //头文件只被编译一次
    #pragma once
    //自定义元素的类型,方便用户去修改元素的数据类型
    typedef char LinkListType;
    //定义一个结构体,用于元素的存放和查找下一个元素所在的位置
    typedef struct LinkNode
    {
        LinkListType data;
        struct LinkNode* next;
    }LinkNode;   //其中LinkNode是对所定义的结构体的重命名,LinkNode*是定义指向该结构体的指针类型
    //宏定义一个标识符,用于测试函数时打印其对应的函数名,方便了代码的编写

    #define HEADER printf("============%s===========\n",__FUNCTION__);

1.2头文件complexlist.h

    //头文件只被编译一次
    #pragma once
    //自定义元素的类型,方便用户去修改元素的数据类型
    typedef char ComplexListType;
    //定义一个结构体,用于元素的存放、下一个元素所在的位置、以及随机指向
    typedef struct ComplexNode
    {
        ComplexListType data;
        struct ComplexNode* next;
        struct ComplexNode* random;

    }ComplexNode;   //其中ComplexNode是对所定义的结构体的重命名,ComplexNode*是定义指向该结构体的指针类型

1.3单链表的初始化

    void LinkListInit(LinkNode** phead)
    {
            *phead=NULL;

扫描二维码关注公众号,回复: 932496 查看本文章

    }

1.4单链表的打印

    void LinkListPrintChar(LinkNode* head,char* msg)
    {
        //非法输入
        if(msg==NULL)
        {
            return;
        }
        //先打印字符串
        printf("%s\n",msg);
        //空链表
        if(head==NULL)
        {
            return;
        }
        //非空链表
        LinkNode* cur=head;
        while(cur!=NULL)
        {
            printf("[%p][%c] ",cur,cur->data);
            cur=cur->next;
        }
        printf("\n");    

    }

1.5链表节点的创建

    LinkNode* CreateNode(LinkListType value)
    {
        LinkNode* new_node=(LinkNode*)malloc(sizeof(LinkNode));
        new_node->data=value;
        new_node->next=NULL;
        return new_node;

    }

1.6链表节点的销毁

    void DestroyNode(LinkNode* node)
    {
        free(node);

    }  

1.7对链表进行尾插操作

     void LinkListPushBack(LinkNode** phead,LinkListType value)
    {
        //非法操作
        if(phead==NULL)
            return;
        //空链表
        if(*phead==NULL)
        {
            LinkNode* new_node=CreateNode(value);
            *phead=new_node;
            return;
        }
        //非空链表
        LinkNode* cur=*phead;
        for(cur=*phead;cur->next!=NULL;cur=cur->next)
        {
            ;
        }
        //循环结束后,cur来到了最后一个节点位置
        LinkNode* new_node=CreateNode(value);
        cur->next=new_node;
    }

2.链表常见笔试面试题

2.1从尾到头打印单链表

思路:递归思想
void LinkListReversePrintChar(LinkNode* head)
{
    //递归出口
    if(head==NULL)
    {
        return;
    }
    //递归
    LinkListReversePrintChar(head->next);
    printf("[%p][%c] ",head,head->data);

}

2.2删除一个无头单链表的非尾节点(不能遍历链表)

分析:无头单链表指没有头节点的单链表,非尾节点指不是尾节点

思路:找到pos位置后,将pos->next->data与pos->data交换,然后删除pos->next节点

void LinkListErase(LinkNode** phead,LinkNode* pos)
{
    //非法输入
    if(phead==NULL)
    {
        return;
    }
    //pos位置为第一个节点时,需修改头指针
    if(pos==*phead)
    {
        *phead=pos->next;
        DestroyNode(pos);
    }
    //pos位置不是第一个节点且由题意也不是最后一个节点时
    pos->data=pos->next->data;  //先修改data值
    LinkNode* erase=pos->next;  //将pos->next作为要删除的节点
    pos->next=erase->next;
    DestroyNode(erase);  //删除该节点 

2.3在无头单链表的一个节点前插入一个节点(不能遍历单链表)

思路:先在pos位置后插一个节点,再修改这两个节点的data值即为前插

void LinkListBeforeInsert(LinkNode** phead,LinkNode* pos,LinkListType value)
{
    //非法输入
    if(phead==NULL||pos==NULL)
    {
        return;
    }
    //pos==*phead时,即插在第一个节点时
    if(pos==*phead)
    {
        LinkNode* new_node=CreateNode(value);  //创建节点
        new_node->next=*phead;
        *phead=new_node;
        return;
    }
    //pos!=*phead时,即插入的节点不是第一个节点时
    LinkNode* new_node=CreateNode(pos->data);  //创建data值为pos->data的节点
    new_node->next=pos->next;   //后插
    pos->next=new_node;       //后插
    pos->data=value;

}   

2.4单链表实现约瑟夫环

思路:当以N的速度淘汰节点时只需走N-1步就找到了第N个节点,然后删除该节点;直到只有一个节点结束

LinkNode* LinkListJosephCircle(LinkNode* head,int N)
{
    //链表为空
    if(head==NULL)
    {
        return NULL;
    }
    //链表非空
    LinkNode* cur=head;
    while(cur->next!=cur)   //循环结束条件为cur->next==cur,即只有一个节点时
    {
        int i=1;
        for(i=1;i<N;i++)  //for循环N-1次
        {
            cur=cur->next;
        }
        //循环结束后,cur就指向了要删除的节点
        printf("淘汰:[%p][%c]\n",cur,cur->data);
        //为了不再遍历找到cur的前一个节点从而去删除cur,故使用下面的方法
        cur->data=cur->next->data;
        LinkNode* to_delete=cur->next;  //利用data的赋值,将删除节点地址变为cur->next
        cur->next=to_delete->next;
        DestroyNode(to_delete);
    }
    //while循环结束,返回唯一一个节点的指向cur
    return cur;

}  

2.5.1逆置/反转单链表

思路1:从第二个节点开始,将其删除,然后插在第一个节点处;直到单链表结束

void LinkListReverse(LinkNode** phead)
{
    //非法输入
    if(phead==NULL)
    {
        return;
    }
    //空链表
    if(*phead==NULL)
    {
        return;
    }
    //只有一个节点时,不需要逆置
    if((*phead)->next==NULL)
    {
        return;
    }
    //至少两个节点,逆置
    LinkNode* cur=*phead;
    while(cur->next!=NULL)  //while循huan结束条件是cur->next==NULL
    {
        LinkNode* remove=cur->next;  //要移除的节点
        cur->next=remove->next;      //移除了该节点
        remove->next=*phead;     //头插法插remove
        *phead=remove;          //头插法插remove
    }

}  

2.5.2逆置/反转单链表

思路2:改变每个节点的指针的指向从而发生逆置

void LinkListReverseEx(LinkNode** phead)
{
    //非法输入
    if(phead==NULL)
    {
        return;
    }
    //空链表
    if(*phead==NULL)
    {
        return;
    }
    //只有一个节点
    if((*phead)->next==NULL)
    {
        return;
    }
    //至少两个节点
    LinkNode* pre=*phead;
    LinkNode* cur=(*phead)->next;
    pre->next=NULL;  //让第一个结点的next置为空,则走到最后就是最后一个节点的next置为空
    while(cur!=NULL)  //循环结束条件是cur==NULL,表示cur走到了最后一个节点位置
    {
        LinkNode* next=cur->next;
        //修改cur->next的指向
        cur->next=pre;
        //更新pre、cur
        pre=cur;
        cur=next;
    }
    //循环结束后,修改头指针的指向
    *phead=pre;

2.6单链表冒泡排序

思路:两重循环,总共需要多少趟结束,每趟需要比较多少次结束

void Swap(LinkListType* value1,LinkListType* value2)
{
    LinkListType tmp=*value1;
    *value1=*value2;
    *value2=tmp;
}
void LinkListBubbleSort(LinkNode* head)
{   
    //空链表
    if(head==NULL)
    {
        return;
    }
    //只有一个节点,不需要排序
    if(head->next==NULL)
    {
    //至少两个节点,进行排序
    LinkNode* count=head;  //代表趟数
    LinkNode* tail=NULL;   //代表每趟未排序的最后一个节点
    for(count=head;count!=NULL;count=count->next)  //总共比较的趟数,结束条件为count==NULL
    {
        LinkNode* cur=head;   //代表每趟要比较的节点,每次比较都从第一个节点开始
        for(cur=head;cur->next!=tail;cur=cur->next)
        {
            if((cur->data)>(cur->next->data))
            {
                Swap(&(cur->data),&(cur->next->data));
            }
        }
        //内循环结束后,最后一个节点是已排好序的,修改tail
        tail=cur;
    }

}

2.7将两个有序链表合并成一个有序链表

思路:定义两个分别指向链表1、2的指针cur1、cur2,遍历链表比较cur1->data和cur2->data的大小;再定义新合并链表的头指针new_head和尾指针new_tail,一旦找到最小元素节点就将其插入到新链表中,直到链表1、2遍历结束。

LinkNode* LinkListMerge(LinkNode* head1,LinkNode* head2)
{
    //两个链表都为空
    if(head1==NULL&&head2==NULL)
    {
        return NULL;
    }
    //两个链表中其中一个链表为空,则返回另一链表的头指针
    if(head1==NULL&&head2!=NULL)
    {
        return head2;
    }
    if(head2==NULL&&head1!=NULL)
    {
        return head1;
    }
    //两个链表都不为空时,进行节点比较
    LinkNode* cur1=head1;   //cur1用来遍历链表1
    LinkNode* cur2=head2;   //cur2用来遍历链表2
    LinkNode* new_head=NULL;   //new_head指新链表的头指针
    LinkNode* new_tail=NULL;   //new_tail指新链表的尾指针
    while(cur1!=NULL&&cur2!=NULL)  //循环结束条件是一旦有单链表走完,while循环就结束
    {
        //当cur1->data小于cur2->data时,更新cur1的值
        if(cur1->data<cur2->data)
        {
            //当新链表还是空链表时,修改新链表的new_head的值
            if(new_tail==NULL)
            {
                new_head=new_tail=cur1;
            }
            //当新链表不是空链表时,修改新链表的new_tail的指向
            else
            {
                new_tail->next=cur1;
                new_tail=new_tail->next;
            }
            //更新cur1的值
            cur1=cur1->next;
        }
        //当cur1->data大于等于cur2->data时,更新cur2的值
        else
        {
            //当新链表还是空链表时,修改新链表的new_head的值
            if(new_tail==NULL)
            {
                new_head=new_tail=cur2;
            }
            //当新链表不是空链表时,修改新链表的new_tail的指向
            else
            {
                new_tail->next=cur2;
                new_tail=new_tail->next;
            }
            //更新cur2的值
            cur2=cur2->next;
        }
    }
    //循环结束后判断哪个链表已经走完
    if(cur1==NULL)  //表示单链表1走完,将单链表2的剩余节点接到新链表的尾节点后
    {
        new_tail->next=cur2;
    }
    else  //表示单链表2走完,将单链表1的剩余节点接到新链表的尾节点后
    {
        new_tail->next=cur1;
    }
    return new_head;

}  

2.8查找单链表的中间节点(要求只能遍历一次单链表)

思路:定义两个快慢指针:slow、fast,fast每走两步slow每走一步,当fast走到最后时,slow就走到了单链表的中间节点。

LinkNode* LinkListMidNode(LinkNode* head)
{
    //单链表为空时
    if(head==NULL)
    {
        return NULL;
    }
    //单链表非空
    LinkNode* slow=head;
    LinkNode* fast=head;
    while(fast!=NULL&&fast->next!=NULL)  //循环结束条件是fast走到最后一个节点
    {
        slow=slow->next;  //slow每走一步
        fast=fast->next->next;  //fast每走两步
    }
    return slow;

}

2.9查找单链表的倒数第K个节点,要求只能遍历一次链表

思路:使用快慢指针slow、fast,当快指针fast==NULL时,则慢指针slow走到了倒数第K个节点。

LinkNode* LinkListFindKNode(LinkNode* head,size_t k)
{
    //空链表
    if(head==NULL)
    {
        return NULL;
    }
    //非空链表
    LinkNode* fast=head;
    LinkNode* slow=head;
    size_t i=0;
    for(i=0;i<k;i++)  //快指针fast先走K步
    {
        if(fast==NULL)  //判断单链表是否有倒数第K个节点
        {
            break;  //跳出说明单链表的长度不够
        }
        fast=fast->next;
    }
    //判断结束for循环的结束条件是break中止的还是自然结束
    if(i!=k)  //表示是break跳出for循环
    {
        return NULL;
    }
    while(fast!=NULL)
    {
        slow=slow->next;
        fast=fast->next;
    }
    return slow;

2.10 删除单链表的倒数第K个节点

思路:将单链表的长度len与K进行比较:1.K>len时直接退出 2.K==len时修改头指针 3.K<len时进行正常删除

//函数实现用到单链表长度,,所以先计算单链表的长度
size_t LinkListLength(LinkNode* head)
{
    //判断是否为单链表
    if(head==NULL)
    {
        return 0;
    }
    LinkNode* cur=head;
    size_t len=0;
    while(cur!=NULL)
    {
        len++;
        cur=cur->next;
    }
    return len;
}
void LinkListEraseKNode(LinkNode** phead,size_t k)
{
    //非法输入
    if(phead==NULL)
    {
        return;
    }
    //计算单链表的长度
    size_t len=LinkListLength(*phead);
    //K>len时,无法删除
    if(k>len)
    {
        return;
    }
    //k==len时,修改头指针的指向
    else if(k==len)
    {
        LinkNode* to_delete=*phead;
        *phead=to_delete->next;
        DestroyNode(to_delete);
    }
    //k<len时,正常删除
    else
    {
        LinkNode* to_find=LinkListFindKNode(*phead,k+1);
        LinkNode* to_delete=to_find->next;
        to_find->next=to_delete->next;
        DestroyNode(to_delete);
    }

}

2.11.1判断单链表的是否带环?

思路:定义两个快慢指针fast、slow,fast每次走两步,slow每次走一步,当慢指针和快指针重合时,表示带环。

int LinkListHasCircle(LinkNode* head)
{
    //空链表
    if(head==NULL)
    LinkNode* fast=head;
    LinkNode* slow=head;
    while(fast!=NULL&&fast->next!=NULL)  //当fast走到了最后一个节点时while循环结束
    {
        fast=fast->next->next;  //fast每次走两步
        slow=slow->next;    //slow每次走一步
        //判断fast、slow是否重合
        if(fast==slow)
        {
            return 1;
        }
    }
    //循环结束则表示单链表无环
    return 0;

}

2.11.2若单链表带环,求带环的单链表的环的长度

思路:1.找到fast、slow相遇的节点meet_node,从该节点的下一个节点meet_node->next开始遍历到相遇的节点处meet_node即为环的长度;2.利用1的思路需要找到相遇的节点位置,故对LinkListHasCircle()函数修改返回值类型,使其返回值为快慢指针相遇的节点位置,从而利用该函数找到两者相遇的节点位置。

LinkNode* LinkListHasCircle1(LinkNode* head)
{
    //空链表
    if(head==NULL)
    {
        return NULL;
    }
    //定义快慢指针
    LinkNode* fast=head;
    LinkNode* slow=head;
    while(fast!=NULL&&fast->next!=NULL)  //当fast走到了最后一个节点时while循环结束
    {
        fast=fast->next->next;  //fast每次走两步
        slow=slow->next;    //slow每次走一步
        //判断fast、slow是否重合
        if(fast==slow)
        {
            return slow;
        }
    }
    //循环结束则表示单链表无环
    return NULL;
}
size_t LinkListCircleLength(LinkNode* head)
{
    //判断空链表
    if(head==NULL)
    {
        return 0;
    }
    //计算相遇节点位置
    LinkNode* meet_node=LinkListHasCircle1(head);
    if(meet_node==NULL)
    {
        //空链表或者无环的情况
        return 0;
    }
    size_t len=1;
    LinkNode* cur=meet_node->next;
    while(cur!=meet_node)
    {
        cur=cur->next;
        len++;
    }
    return len;

}

2.11.3求环的入口点

思路:根据快慢指针之间的路程关系,找到求环的入口点的公式

LinkNode* LinkListCircleEntry(LinkNode* head)
{
    //空链表
    if(head==NULL)
    {
        return NULL;
    }
    //计算相遇的节点
    LinkNode* meet_node=LinkListHasCircle1(head);
    //meet_node==NULL时,无环
    if(meet_node==NULL)
    {
        return NULL;
    }
    //单链表有环
    LinkNode* cur1=head;
    LinkNode* cur2=meet_node;
    while(cur1!=cur2)
    {
        cur1=cur1->next;
        cur2=cur2->next;
    }
    return cur1;

}

2.12.1判断两个无环链表是否相交

思路:单链表1、2的头指针cur1、cur2分别遍历链表结束后,当cur1==cur2时,两单链表相交

int LinkListHasCross(LinkNode* head1,LinkNode* head2)
{
    //二者单链表有一为空时
    if(head1==NULL||head2==NULL)
    {
    LinkNode* cur1=head1;
    LinkNode* cur2=head2;
    for(;cur1->next!=NULL;cur1=cur1->next)
    {
        ;
    }
    for(;cur2->next!=NULL;cur2=cur2->next)
    {
        ;
    }
    //两单链表都遍历完
    if(cur1==cur2)
    {
        return 1;
    }
    return 0;

}

2.12.2求两个无环单链表相交的交点

思路:根据两单链表的长度分为两种情况:当len1>len2时,让单链表1先走len1-len2步;当len1<len2时,让单链表2先走len2-len1步;一旦它俩的链表长度一样长时,一起遍历,若cur1==cur2时,就是它们的交点,直到最后一个节点都没有相交,则表示无交点。

LinkNode* LinkListPosCross(LinkNode* head1,LinkNode* head2)
{
    //判断空链表
    if(head1==NULL||head2==NULL)
    {
        return NULL;
    }
    //计算两单链表的长度
    size_t len1=LinkListLength(head1);
    size_t len2=LinkListLength(head2);
    LinkNode* cur1=head1;
    LinkNode* cur2=head2;
    //当len1>len2时,让单链表1先走len1-len2步
    if(len1>len2)
    {
        int i=0;
        for(i=0;i<len1-len2;i++)
        {
            cur1=cur1->next;
        }
    }
    //当len2>len1时,让单链表1先走len1-len1步
    else
    {
        int i=0;
        for(i=0;i<len1-len1;i++)
        {
            cur2=cur2->next;
        }
    }
    //一旦走到这儿,就表示len1==len2,两单链表一起走
    while(cur1!=NULL&&cur2!=NULL)
    {
        //当两指针相等时
        if(cur1==cur2)
        {
            return cur1;
        }
        cur1=cur1->next;
        cur2=cur2->next;
    }
    //一旦走到这儿,表示无交点
    return NULL;

}

2.13.1判断两个单链表是否相交?(可能带环,可能不带环)

思路:分为三种情况:1.都不带环,用之前的函数LinkListHasCross判断;2.一个带环一个不带环时,一定不相交;3.都带环(都带环分为三种情况:不相交;相交在环外;相交在环内)

int LinkListHasCrossEx(LinkNode* head1,LinkNode* head2)
{
    //判断空链表
    if(head1==NULL||head2==NULL)
    {
        return 0;
    }
    //计算两单链表的环入口点
    LinkNode* entry1=LinkListCircleEntry(head1);
    LinkNode* entry2=LinkListCircleEntry(head2);
    //1.都不带环时,用LinkListHasCross判断两个单链表是否相交
    if(entry1==NULL&&entry2==NULL)
    {
        int cross=LinkListHasCross(head1,head2);
        return cross;
    }
    //2.一个带环一个不带环时,,一定不相交
    else if((entry1==NULL&&entry2!=NULL)||(entry1!=NULL&&entry2==NULL))
    {
        return 0;
    }
    //3.都带环时,又分三种情况讨论
    else
    {
        //1.在环外相交时,入口点相同
        if(entry1==entry2)
        {
            return 1;
        }
        //2.在环内相交,从一个入口点环绕一周遇到另一个入口点
        LinkNode* cur=entry1->next;
        while(cur!=entry1)
        {
            if(cur==entry2)
            {
                return 1;
            }
            cur=cur->next;
        }
        //3.不相交
        return 0;
    }
    return 0;

}

2.13.2计算两个单链表的交点(可能带环,可能不带环)

思路:分为三种情况:1.都不带环,用之前的函数LinkListPosCross计算;2.一个带环一个不带环时,一定不相交,返回NULL;3.都带环(都带环分为三种情况:不相交,返回NULL;相交在环外,计算;相交在环内,计算)

LinkNode* LinkListPosCrossEx(LinkNode* head1,LinkNode* head2)
{
    //判断空链表
    if(head1==NULL||head2==NULL)
    {
        return NULL;
    }
    //计算两单链表的环入口点
    LinkNode* entry1=LinkListCircleEntry(head1);
    LinkNode* entry2=LinkListCircleEntry(head2);
    //1.都不带环时,用LinkListPosCross计算两个单链表的交点
    if(entry1==NULL&&entry2==NULL)
    {
        LinkNode* pos=LinkListPosCross(head1,head2);
        return pos;
    }
    //2.一个带环一个不带环时,,一定不相交
    else if((entry1==NULL&&entry2!=NULL)||(entry1!=NULL&&entry2==NULL))
    {
        return NULL;
    }
    //3.都带环时,又分三种情况讨论
    else
    {
        LinkNode* cur1=head1;
        LinkNode* cur2=head2;
        //1.在环外相交时,入口点相同,计算两个单链表的长度,让较长的先走相差的节点个数步,再一起走
        if(entry1==entry2)
        {
            size_t len1=LinkListLength(head1);
            size_t len2=LinkListLength(head2);
            if(len1>len2)
            {
                int i=0;
                for(i=0;i<len1-len2;i++)
                {
                    cur1=cur1->next;
                }
            }
            else
            {
                int i=0;
                for(i=0;i<len2-len1;i++)
                {
                    cur2=cur2->next;
                }
            }
            while(cur1!=NULL)
            {
                if(cur1==cur2)
                {
                    return cur1;
                }
                cur1=cur1->next;
        }
        //2.在环内相交,从一个入口点环绕一周遇到另一个入口点,此时有两个交点:两个单链表的环入口点
        LinkNode* cur=entry1->next;
        while(cur!=entry1)
        {
            if(cur==entry2)
            {
                return entry2;
            }
            cur=cur->next;
        }
        //3.不相交
        return NULL;
    }
    return NULL;

}

2.14求两个已排序的链表中相同的元素

思路:两个指针cur1、cur2分别遍历两个链表有三种情况:1.cur1->data<cur2->data时,让cur1=cur1->next;2.cur1->data>cur2->data时,让cur2=cur->next;3.cur1->data==cur2->data时,创建新节点,节点元素值为cur1->data或cur2->data

LinkNode* LinkListEqualsNode(LinkNode* head1,LinkNode* head2)
{
    //判断链表是否为空
    if(head1==NULL||head2==NULL)
    {
        return NULL;
    }
    //创建存放相同节点的链表的头尾指针
    LinkNode* head=NULL;
    LinkNode* tail=NULL;
    //定义cur1、cur2去遍历两个链表
    LinkNode* cur1=head1;
    LinkNode* cur2=head2;
    while(cur1!=NULL&&cur2!=NULL)
    {
        //cur1->data<cur2->data时,更新cur1的值
        if(cur1->data<cur2->data)
        {
            cur1=cur1->next;
        }
        //cur1->data>cur2->data时,更新cur2的值
        else if(cur1->data>cur2->data)
        {
            cur2=cur2->next;
        }
        //cur1->data==cur2->data时,创建新节点
        else
        {
            LinkNode* new_node=CreateNode(cur1->data);
            if(head==NULL)
            {
                head=tail=new_node;
            }
            else
            {
                tail->next=new_node;
                tail=tail->next;
            }
            cur1=cur1->next;
            cur2=cur2->next;
        }
    }
    return head;

}

2.15.1复杂链表的复制

思路:1.先按照一般链表复制的方式进行复制
           2.遍历旧链表求出每个节点的random的偏移量(偏移量:random指向位置相对头指针的偏移量)

           3.根据所计算的每个节点的偏移量去遍历新链表给每个节点的random进行赋值

//Diff函数
size_t Diff(ComplexNode* src,ComplexNode* dst)
{
    size_t offset=0;
    while(src!=NULL)
    {
        if(src==dst)
            break;
        offset++;
        src=src->next;
    }
    //循环结束,若src==NULL表示在链表中没找到random,返回特值
    if(src==NULL)
    {
        return (size_t)-1;
    }
    return offset;
}
//Step函数
ComplexNode* Step(ComplexNode* head,size_t offset)
{
    ComplexNode* cur=head;
    size_t i=0;
    while(1)
    {
        if(cur==NULL)
        {
            return NULL;
        }
        if(i==offset)
        {
            return cur;
        }
        i++;
        cur=cur->next;
    }
    return cur;
}
ComplexNode* ComplexListCopy(ComplexNode* head)
{
    //判断链表是否为空
    if(head==NULL)
    {
        return NULL;
    }
    //1.先按照一般链表复制的方式进行复制
    ComplexNode* cur=head;
    ComplexNode* new_head=NULL;
    ComplexNode* new_tail=NULL;
    for(cur=head;cur!=NULL;cur=cur->next)
    {
        if(new_head==NULL)
        {
            new_head=new_tail=cur;
        }
        else
        {
            new_tail->next=cur;
            new_tail=new_tail->next;
        }
    }
    //2.遍历旧链表求出每个节点的random的偏移量(偏移量:random指向位置相对头指针的偏移量)
    //3.根据所计算的每个节点的偏移量去遍历新链表给每个节点的random进行赋值
    ComplexNode* new_cur=new_head;
    for(cur=head;cur!=NULL;cur=cur->next,new_cur=new_cur->next)
    {
        //当cur->random==NULL时,直接将new_cur->random修改为NULL
        if(cur->random==NULL)
        {
            new_cur->random=NULL;
            continue;
        }
        //通过Diff函数计算出节点的偏移量
        size_t offset=Diff(head,cur->random);
        //通过Step函数给new_cur->random赋值
        new_cur->random=Step(new_head,offset);
    }
    return new_head;
}
//创建节点
ComplexNode* CreateComplexNode(ComplexListType value)
{
    ComplexNode* new_node=(ComplexNode*)malloc(sizeof(ComplexNode));
    new_node->data=value;
    new_node->next=NULL;
    new_node->random=NULL;
    return new_node;

}

2.15.2复杂单链表的复制

思路:1.对单链表进行遍历时,每个节点后插入一个节点,节点元素为其本身
           2.遍历维护新节点的random指针

           3.拆除新节点 

ComplexNode* ComplexListCopyEx(ComplexNode* head)
{
    //判断链表是否为空
    if(head==NULL)
    {
        return NULL;
    }
    //1.对单链表进行遍历时,每个节点后插入一个节点,节点元素为其本身
    ComplexNode* cur=head;
    for(cur=head;cur!=NULL;cur=cur->next->next)
    {
        ComplexNode* new_node=CreateComplexNode(cur->data);
        new_node->next=cur->next;
        cur->next=new_node;
    }
    //2.遍历维护新节点的random指针
    for(cur=head;cur!=NULL;cur=cur->next->next)
    {
        ComplexNode* new_cur=cur->next;
        if(cur->random==NULL)
        {
            new_cur->random==NULL;
            continue;
        }
        new_cur->random=cur->random->next;
    }
    //3.拆除新节点
    ComplexNode* new_head=NULL;
    ComplexNode* new_tail=NULL;
    for(cur=head;cur!=NULL;cur=cur->next)
    {
        ComplexNode* new_cur=cur->next;
        cur->next=new_cur->next;
        if(new_head==NULL)
        {
            new_head=new_tail=new_cur;
        }
        else
        {
            new_tail->next=new_cur;
            new_tail=new_tail->next;
        }
    }
    return new_head;

}

3.测试以上函数的正确与否

3.1测试LinkListInit

void Test_LinkListInit()
{
    HEADER;
    LinkNode* head; //创建头指针
    LinkListInit(&head);
    LinkListPrintChar(head,"初始化链表");

}

3.2测试LinkListPushBack

void Test_LinkListPushBack()
{
    HEADER;
    LinkNode* head;
    LinkListInit(&head);
    LinkListPushBack(&head,'a');
    LinkListPushBack(&head,'b');
    LinkListPushBack(&head,'c');
    LinkListPushBack(&head,'d');
    LinkListPrintChar(head,"尾插四个节点:'a''b''c''d'");

}

3.3测试LinkListReversePrintChar

void Test_LinkListReversePrintChar()
{
    HEADER;
    LinkNode* head;
    LinkListInit(&head);
    LinkListPushBack(&head,'a');
    LinkListPushBack(&head,'b');
    LinkListPushBack(&head,'c');
    LinkListPushBack(&head,'d');
    LinkListPrintChar(head,"从头到尾打印:'a''b''c''d'");
    printf("从尾到头打印\n");
    LinkListReversePrintChar(head);
    printf("\n");

}

3.4测试LinkListErase

void Test_LinkListErase()
{
    HEADER;
    LinkNode* head;
    LinkListInit(&head);
    LinkListPushBack(&head,'a');
    LinkListPushBack(&head,'b');
    LinkListPushBack(&head,'c');
    LinkListPushBack(&head,'d');
    LinkListErase(&head,head->next);
    LinkListPrintChar(head,"删除节点'b'");

}

3.5测试LinkListBeforeInsert

void Test_LinkListBeforeInsert()
{
    HEADER;
    LinkNode* head;
    LinkListInit(&head);
    LinkListPushBack(&head,'a');
    LinkListPushBack(&head,'b');
    LinkListPushBack(&head,'c');
    LinkListPushBack(&head,'d');
    LinkListBeforeInsert(&head,head,'x');
    LinkListPrintChar(head,"在'a'前插入'x'");
    LinkListBeforeInsert(&head,head->next->next,'y');
    LinkListPrintChar(head,"在'b'前插入'y'");

}

3.6测试LinkListJosephCircle

void Test_LinkListJosephCircle()
{
    HEADER;
    //创建约瑟夫环
    LinkNode* a=CreateNode('a');
    LinkNode* b=CreateNode('b');
    LinkNode* c=CreateNode('c');
    LinkNode* d=CreateNode('d');
    LinkNode* e=CreateNode('e');
    LinkNode* f=CreateNode('f');
    LinkNode* g=CreateNode('g');
    a->next=b;
    b->next=c;
    c->next=d;
    d->next=e;
    e->next=f;
    f->next=g;
    g->next=a;
    LinkNode* end_node=LinkListJosephCircle(a,3);
    printf("最终的节点为:[%p][%c]\n",end_node,end_node->data);

}

3.7.1测试LinkListReverse

void Test_LinkListReverse()
{
    HEADER;
    LinkNode* head;
    LinkListInit(&head);
    LinkListPushBack(&head,'a');
    LinkListPushBack(&head,'b');
    LinkListPushBack(&head,'c');
    LinkListPushBack(&head,'d');
    LinkListPrintChar(head,"正序的单链表");
    LinkListReverse(&head);
    LinkListPrintChar(head,"逆置后的单链表");

}

3.7.2测试LinkListReverseEx

void Test_LinkListReverseEx()
{
    HEADER;
    LinkNode* head;
    LinkListInit(&head);
    LinkListPushBack(&head,'a');
    LinkListPushBack(&head,'b');
    LinkListPushBack(&head,'c');
    LinkListPushBack(&head,'d');
    LinkListPrintChar(head,"正序的单链表");
    LinkListReverseEx(&head);
    LinkListPrintChar(head,"逆置后的单链表");

}

3.8测试LinkListBubbleSort

void Test_LinkListBubbleSort()
{
    HEADER;
    LinkNode* head;
    LinkListInit(&head);
    LinkListPushBack(&head,'x');
    LinkListPushBack(&head,'d');
    LinkListPushBack(&head,'a');
    LinkListPushBack(&head,'b');
    LinkListBubbleSort(head);
    LinkListPrintChar(head,"对单链表'x''d''a''b'升序排序");

}

3.9测试LinkListMerge

void Test_LinkListMerge()
{
    HEADER;
    LinkNode* head1;
    LinkListInit(&head1);
    LinkListPushBack(&head1,'a');
    LinkListPushBack(&head1,'d');
    LinkListPushBack(&head1,'f');
    LinkListPushBack(&head1,'k');
    LinkNode* head2;
    LinkListInit(&head2);
    LinkListPushBack(&head2,'b');
    LinkListPushBack(&head2,'e');
    LinkListPushBack(&head2,'f');
    LinkListPushBack(&head2,'g');
    LinkListPrintChar(head1,"打印链表1");
    LinkListPrintChar(head2,"打印链表2");
    LinkNode* ret=LinkListMerge(head1,head2);
    LinkListPrintChar(ret,"打印新链表");

}

3.10 测试LinkListMidNode

void Test_LinkListMidNode()
{
    HEADER;
    LinkNode* head;
    LinkListInit(&head);
    LinkListPushBack(&head,'a');
    LinkListPushBack(&head,'d');
    LinkListPushBack(&head,'f');
    LinkListPushBack(&head,'k');
    LinkNode* ret=LinkListMidNode(head);
    printf("except f,autual %c\n",ret->data);

}

3.11 测试LinkListFindKNode

void Test_LinkListFindKNode()
{
    HEADER;
    LinkNode* head;
    LinkListInit(&head);
    LinkListPushBack(&head,'a');
    LinkListPushBack(&head,'d');
    LinkListPushBack(&head,'f');
    LinkListPushBack(&head,'k');
    LinkNode* ret=LinkListFindKNode(head,3);
    printf("except d,autual %c\n",ret->data);

}

3.12 测试LinkListEraseKNode

void Test_LinkListEraseKNode()
{
    HEADER;
    LinkNode* head;
    LinkListInit(&head);
    LinkListPushBack(&head,'a');
    LinkListPushBack(&head,'d');
    LinkListPushBack(&head,'f');
    LinkListPushBack(&head,'k');
    LinkListPrintChar(head,"链表元素为:'a' 'd' 'f' 'k'");
    //测试所写的计算单链表长度的函数LinkListLength是否正确
    size_t len=LinkListLength(head);
    printf("expected 4,actual %d\n",len);
    LinkListEraseKNode(&head,4);
    LinkListPrintChar(head,"删除倒数第4个元素");
    LinkListEraseKNode(&head,6);
    LinkListPrintChar(head,"删除倒数第6个元素");
    LinkListEraseKNode(&head,1);
    LinkListPrintChar(head,"删除倒数第1个元素");
    LinkListEraseKNode(&head,2);
    LinkListPrintChar(head,"删除倒数第2个元素");

}

3.13.1 测试LinkListHasCircle

void Test_LinkListHasCircle()
{
    HEADER;
    //测试不带环的单链表
    LinkNode* head;
    LinkListInit(&head);
    LinkListPushBack(&head,'a');
    LinkListPushBack(&head,'b');
    LinkListPushBack(&head,'c');
    LinkListPushBack(&head,'d');
    int ret=LinkListHasCircle(head);
    printf("expected 0,actual %d\n",ret);
    //测试为环的单链表 
    LinkNode* a=CreateNode('a');
    LinkNode* b=CreateNode('b');
    LinkNode* c=CreateNode('c');
    LinkNode* d=CreateNode('d');
    a->next=b;
    b->next=c;
    c->next=d;
    d->next=c;
    ret=LinkListHasCircle(a);
    printf("expected 1,actual %d\n",ret);

}

3.13.2 测试LinkListCircleLength

void Test_LinkListCircleLength()
{
    HEADER;
    //测试不带环的单链表
    LinkNode* head;
    LinkListInit(&head);
    LinkListPushBack(&head,'a');
    LinkListPushBack(&head,'b');
    LinkListPushBack(&head,'c');
    LinkListPushBack(&head,'d');
    size_t ret=LinkListCircleLength(head);
    printf("expected 0,actual %d\n",ret);
    //测试为环的单链表 
    LinkNode* a=CreateNode('a');
    LinkNode* b=CreateNode('b');
    LinkNode* c=CreateNode('c');
    LinkNode* d=CreateNode('d');
    a->next=b;
    b->next=c;
    c->next=d;
    d->next=c;
    ret=LinkListCircleLength(a);
    printf("expected 2,actual %d\n",ret);

}

3.13.3 测试LinkListCircleEntry

void Test_LinkListCircleEntry()
{
    HEADER;
    //测试不带环的单链表
    LinkNode* head;
    LinkListInit(&head);
    LinkListPushBack(&head,'a');
    LinkListPushBack(&head,'b');
    LinkListPushBack(&head,'c');
    LinkListPushBack(&head,'d');
    LinkNode* ret=LinkListCircleEntry(head);
    printf("expected NULL,actual %p\n",ret);
    //测试为环的单链表 
    LinkNode* a=CreateNode('a');
    LinkNode* b=CreateNode('b');
    LinkNode* c=CreateNode('c');
    LinkNode* d=CreateNode('d');
    a->next=b;
    b->next=c;
    c->next=d;
    d->next=b;
    ret=LinkListCircleEntry(a);
    printf("expected 'b',actual %c\n",ret->data);

}

3.14.1测试LinkListHasCross

void Test_LinkListHasCross()
{
    HEADER;
    LinkNode* a=CreateNode('a');
    LinkNode* b=CreateNode('b');
    LinkNode* c=CreateNode('c');
    LinkNode* d=CreateNode('d');
    LinkNode* e=CreateNode('e');
    a->next=b;
    b->next=c;
    c->next=d;
    int ret=LinkListHasCross(a,b);
    printf("expected 1,actual %d\n",ret);
    ret=LinkListHasCross(a,e);
    printf("expected 0,actual %d\n",ret);

}

3.14.2测试LinkListPosCross

void Test_LinkListPosCross()
{
    HEADER;
    LinkNode* a=CreateNode('a');
    LinkNode* b=CreateNode('b');
    LinkNode* c=CreateNode('c');
    LinkNode* d=CreateNode('d');
    LinkNode* e=CreateNode('e');
    a->next=b;
    b->next=c;
    c->next=d;
    LinkNode* pos=LinkListPosCross(a,b);
    printf("expected 'b',actual %c\n",pos->data);
    pos=LinkListPosCross(a,e);
    printf("expected NULL,actual %p\n",pos);

}

3.15.1测试LinkListHasCrossEx

void Test_LinkListHasCrossEx()
{
    HEADER;
    //带环链表1
    LinkNode* a=CreateNode('a');
    LinkNode* b=CreateNode('b');
    LinkNode* c=CreateNode('c');
    LinkNode* d=CreateNode('d');
    a->next=b;
    b->next=c;
    c->next=d;
    d->next=c;
    //带环链表2
    LinkNode* a1=CreateNode('a');
    LinkNode* b1=CreateNode('b');
    LinkNode* c1=CreateNode('c');
    LinkNode* d1=CreateNode('d');
    a1->next=b1;
    b1->next=c1;
    c1->next=d1;
    d1->next=c1;
    LinkNode* e1=CreateNode('e');
    e1->next=d;
    //不带环链表3
    LinkNode* e=CreateNode('e');
    LinkNode* f=CreateNode('f');
    e->next=f;
    //不带环链表4
    LinkNode* g=CreateNode('g');
    LinkNode* h=CreateNode('h');
    g->next=h;
    //不带环链表5
    LinkNode* i=CreateNode('i');
    LinkNode* j=CreateNode('j');
    i->next=j;
    j->next=g;
    //测试都不带环
    int ret=LinkListHasCrossEx(e,g);
    printf("expected 0,actual %d\n",ret);
    ret=LinkListHasCrossEx(i,g);
    printf("expected 1,actual %d\n",ret);
    //测试一个带环一个不带环
    ret=LinkListHasCrossEx(a,e);
    printf("expected 0,actual %d\n",ret);
    //测试都带环
    ret=LinkListHasCrossEx(a,a1);
    printf("expected 0,actual %d\n",ret);
    ret=LinkListHasCrossEx(a,e1);
    printf("expected 1,actual %d\n",ret);

}

3.15.2 测试LinkListPosCrossEx

void Test_LinkListPosCrossEx()
{
    HEADER;
    //带环链表1
    LinkNode* a=CreateNode('a');
    LinkNode* b=CreateNode('b');
    LinkNode* c=CreateNode('c');
    LinkNode* d=CreateNode('d');
    a->next=b;
    b->next=c;
    c->next=d;
    d->next=c;
    //带环链表2
    LinkNode* a1=CreateNode('a');
    LinkNode* b1=CreateNode('b');
    LinkNode* c1=CreateNode('c');
    LinkNode* d1=CreateNode('d');
    a1->next=b1;
    b1->next=c1;
    c1->next=d1;
    d1->next=c1;
    LinkNode* e1=CreateNode('e');
    e1->next=d;
    //不带环链表3
    LinkNode* e=CreateNode('e');
    LinkNode* f=CreateNode('f');
    e->next=f;
    //不带环链表4
    LinkNode* g=CreateNode('g');
    LinkNode* h=CreateNode('h');
    g->next=h;
    //不带环链表5
    LinkNode* i=CreateNode('i');
    LinkNode* j=CreateNode('j');
    i->next=j;
    j->next=g;
    //测试都不带环
    LinkNode* pos=LinkListPosCrossEx(e,g);
    printf("expected NULL,actual %p\n",pos);
    pos=LinkListPosCrossEx(i,g);
    printf("expected 'g',actual %c\n",pos->data);
    //测试一个带环一个不带环
    pos=LinkListPosCrossEx(a,e);
    printf("expected NULL,actual %p\n",pos);
    //测试都带环
    pos=LinkListPosCrossEx(a,a1);
    printf("expected NULL,actual %p\n",pos);
    pos=LinkListPosCrossEx(a,e1);
    printf("expected 'd',actual %c\n",pos->data);

}

3.16 测试LinkListEqualsNode

void Test_LinkListEqualsNode()
{
    HEADER;
    LinkNode* head1=NULL;
    LinkListPushBack(&head1,'a');
    LinkListPushBack(&head1,'b');
    LinkListPushBack(&head1,'c');
    LinkListPushBack(&head1,'d');
    LinkListPushBack(&head1,'z');
    LinkNode* head2=NULL;
    LinkListPushBack(&head2,'a');
    LinkListPushBack(&head2,'c');
    LinkListPushBack(&head2,'d');
    LinkListPushBack(&head2,'g');
    LinkListPushBack(&head2,'h');
    LinkListPushBack(&head2,'z');
    LinkListPrintChar(head1,"单链表'a''b''c''d''z'");
    LinkListPrintChar(head2,"单链表'a''c''d''g''h''z'");
    LinkNode* new_head=LinkListEqualsNode(head1,head2);
    LinkListPrintChar(new_head,"新链表");

}

3.17.1 测试ComplexListPrintChar

//打印复杂单链表
void ComplexListPrintChar(ComplexNode* head,char* msg)
{
    //非法输入
    if(msg==NULL)
    {
        return;
    }
    //先打印字符串
    printf("%s\n",msg);
    //空链表
    if(head==NULL)
    {
        return;
    }
    //非空链表
    ComplexNode* cur=head;
    while(cur!=NULL)
    {
        if(cur->random==NULL)
        {
            printf("[%p][%c][%c] ",cur,cur->data,'$');
            cur=cur->next;
        }
        else
        {
            printf("[%p][%c][%c] ",cur,cur->data,cur->random->data);
            cur=cur->next;
        }
    }
    printf("\n");

}

3.17.2 测试ComplexListCopy

void Test_ComplexListCopy()
{
    HEADER;
    ComplexNode* a=CreateComplexNode('a');
    ComplexNode* b=CreateComplexNode('b');
    ComplexNode* c=CreateComplexNode('c');
    ComplexNode* d=CreateComplexNode('d');
    a->next=b;
    b->next=c;
    c->next=d;
    a->random=d;
    b->random=c;
    c->random=NULL;
    d->random=a;
    ComplexListPrintChar(a,"复制复杂链表之前");
    ComplexNode* new_head=ComplexListCopy(a);
    ComplexListPrintChar(new_head,"复制复杂链表之后");

}

3.17.3 测试ComplexListCopyEx

void Test_ComplexListCopyEx()
{
    HEADER;
    ComplexNode* a=CreateComplexNode('a');
    ComplexNode* b=CreateComplexNode('b');
    ComplexNode* c=CreateComplexNode('c');
    ComplexNode* d=CreateComplexNode('d');
    a->next=b;
    b->next=c;
    c->next=d;
    a->random=d;
    b->random=c;
    c->random=NULL;
    d->random=a;
    ComplexListPrintChar(a,"复制复杂链表之前");
    ComplexNode* new_head=ComplexListCopyEx(a);
    ComplexListPrintChar(new_head,"复制复杂链表之后");

}

//主函数

int main()
{
    Test_LinkListInit();
    Test_LinkListPushBack();
    Test_LinkListReversePrintChar();
    Test_LinkListErase();
    Test_LinkListBeforeInsert();
    Test_LinkListJosephCircle();
    Test_LinkListReverse();
    Test_LinkListReverseEx();
    Test_LinkListBubbleSort();
    Test_LinkListMerge();
    Test_LinkListMidNode();
    Test_LinkListFindKNode();
    Test_LinkListEraseKNode();
    Test_LinkListHasCircle();
    Test_LinkListCircleLength();
    Test_LinkListCircleEntry();
    Test_LinkListHasCross();
    Test_LinkListPosCross();
    Test_LinkListHasCrossEx();
    Test_LinkListPosCrossEx();
    Test_LinkListEqualsNode();
    Test_ComplexListCopy();
    Test_ComplexListCopyEx();
    return 0;

}

以上就是关于链表的常见笔试面试题!


猜你喜欢

转载自blog.csdn.net/tongxuexie/article/details/79832762