下面的题中要用到的函数以及数据类型:
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int DataType;
typedef struct SLinkList
{
DataType data;
struct SLinkList*PNext;
}SNode;
// 初始化
void SLinkList_InIt(SNode **Phead)
{
assert(Phead != NULL);
*Phead = NULL;
}
1.合并两个有序链表,合并后依然有序
思路:
- 两个指针分别指向两个有序链表
- 比较两个指针指向的节点的数字的大小,将较小的拿出来放到新的链表中,
- 更新拿出来数据的那个指针,另外一个不动
- 将剩下的节点链接到新的链表后面
/*
1. 两个指针分别指向两个有序链表
2.比较两个指针指向的节点的数字的大小,将较小的拿出来放到新的链表中,
3.更新拿出来数据的那个指针,另外一个不动
4.当其中一个链表指向NULL时,停止循环判断是否有剩下的链表,如果有将剩下的节点链接到新的链表后面
5.返回新的链表
*/
//创建一个新结点
static SNode* BuySListNode(DataType data)
{
SNode *pNewNode = (SNode*)malloc(sizeof(SNode));
if (pNewNode == NULL)
{
assert(0);
return NULL;
}
pNewNode->data = data;
pNewNode->PNext = NULL;
return pNewNode;
}
void PushBack(SNode **Phead, DataType data)//后插
{
assert(Phead != NULL);
if (*Phead == NULL)
{
*Phead = BuySListNode(data);
return;
}
else
{
SNode*ptmp = *Phead;
while (ptmp->PNext)
{
ptmp = ptmp->PNext;
}
ptmp->PNext = BuySListNode(data);
}
}
SNode*MergeSList(SNode*Pfirst1, SNode* Pfirst2)//合并函数
{
SNode*PL1 = Pfirst1;
SNode*PL2 = Pfirst2;
SNode*PNewNode = NULL;
SNode*RetNode = NULL;
SNode*Pnode = NULL;
while (PL1 != NULL&&PL2 != NULL)//比较两个链表中的数据,如果任何一个链表指向NULL结束循环
{
if (PL1->data < PL2->data)
{
PushBack(&PNewNode, PL1->data);
PL1 = PL1->PNext;
}
else
{
PushBack(&PNewNode, PL2->data);
PL2 = PL2->PNext;
}
}
if (PL1 == NULL)//判断链表是否有剩余
{
RetNode = PL2;
}
else
{
RetNode = PL1;
}
for (Pnode = RetNode; Pnode != NULL; Pnode = Pnode->PNext)//将剩余的链表中的数据尾插到新的链表中
{
PushBack(&PNewNode, Pnode->data);
}
return PNewNode;
}
原理图:
2.查找单链表的中间节点,要求只能遍历一次链表
思路:
- 快慢指针,快指针一次向后走两步,慢指针一次走一步,慢指针最后所停的位置就是中间节点的位置
- 当快指针指向空或者PFast->PNext == NULL或者PFast->PNext->PNext == NULL,它们三个条件任意一个成立的时候退出循环
//查找链表的中间节点,要求只能遍历一次链表(快慢指针的思想)
SNode* FindMiddleNode(SNode*Phead)
{
assert(Phead != NULL);
if (Phead == NULL)
{
return NULL;
}
SNode*PFast = Phead;
SNode*PSlow = Phead;
while (PFast != NULL&&PFast->PNext != NULL&&PFast->PNext->PNext != NULL)
{
PFast = PFast->PNext->PNext;
PSlow = PSlow->PNext;
}
return PSlow;
}
原理图:
3.查找单链表的倒数第K个节点,要求只能遍历一次链表
这个题与上一题有异曲同工之妙,都是快慢指针的思想。
思路:
- 快慢指针的思想
- 先让快指针往前走k-1步
- 再同时让快指针和慢指针往后走
- 当快指针指向NULL的时候这时候慢指针指向的节点就是要找的倒数第K个节点
SNode* FindLastKNode(SNode*Phead, int k)
{
assert(Phead != NULL);
if (Phead == NULL || k <= 0)
{
return NULL;
}
SNode*PFast = Phead;
SNode*PSlow = Phead;
int i = 0;
for (i = 0; i < k - 1; i++)//这里PFsat先往前走k-1步
{
PFast = PFast->PNext;
}
while (PFast->PNext != NULL)
{
PFast = PFast->PNext;
PSlow = PSlow->PNext;
}
return PSlow;
}
原理图:
4.求两个已排序链表中相同的数据。
思路:
- 两个指针分别指向两个有序链表
- 比较两个指针指向的节点的数字的大小,哪个小就让哪个往前走一步,
- 如果相等的话将数值尾插到新的链表中,然后两个指针同时往后走一步
- 当两个链表任意一个指向NULL时,停止循环
- 返回新的链表
static SNode* BuySListNode(DataType data)
{
SNode *pNewNode = (SNode*)malloc(sizeof(SNode));
if (pNewNode == NULL)
{
assert(0);
return NULL;
}
pNewNode->data = data;
pNewNode->PNext = NULL;
return pNewNode;
}
void PushBack(SNode **Phead, DataType data)//后插
{
assert(Phead != NULL);
if (*Phead == NULL)
{
*Phead = BuySListNode(data);
return;
}
else
{
SNode*ptmp = *Phead;
while (ptmp->PNext)
{
ptmp = ptmp->PNext;
}
ptmp->PNext = BuySListNode(data);
}
}
SNode* UnionSet(SNode*Phead1, SNode*Phead2)
{
if (Phead1 == NULL || Phead2 == NULL)
{
return NULL;
}
SNode*PL1 = Phead1;
SNode*PL2 = Phead2;
SNode*PNewNode = NULL;
SNode*Pnode = NULL;
while ((PL1 != NULL) && (PL2 != NULL))
{
if (PL1->data < PL2->data)
{
PL1 = PL1->PNext;
}
else if (PL1->data>PL2->data)
{
PL2 = PL2->PNext;
}
else
{
PushBack(&PNewNode, PL1->data);
PL1 = PL1->PNext;
PL2 = PL2->PNext;
}
}
return PNewNode;
}
原理图: