第一次写文章,为了大三算法课挣平时分哈哈哈
以后会不定时更新每一次的算法作业或者平时的一些学习心得~
这是第一个作业的第一题:
对于一个不带头结点的单链表L,设计一个递归算法逆置所有结点。编写完整的程序并进行测试,分析其时间复杂度。
但是看到这道题,王道书的课后题写过类似的题,但是忘得精光,在网上看了一些代码,终于又会了。不得不说,一辈子痛恨递归!!!对于我这种强迫症来说很不友好。暂时立个小flag,下面一段时间,每天练一道递归算法题,而且都要算时间复杂度和空间复杂度,进行“刻意训练”,重复重复再重复(希望这个小flag不要打水漂呜呜呜)
回到正题~~~
思路:
递归函数f(L)返回值p:指向逆置函数的首结点的指针,也就是指向原单链表的尾结点的指针;
![](https://img-blog.csdnimg.cn/img_convert/53bb8d36e4d1ffbbc11e77ee3cac4a8f.jpeg)
递归主体:将f(L)中L所指向的元素链接到逆置链表的末尾。这里会发现每次L所指向的元素本身在原链表中指向了逆置链表的尾元素,所以递归主体的操作只需要对L和L->next进行操作。
![](https://img-blog.csdnimg.cn/img_convert/9b602d6887f8a3392630d20d378c724b.jpeg)
每一步都画出来了(递归真的对强迫星人不太友好~~)
L->next->next = L;//这里的L从指向倒数第二个元素开始,每一次出递归,L就往前指一个
L->next = NULL;
同时要注意到,函数的返回值p应该是不变的。这一点会在后面的单步调试中体现。
下面是代码,代码细节部分见注释:
#include<iostream>
#include <stdlib.h>
using namespace std;
typedef struct LNode
{
int data;
struct LNode* next;
}LNode,*Linklist;
//递归函数作用:找到尾结点作为逆置链表的首结点,并将L所指向的结点链接到逆置链表的尾部
LNode * reverse(LNode *L)
{
LNode * p;//P是逆置后单链表的首结点(永远指向原单链表的最后一个结点)
if (L == NULL || L->next == NULL)//若整个单链表为空(没有递归) 或者 递归到最后一个元素(递归出口)
return L;//只在L->next为空时,将指向最后一个元素的指针L返回去 (只执行一次)
p = reverse(L->next);//递归来源:L->next,作为下一次Reverse()的参数
//以下两步:将L所指向的元素链接到逆置链表的表尾
L->next->next = L;//这里的L从指向倒数第二个元素开始,每一次出递归,L就往前指一个
L->next = NULL;
return p;//把指向最后一个元素的指针传递出去(作为逆置链表的首指针)
}
int main()
{
Linklist L;
LNode* q;
L = (LNode*)malloc(sizeof(LNode));
L->next = NULL;
q = L;
int n;
cout << "请输入链表结点数:";
cin >> n;
cout << "请输入链表:" ;
//不带头结点的单链表创建方法:先创建一个带头结点的单链表,再让头指针指向首结点
for (int i = 0;i < n;i++)
{
LNode* p;
p = (LNode*)malloc(sizeof(LNode));
cin >> p->data;
q->next = p;
q = p;
q->next = NULL;
}
L = L->next;
L = reverse(L);
cout << "链表逆置后:";
while (L != NULL)
{
cout << L->data << " ";
L = L->next;
}
cout << endl;
return 0;
}
下面是Debug步骤:
首先,在递归函数Reverse()上打上断点(今晚刚学会的打断点Debug...哎~之前我在干嘛。。。),添加查看L和p变量。
![](https://img-blog.csdnimg.cn/img_convert/0e499686830a017464cb65b9e58f1aec.png)
在运行窗口输入如下:
![](https://img-blog.csdnimg.cn/img_convert/4c3ce174148bafbc5f45de35d3324ff7.png)
刚开始,原链表的首结点的地址为0x1718b0,p是系统赋的随机值
![](https://img-blog.csdnimg.cn/img_convert/e55524683a06ea8f90a303686ee77962.png)
接着,第二次进入递归函数,指向第二个元素的指针地址为0x174070,p依旧是系统赋的随机值
![](https://img-blog.csdnimg.cn/img_convert/9c2cb9b27fb0d3d5512d2586e5dbe702.png)
最后一次,由于L->next==NULL了,满足递归终止条件,最后一个元素的指针地址为0z1740b0,返回给p指针,并一直传递到最后。
![](https://img-blog.csdnimg.cn/img_convert/34e288841bfe1259f82d921eca1fcc66.png)
下面,你会发现L指针的值从最后一个元素的指针地址往回变,同时p指针的值一直都是最后一个元素的指针地址。
![](https://img-blog.csdnimg.cn/img_convert/cef8f811a7641f8dd0683c7d4d7612f3.png)
![](https://img-blog.csdnimg.cn/img_convert/fec07a31b02e37c0a9bde6c49915a6f9.png)
最后递归结束,继续下面的输出:
![](https://img-blog.csdnimg.cn/img_convert/508c7a4b132dbbf95ffa5d9c99c780e7.png)
运行结果:
![](https://img-blog.csdnimg.cn/img_convert/98843ee400db0ab83d419760ab79ab4a.png)
时间复杂度分析:(不确定对不对)
由于逆置函数中的递归主体的基本操作为“L->next->next = L;L->next = NULL;”,递归来源是L->next,由单链表中的结点个数n决定,所以整个算法的时间复杂度为O(n)
第一次写博客文章,可能排版、语言组织不是很好,以后一定会多加锻炼~~多多包函~~有不同的想法可以在评论区留言交流~
(写于2023-3-12 晚9:16)