关于单链表的一些面试题-基础篇(C语言实现)
1.比较顺序表和链表的优缺点,说说它们分别在什么场景下使用?
- 在需要进行频繁查找,但很少进行插入和删除操作的时候,宜采用存储结构
- 在需要频繁插入、删除时,链表结构更佳
2.从尾到头打印单链表
因为单链表本身属性的性质,在这里采用递归的方式实现从尾到头打印单链表的方法
具体思路如图所示:
代码实现
void PrintTailToHead(ListNode *pList)
{
if(pList == NULL)
{
return;
}
PrintTailToHead(pList->next);
printf("%d->",pList->data);
}
3.删除一个无头单链表的非尾节点
注意:无头单链表并不是指单链表没有头指针,而是指在题目实现中不给出该单链表的头指针
删除无头单链表的非尾结点:
1、需要注意判断是否是非尾结点
2、在移动时注意要给两个指针,方便找到赋值元素的上一个结点位置
3、记得释放被删指针及临时指针的内存,防止出现野指针或者内存泄露的问题
具体思路如图所示:
代码实现
void EraseNoHeadList(ListNode* pList,ListNode** pos)
{
ListNode* next = *pos;
ListNode* pre = *pos;
assert(*pos);
if(((*pos) == pList) || ((*pos)->next == NULL))
{
return;
}
else
{
while(next->next)
{
pre = next;
pre->data = (next->next)->data;
next = next->next;
}
free(next);
pre->next = NULL;
}
}
4.在无头单链表的一个节点前插入一个节点
注意:无头单链表并不是指单链表没有头指针,而是指在题目实现中不给出该单链表的头指针
在无头单链表的一个节点前插入一个节点:
1、因为是无头单链表,所以无法找到当前结点的前一个位置
2、故采取在当前结点的下一个位置插入一个新结点
3、之后再交换两结点中的数据即可
具体思路如图所示:
代码实现
void InsertNoHeadList(ListNode** ppList, ListNode* pos,DataType x)
{
if(*ppList == NULL)
*ppList= CreateNode(x);
else
{
ListNode* cur = CreateNode(x);
cur->next = pos->next;
pos->next = cur;
cur->data = pos->data;
pos->data = x;
}
}
5.单链表实现约瑟夫环
实现思路
1、使单链表成环
2、判断链表是否为空
3、循环遍历代码,找到要删除的结点
4、删除结点
代码实现
ListNode* JosephRing(ListNode* pList, int m)//单链表实现约瑟夫环,m指每一轮需要报的数
{
ListNode* cur = pList;
ListNode* next;
ListNode* tmp = pList;
int count;
while(tmp->next)//使单链表成环
{
tmp = tmp->next;
}
tmp->next = pList;
if(pList == NULL)//判断链表是否为空
return;
while(cur != cur->next)
{
count = m;
while(--count)//走(k-1)步
{
cur = cur->next;
}
next = cur->next;
cur->data = next->data;
cur->next = next->next;
free(next);
}
cur->next = NULL;
return cur;
}
6.逆置/反转单链表
实现思路
1、摘结点
2、循环赋值给新链表
代码实现
ListNode* Reverse(ListNode *pList)
{
ListNode* cur = pList;
ListNode* tmp = NULL;
ListNode* newlist = NULL;
while(cur)//摘结点
{
tmp = cur;
cur = cur->next;
tmp->next = newlist;
newlist = tmp;
}
return newlist;
}
7.单链表排序(冒泡排序)
实现思路
1、判断链表是否为空
2、用tail指针控制cur停止位置
3、用两层循环分别实现链表遍历&数据交换
4、用exchange优化程序,使程序可以在一定程度上减少遍历链表的次数(当链表部分有序时)
代码实现
void BubbleSort(ListNode* pList)//单链表排序(冒泡排序)
{
ListNode* tail = pList;
ListNode* next =pList;
ListNode* cur = next->next;
DataType tmp;
int exchange = 0;
while(tail->next)//尾指针-控制终止位置
{
tail = tail->next;
}
//1.链表只有一个元素
if(pList == NULL || pList->next == NULL)
return;
//2.链表有多个元素
else
{
while(tail != pList->next)
{
while(cur != tail)
{
exchange = 0;
if(next->data > cur->data)
{
tmp = next->data;
next->data = cur->data;
cur->data = tmp;
}
cur = cur->next;
next = next->next;
exchange = 1;
}
if(next->data > cur->data)
{
tmp = next->data;
next->data = cur->data;
cur->data = tmp;
}
tail = next;
next = pList;
cur = next->next;
if(exchange = 0)
break;
}
}
}
8.合并两个有序链表,合并后依然有序
实现思路
1、判断两链表是否为空
2、摘取新链表的表头结点
3、比较两链表的大小,合并有序链表
代码实现
ListNode* MergeList(ListNode* List1, ListNode* List2)//合并两个有序链表,合并后依然有序
{
ListNode* List = NULL;
ListNode* tmp = NULL;
//判断链表是否为空
if(List1 == NULL && List2 == NULL)//两表均为空
return NULL;
else if(List1 != NULL || List2 != NULL)//其中一表为空
{
if(List1 == NULL)
return List2;
return List1;
}
else
{
if(List1->data < List2->data)//摘取新链表的表头
{
List = List1;
List1 = List1->next;
}
else
{
List = List2;
List2 = List2->next;
}
tmp = List;
while(List1 && List2)
{
if(List1->data < List2->data)
{
List->next = List1;
List = List->next;
List1 = List1->next;
}
else
{
List->next = List2;
List = List->next;
List2 = List2->next;
}
}
if(List1 == NULL)
List->next = List2;
if(List2 == NULL)
List->next = List1;
return tmp;
}
}
9.查找单链表的中间节点,要求只能遍历一次链表
实现思路
1、因为要求只能遍历一次链表,所以采用快慢指针的方法来进行查找
2、慢指针每次移动一个结点,快指针每次移动两个结点,即两个指针在每次移动时会拉开1的距离
3、到最后,快指针刚刚好是慢指针的两倍,当时,慢指针应正好指向链表的中间结点。
代码实现
ListNode* FindMiddle(ListNode* pList)//查找单链表的中间节点,要求只能遍历一次链表
{
ListNode* fast = pList;
ListNode* slow = pList;
if(pList == NULL)//链表没有元素
return NULL;
else if(pList->next->next == NULL || pList->next == NULL)//链表只有一个or两个元素
return pList;
else//链表有多个元素
{
while(fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
}
10.查找单链表的倒数第k个节点,要求只能遍历一次链表
实现思路
1、先挪快指针k步
2、然后快指针和慢指针一起挪动
3、当fast->next == NULL时,slow即链表中的倒数第k个结点
代码实现
ListNode* FindK(ListNode* pList, int k)//查找单链表的倒数第k个节点,要求只能遍历一次链表
{
ListNode* fast = pList;
ListNode* slow = pList;
if(pList == NULL)//链表没有元素
return NULL;
while(--k)
{
fast = fast->next;
if(fast == NULL)
return NULL;
}
while(fast->next != NULL)
{
fast = fast->next;
slow = slow->next;
}
return slow;
}