C语言实现单链表面试题(基础篇)
1.比较顺序表和链表的优缺点,说说它们分别在什么场景下使用?
顺序表:
(1)内存中地址连续
(2)支持随机查找,类似于数组用下标查找
(3)适用于需要大量访问元素而很少增加或者删除元素的程序
链表:
(1)内存中地址不连续
(2)不支持随机查找
(3)适用于需要进行大量的增加或者删除而很少查找的程序
先定义一个单链表:
#include<stdio.h>
typedef int DataType;
typedef struct ListNode
{
DataType data;
struct Node* next;
}Node,*pNode,*pList;
2.逆序打印单链表
思路:这里采用递归实现逆序打印,一直递归到最后一个,然后打印出来,返回上一层,这就实现了逆序打印。
具体代码如下:
//逆序打印单链表
void ReversePrint(pList plist)
{
pNode cur = plist;
if(cur == NULL)
{
return;
}
else if(cur->next == NULL)
{
printf("%d",cur->data);
return;
}
else
{
ReversePrint(cur->next);//递归调用
printf("%d",cur->data);
}
}
3.删除一个无头单链表的非尾节点
思路:删除无头单链表的非尾节点,也就是不删除尾,我们就要判断不为尾节点再删除;交换当前节点和下一个节点的值,释放掉下一个节点。
具体代码如下:
//删除无头单链表的非尾节点
void EraseNotTail(pNode pos)
{
pNode del = pos->next;
assert(pos->next != NULL);
//数据替换删除
pos->data = pos->next->data;
pos->next = pos->next->next;
free(del);
}
4.在无头单链表的一个节点前插入一个节点
思路:在当前节点后面插入一个节点,交换插入的节点与当前节点的数据。
具体代码如下:
//在当前节点前插入一个数据x
void InsertFrontNode(pNode pos,DataType d)
{
pNode NewNode = BuyNode(d);
pNode tmp = NULL;
pNode cur = pos;
NewNode->next = pos->next;
cur->next = NewNode;
//数据交换
tmp = cur->data;
cur->data = NewNode->data;
NewNode->data = tmp;
}
5.单链表实现约瑟夫环
思路:每次数到num的时候删除该节点,直到最后只剩下一个节点,并返回该节点。
具体代码如下:
//约瑟夫环
pNode JosephCycle(pList* pplist,int num)
{
pNode cur = *pplist;
assert(pplist);
while(1)
{
int i = 0;
pNode del = NULL;
if(cur->next == cur)
{
break;
}
for(i=0;i<num-1;i++)
{
cur = cur->next;
}
printf("%d\n",cur->data);
del = cur->next;
cur->data = cur->next->data;
cur->next = cur->next->next;
free(del);
}
return cur;
}
6.逆置/反转单链表
思路:创建一个新的头指针,用cur和tmp摘节点进行头插,每次把链表的头结点指向新的头结点。
具体代码如下:
//链表的逆序
void ReverseList(pList* pplist)
{
pList NewList = NULL;
pNode cur = *pplist;
assert(pplist);
//空链表或者一个节点不用逆序
if((*pplist == NULL) || ((*pplist)->next == NULL))
{
return;
}
//
while(cur)
{
pNode tmp = cur;
cur = cur->next;
tmp->next = NewList;
NewList = tmp;
}
*pplist = NewList;
}
7.单链表排序(冒泡排序&快速排序)
思路:用cur和next控制,用tail标记链表尾部,符合条件则交换,当next走到tail时第一趟结束,然后重复上述过程。
具体代码如下:
//排序链表(冒泡)
void BubbleSort(pList* pplist)
{
pNode cur = *pplist;
pNode tail = NULL;
assert(pplist);
if((cur == NULL) || (cur->next == NULL))
{
return;
}
while(cur->next != tail)
{
while(cur->next != tail)
{
if(cur->data > cur->next->data)
{
DataType tmp = cur->data;
cur->data = cur->next->data;
cur->next->data = tmp;
}
cur = cur->next;
}
tail = cur;
cur = *pplist;
}
}
8.合并两个有序链表,合并后依然有序
思路:定义一个新的头指针,一开始指向pList1和pList2首元素较小的那个,然后依次遍历两个链表,每次把较小的那个值加到新头指针List的后面。
具体代码如下:
//合并两个有序的链表
pList Merge(pList l1,pList l2)
{
pList myList = NULL;
pNode tail = NULL;
if(l1 == l2)
{
return l1;
}
if(l1 == NULL)
{
return l2;
}
if(l2 == NULL)
{
return l1;
}
//确定第一个节点
if(l1->data <= l2->data)
{
myList = l1;
l1 = l1->next;
}
else
{
myList = l2;
l2 = l2->next;
}
//把尾指针赋值
tail = myList;
while((l1 != NULL)&&(l2 != NULL))
{
if(l1->data <= l2->data)
{
tail->next = l1;
tail = l1;
l1 = l1->next;
}
else
{
tail->next = l2;
tail = l2;
l2 = l2->next;
}
}
//有一条为空了
if(l1 == NULL)
{
tail->next = l2;
}
else
{
tail->next = l1;
}
return myList;
}
9.查找单链表的中间节点,要求只能遍历一次链表
思路:快慢针指,每次快指针走两步,慢指针走一步;
(1)链表有奇数个节点,快指针走到结尾时慢指针刚好走到中间。
(2)链表有偶数个节点,快指针指向空,慢指针走到链表中间。
具体代码如下:
//查找中间节点
pNode FindMidNode(pList plist)//快慢指针
{
pNode fast = plist;
pNode slow = plist;
while(fast && fast->next)
{
fast = fast->next->next;
slow = slow->next;
}
return slow;
}
10.查找单链表的倒数第k个节点,要求只能遍历一次链表
思路:快慢指针,快指针先走k-1步,然后快慢指针同时一步一步的走,当快指针走到链表结尾时,慢指针刚好指向第k个节点。
具体代码如下:
//删除单链表倒数第k个节点,时间复杂度o(N)
void DelKNode(pList* pplist,int k)
{
pNode fast = *pplist;
pNode slow = *pplist;
assert(pplist);
if(*pplist == NULL)
{
return;
}
while(fast->next != NULL)
{
--k;
if(k<=0)
{
slow = slow->next;
}
fast = fast->next;
}
if(k<=0)
{
pNode del = slow->next;
slow->data = slow->next->data;
slow->next = slow->next->next;
free(del);
}
}
更精彩的请见下一篇博客!