本文所有的函数都是在上一篇单链表的基础上建立的。有兴趣的可以了解一下:https://blog.csdn.net/iconm/article/details/80541643
1、从尾到头打印链表
分析:如下图所示,单链表为1、2、3、4、NULL,题目要求从尾到头打印,即打印的效果为4、3、2、1、NULL。我们可以用递归和循环来实现这一功能。
- 递归:递归的结束条件为pNode为空,代码比较简单。
- 循环:可以定义两个指针,一个用来遍历链表,一个用来结束循环。如图所示,用pNode来遍历链表,让pNode从链表的第一个开始遍历,先将pEnd置为NULL,当pNode->pNext指向pEnd时一次遍历结束,将pEnd赋为pNode,直到pEnd为链表的第一个元素为止。
函数实现源程序
//从尾到头打印单链表,递归实现
void ReverseRecPrint(SList *pHead)
{
SList *pNode = pHead;
if(pNode == NULL)
{
printf("NULL");
return;
}
ReverseRecPrint(pNode->pNext);
printf(" <- %d",pNode->data);
}
//从尾到头打印单链表,循环实现
void ReversePrint(SList *pHead)
{
SList *pNode = pHead;
SList *pEnd = NULL;
if(pNode == NULL)
{
printf("NULL\n");
return;
}
printf("NULL");
while(pEnd != pHead)
{
for(pNode=pHead; pNode->pNext!=pEnd; pNode=pNode->pNext)
{
;
}
printf(" <- %d",pNode->data);
pEnd = pNode;
}
}
测试程序
void TestReversePrint()
{
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);
//从尾到头打印链表
printf("原链表:\n");
Print(Head);
printf("从尾到头打印链表(递归):\n");
ReverseRecPrint(Head);
printf("\n");
printf("从尾到头打印链表(循环):\n");
ReversePrint(Head);
printf("\n");
Destory(&Head);
}
执行结果:
2、逆置/反转单链表
- 和第一题类似,可以新建一个链表,利用头删、头插来实现。(注:头删时伪删除,以防找不到数据)
- 也可以通过pNext改箭头的方向,给三个指针p1、p2、p3,p1初始化为NULL,p2初始化为链表的第一个元素,p3初始化为p2的pNext,然后让p2的pNext指向p1,再将三个指针依次往后挪,直到p2为NULL为止。
函数实现源程序
//逆置/反转单链表
SList *ReverseList(SList *pHead)
{
SList *pNode = pHead;
SList *pNewHead = NULL;
SList *pNext = NULL;
assert(pNode != NULL);
//头删、头插
while(pNode != NULL)
{
pNext = pNode->pNext;
PushFront(&pNewHead,pNode->data);
pNode = pNext;
}
return pNewHead;
}
//逆置/反转单链表(循环)
SList *ReverseListLoop(SList *pHead)
{
SList *p1 = NULL;
SList *p2 = pHead;
SList *p3 = p2->pNext;
while(p2 != NULL)
{
p2->pNext = p1;
p1 = p2;
p2 = p3;
if(p2 != NULL)
{
p3 = p2->pNext;
}
}
return p1;
}
测试程序
void TestReverseList()
{
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);
//逆置/反转单链表
Print(ReverseList(Head));
Print(ReverseListLoop(Head));
Destory(&Head);
}
执行结果:
3、删除一个无头单链表的非尾节点(不能遍历链表)
题目要求不能遍历链表,可以采用伪删除的方法,删除要删节点后面的那个节点,将这个节点的数据保存到要删除的节点,就能实现其功能,即旧瓶装新酒的思想。(测试时可通过Find函数返回要删除节点的地址。)
函数实现源程序
//删除一个无头单链表的非尾节点(不能遍历链表)
void DelNoTailNode(SList *pPos)
{
SList *pNext = pPos->pNext;
assert(pPos != NULL);
assert(pPos->pNext != NULL);
pPos->data = pNext->data;
pPos->pNext = pNext->pNext;
free(pNext);
pNext = NULL;
}
测试程序
void TestDelNoTailNode()
{
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);
//删除一个无头单链表的非尾节点
DelNoTailNode(Find(Head,1));
Print(Head);
Destory(&Head);
}
执行结果:(删除1)
4、在无头单链表的一个节点前插入一个节点(不能遍历链表)
和第三题的思路一样,旧瓶装新酒。
函数实现源程序
//在无头单链表的一个节点前插入一个节点(不能遍历链表)
void Insert(SList *pPos,DataType data)
{
SList *pNewNode = BuyNewNode(pPos->data);
assert(pPos != NULL);
if(pPos == NULL)
{
PushFront(&pPos,data);
return;
}
pNewNode->pNext = pPos->pNext;
pPos->data = data;
pPos->pNext = pNewNode;
}
测试程序
void TestInsert()
{
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);
//在无头单链表的一个节点前插入一个节点(不能遍历链表)
Insert(Find(Head,1),0);
Print(Head);
Destory(&Head);
}
执行结果:(在1的前面插入0)
5、单链表实现约瑟夫环(JosephCircle)
先将单链表连成环,开始报数,报到k时将其释放掉,注意释放第k个,循环k-1次。为了防止丢失,一个指针来遍历,一个用来保存前一个,释放前应将链表再连成环,即将pPrev的pNext指向pNode的pNext。(测试时不要使用销毁函数)
函数实现源程序
int JosephCircle(SList *pHead,int k)
{
SList *pNode = pHead;
SList *pPrev = NULL;
int i = 0;
assert(pHead != NULL);
while(pNode->pNext != NULL)
{
pNode = pNode->pNext;
}
pNode->pNext = pHead;//连成环
pNode = pHead;
while(pNode->pNext != pNode)//只剩一个
{
for(i=0;i<k-1;i++)//报数
{
pPrev = pNode;
pNode = pNode->pNext;
}
pPrev->pNext = pNode->pNext;
free(pNode);//KILL
pNode = pPrev->pNext;//指向原来的下下一个,即现在的下一个
}
return pNode->data;
}
测试程序
void TestJosephCircle()
{
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);
//单链表实现约瑟夫环(JosephCircle)
printf("%d\n", JosephCircle(Head,3));
}
运行结果: