本文章主要介绍关于链表的常见笔试面试题: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;
}
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遍历结束。
{
//两个链表都为空
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()函数修改返回值类型,使其返回值为快慢指针相遇的节点位置,从而利用该函数找到两者相遇的节点位置。
{
//空链表
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
{
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
{
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
{
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;
}
以上就是关于链表的常见笔试面试题!