本文所有的函数都是在上上一篇单链表和上一篇链表面试题(一)的基础上建立的。单链表、链表面试题(一)
1、单链表排序(冒泡排序)
用三个指针来实现该功能,p1初始化为pHead,p2初始化为pHead->pNext,pEnd初始化为NULL,当p2不等于pEnd时,p1和p2的data进行比较,如果p1的data大于p2的data,p1和p2的值交换,否则p1和p2继续向后访问,pEnd指向pHead->pNext时,循环结束。
优化:创建一个标志位IsChange,如果交换将其置为1,当IsChange在一次循环中没有改变时,说明链表此时已经为有序链表,直接跳出循环。
函数实现源程序
Swap(int *x,int *y)
{
int tmp = *x;
*x = *y;
*y = tmp;
}
//单链表排序(冒泡排序)
void BubbleSort(SList *pHead)
{
SList *p1 = pHead;
SList *p2 = pHead->pNext;
SList *pEnd = NULL;
int IsChange = 0;
while(pEnd != pHead->pNext)
{
IsChange = 0;
p1 = pHead;
p2 = pHead->pNext;
while(p2 != pEnd)
{
if(p1->data > p2->data)
{
Swap(&(p1->data),&(p2->data));
IsChange = 1;
}
p1 = p1->pNext;
p2 = p2->pNext;
}
if(IsChange == 0)
break;
pEnd = p1;
}
}
测试程序
void TestBubbleSort()
{
SList *Head = NULL;
Init(&Head);
PushFront(&Head,1);
PushFront(&Head,2);
PushFront(&Head,3);
PushFront(&Head,4);
PushBack(&Head,11);
PushBack(&Head,12);
PushBack(&Head,13);
PushBack(&Head,14);
Print(Head);
//单链表排序(冒泡排序)
BubbleSort(Head);
Print(Head);
Destory(&Head);
}
执行结果:
2、合并两个有序链表,合并后依然有序
用两个指针分别指向两个链表,哪个小,就将哪个尾插到新的链表中,并向后走接着比较,当其中有一个链表结束,那么剩下的那个链表只需依次尾插到新链表即可。
函数实现源程序
//合并两个有序链表,合并后依然有序
SList *MergeSort(SList *pHead1,SList *pHead2)
{
SList *pNode1 = pHead1;
SList *pNode2 = pHead2;
SList *pNode = NULL;
SList *pResult = NULL;
SList *pRemain = NULL;
while(pNode1 != NULL && pNode2 != NULL)
{
if(pNode1->data > pNode2->data)
{
PushBack(&pResult,pNode2->data);
pNode2 = pNode2->pNext;
}
else
{
PushBack(&pResult,pNode1->data);
pNode1 = pNode1->pNext;
}
}
if(pNode1 == NULL)
pRemain = pNode2;
else
pRemain = pNode1;
for(pNode=pRemain; pNode!=NULL; pNode=pNode->pNext)
{
PushBack(&pResult,pNode->data);
}
return pResult;
}
测试程序
void TestMergeSort()
{
SList *Head1 = NULL;
SList *Head2 = NULL;
Init(&Head1);
Init(&Head2);
PushBack(&Head1,1);
PushBack(&Head1,3);
PushBack(&Head1,4);
PushBack(&Head1,6);
PushBack(&Head1,9);
Print(Head1);
PushBack(&Head2,0);
PushBack(&Head2,1);
PushBack(&Head2,2);
PushBack(&Head2,4);
PushBack(&Head2,5);
PushBack(&Head2,9);
PushBack(&Head2,11);
Print(Head2);
Print(MergeSort(Head1,Head2));//合并两个有序链表,合并后依然有序
Destory(&Head2);
Destory(&Head1);
}
执行结果:
3、求两个已排序单链表中相同的数据
思路和第二题相同,用两个指针来遍历两个链表,当两个链表的数据相同时,将其尾插到新链表。
函数实现源程序
//求两个已排序单链表中相同的数据
SList *UnionSet(SList *pHead1,SList *pHead2)
{
SList *pNode1 = pHead1;
SList *pNode2 = pHead2;
SList *pNode = NULL;
SList *pResult = NULL;
while(pNode1 != NULL && pNode2 != NULL)
{
if(pNode1->data > pNode2->data)//如果pNode1->data大于pNode2->data,pNode2向后走
{
pNode2 = pNode2->pNext;
}
else if(pNode1->data < pNode2->data)//如果pNode1->data小于pNode2->data,pNode1向后走
{
pNode1 = pNode1->pNext;
}
else//如果pNode1->data等于pNode2->data,将该数尾插到pResult,pNode1和pNode2同时向后走
{
PushBack(&pResult,pNode1->data);
pNode1 = pNode1->pNext;
pNode2 = pNode2->pNext;
}
}
return pResult;
}
测试程序
void TestUnionSet()
{
SList *Head1 = NULL;
SList *Head2 = NULL;
Init(&Head1);
Init(&Head2);
PushBack(&Head1,1);
PushBack(&Head1,3);
PushBack(&Head1,4);
PushBack(&Head1,6);
PushBack(&Head1,9);
Print(Head1);
PushBack(&Head2,0);
PushBack(&Head2,1);
PushBack(&Head2,2);
PushBack(&Head2,4);
PushBack(&Head2,5);
PushBack(&Head2,9);
PushBack(&Head2,11);
Print(Head2);
Print(UnionSet(Head1,Head2));//求两个已排序单链表中相同的数据
Destory(&Head2);
Destory(&Head1);
}
执行结果:
4、查找链表的中间节点,要求只能遍历一次链表
使用两个指针pSlow和pFast,pFast每次走两步,pSlow每次走一步,当pFast走完的时候,pSlow刚好走到链表的中间节点。
函数实现源程序
//查找链表的中间节点,要求只能遍历一次链表
SList *FindMiddle(SList *pHead)
{
SList *pSlow = pHead;
SList *pFast = pHead;
assert(pHead != NULL);
while(pFast != NULL)
{
pFast = pFast->pNext;
if(pFast != NULL)
{
pFast = pFast->pNext;
}
else
{
break;
}
pSlow = pSlow->pNext;
}
return pSlow;
}
测试程序
void TestFindMiddle()
{
SList *Head1 = NULL;
Init(&Head1);
PushBack(&Head1,1);
PushBack(&Head1,3);
PushBack(&Head1,4);
PushBack(&Head1,6);
PushBack(&Head1,9);
Print(Head1);
printf("%d\n",FindMiddle(Head1)->data);//查找中间节点
Destory(&Head1);
}
执行结果:
5、查找单链表的倒数第K个节点,要求只能遍历一次链表
和第4题相同,仍然采用快慢指针的方法,要找倒数第K个节点,就让快指针先走K步,当快指针走完链表时,慢指针指向链表的倒数第K个节点。
函数实现源程序
//查找单链表的倒数第K个节点,要求只能遍历一次链表
SList *FindKNode(SList *pHead,int k)
{
SList *pSlow = pHead;
SList *pFast = pHead;
int i = 0;
for(i=0; i<k; i++)//快指针先走k步
{
pFast = pFast->pNext;
}
while(pFast != NULL)
{
pSlow = pSlow->pNext;
pFast = pFast->pNext;
}
return pSlow;
}
测试程序
void TestFindKNode()
{
SList *Head1 = NULL;
Init(&Head1);
PushBack(&Head1,1);
PushBack(&Head1,3);
PushBack(&Head1,4);
PushBack(&Head1,6);
PushBack(&Head1,9);
Print(Head1);
printf("%d\n",FindKNode(Head1,2)->data);//查找倒数第k个节点
Destory(&Head1);
}
执行结果:(找倒数第二个)
6、删除链表的倒数第K个
函数实现源程序
//删除链表的倒数第K个
void DeleteKNode(SList **ppHead,int k)
{
SList *pPos = FindKNode(*ppHead,k);
SList *pNode = *ppHead;
if(pPos->pNext == NULL)//如果是尾节点就尾删
{
PopBack(ppHead);
}
else//否则删除一个非尾结点
{
DelNoTailNode(pPos);
}
}
测试程序
void TestDeleteKNode()
{
SList *Head1 = NULL;
Init(&Head1);
PushBack(&Head1,1);
PushBack(&Head1,3);
PushBack(&Head1,4);
PushBack(&Head1,6);
PushBack(&Head1,9);
Print(Head1);
DeleteKNode(&Head1,2);//删除倒数第k个节点
Print(Head1);
Destory(&Head1);
}