单链表插入元素 注释 c语言,数据结构之无头单链表的相关练习题——C语言实现(详细注释)...

本文中所用到的相关链表操作实现均在我上篇博客中:https://blog..net/haoziai905/article/details/87099287

1.删除无头单链表的非尾结点

这道题的重点就在于最后的非尾结点上,既然是非尾结点,则说明其下一个结点必定不为空。而我们通常所使用的删除节点的方法都需要知道所要删除节点的前一个结点,但是要找到链表中一个结点的前一个结点并容易,只能通过遍历链表去寻找,但是遍历链表的代价是非常大的。所以我们需要一个方法,让我们不需要遍历链表就能删除这个结点。

首先,我们需要明白删除一个结点的目的是什么。我们删除一个结点的目的是为了删除这个结点中所储存的数据,而不是一定要删除这个结点才行。所以我们可以通过交换要删除结点与下一个结点的数据,再将下一个结点删除就好了。而不需要去查找要删除结点的上一个结点。

//删除无头单链表的非尾结点

void DelNodeNotTail(pNode pos)

{

assert(pos);

assert(pos->next);

//交换pos结点与pos下一结点的元素

pNode cur = pos->next;

pos->data = cur->data;

pos->next = cur->next;

//删除pos结点的下一个结点

free(cur);

cur = NULL;

}

2.在无头单链表的一个位置前插入一个元素

这个题与上一个题类似,插入元素也是需要知道要插入位置的前一个结点才行,但是遍历链表的代价太大。所以我们也可以通过上一题交换结点中元素的方法来实现,在该位置后插入一个新结点,然后交换两个结点中的元素即可。

//在无头单链表的一个结点前插入一个结点

void InsertNode(pNode pos, DataType data)

{

assert(pos);

pNode cur = pos->next;//保存pos的下一结点位置

DataType tmp = pos->data;//保存pos结点的元素

//在pos位置后插入一个新结点,并交换pos结点与新结点的元素

pos->next = BuyNode(data);

pos->data = data;

pos->next->data = tmp;

//将链表重新连接起来

pos->next->next = cur;

}

3.约瑟夫环

约瑟夫环是什么呢?约瑟夫环(约瑟夫问题)是一个数学的应用问题:已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。我们需要通过代码来模拟约瑟夫环的过程,以此来知道谁会留到最后。

//约瑟夫环

void JosephusCycle(pList* pplist, int k)

{

pNode cur = *pplist;

pNode del = NULL;

//当cur==cur->next时说明只剩下一个人了

while (cur != cur->next)

{

for (int i = 1; i < k; i++)

{

cur = cur->next;

}

printf("The %d is die!\n", cur->data);

del = cur->next->next;//保存cur的下下一个结点

cur->data = cur->next->data;//交换cur结点和下一结点的元素

free(cur->next);//删除cur的下一结点

cur->next = del;//连接起链表

del = NULL;

}

*pplist = cur;

printf("The %d is live!\n", cur->data);

}

4.逆序单链表

要想将单链表逆序有两种方法,第一个方法就是通过三个指针实现,还有一种方法就是通过头插来实现。当我们将一个链表的结点从头到尾一个一个的头插到另一个新链表上时,这时新产生的链表就是第一条链表逆序后的样子。

//逆序链表:三指针法

void ReverseList(pList* pplist)

{

assert(pplist);

//当链表为空链表或者链表中只有一个元素时,不需要逆序

if ((*pplist) == NULL || (*pplist)->next == NULL)

{

return;

}

pNode frist = *pplist;

pNode second = frist->next;

pNode third = second->next;

while (second != NULL)

{

second->next = frist;

frist = second;

second = third;

if (third != NULL)

{

third = third->next;

}

}

(*pplist)->next = NULL;

*pplist = frist;

}

//逆序链表:头插法

void ReverseList(pList* pplist)

{

assert(pplist);

if ((*pplist) == NULL || ((*pplist)->next == NULL))

{

return;

}

pList pHead = NULL;

pNode cur = *pplist;

pNode tmp = cur->next;

while (cur)

{

//头插

if (pHead == NULL)

{

pHead = cur;

}

else

{

cur->next = pHead;

pHead = cur;

cur = tmp;

if (tmp)

{

tmp = tmp->next;

}

}

}

(*pplist)->next = NULL;

*pplist = pHead;

}

5.冒泡排序

冒泡排序时最简单的一种排序方法,实现起来也并不复杂,但是我们通常所实现的冒泡排序都是通过数组或者是顺序来实现的,那么在链表中我们应该如何实现冒泡排序呢?

//冒泡排序

void BubbleSort(pList plist)

{

if (NULL == plist)

{

return;

}

pNode tail = NULL;

while (tail != plist)

{

pNode cur = plist;

pNode next = cur->next;

int flag = 0;//设置标志位,提高排序效率

while (next != tail)

{

//升序排列

if (next->data < cur->data)

{

DataType tmp = next->data;

next->data = cur->data;

cur->data = tmp;

flag = 1;//如果单趟排序中发生了交换则将标志位置1

}

else

{

cur = next;

next = next->next;

}

}

//标志位为0则说明为发生元素交换,则剩下的元素已经有序,不需要排序,直接返回

if (flag == 0)

{

return;

}

tail = cur;

}

}

6.合并两个有序链表,合并后依然有序

只需要一一对比两个链表中元素的大小,然后按照我们所想要的顺序将大的元素或者小的元素尾插入新链表中即可。

//合并两个有序链表合并后仍然有序

pList Merge(pList plist1, pList plist2)

{

//1.两个链表是一条链表

//2.两条链表中有一条为空

//出现上面两种情况都不需要合并链表

if (plist1 == plist2)

{

return plist1;

}

if (plist1 == NULL)

{

return plist2;

}

if (plist2 == NULL)

{

return plist1;

}

pList newlist = NULL;

pList cur = NULL;

while (plist1 != NULL && plist2 != NULL)

{

//比较两条链表当前第一个结点元素的大小

//升序排列则将元素较小的结点接在新链表后

if (plist1->data < plist2->data)

{

if (newlist == NULL)

{

newlist = plist1;

cur = newlist;

}

else

{

cur->next = plist1;

cur = cur->next;

}

plist1 = plist1->next;

}

else

{

if (newlist == NULL)

{

newlist = plist2;

cur = newlist;

}

else

{

cur->next = plist2;

cur = cur->next;

}

plist2 = plist2->next;

}

}

//当其中一个链表已经为空时,直接将另一条链表的整条链表接在新链表后即可

if (plist1 == NULL && plist2 != NULL)

{

cur->next = plist2;

}

else if (plist1 != NULL && plist2 == NULL)

{

cur->next = plist1;

}

return newlist;

}

7.只遍历链表一遍,找到中间结点

//只遍历链表一遍,找到中间结点

pNode FindMidNode(pList plist)

{

//快慢指针法,快指针速度是慢指针速度的两倍

//当快指针走到链表尾时,则慢指针正好走到链表的中间位置

pNode fast = plist;

pNode slow = plist;

while (fast != NULL && fast->next != NULL)

{

slow = slow->next;

fast = fast->next->next;

}

return slow;

}

8.只遍历一遍找到链表的倒数第K个结点

//只遍历一遍找到链表的倒数第K个结点

pNode FindLastKNode(pList plist, int k)

{

if (plist == NULL)

{

return NULL;

}

int count = k;

pNode fast = plist;

pNode slow = plist;

//快指针先走K步

while (count--)

{

//如果快指针已经为空则说明链表中不足K个元素

if (fast == NULL)

{

return NULL;

}

fast = fast->next;

}

//快慢指针一起走,当快指针为空时,慢指针指的就是倒数第K个元素

while (fast != NULL)

{

fast = fast->next;

slow = slow->next;

}

return slow;

}

以上代码均通过VS2017环境测试。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值