初学数据结构,链表的操作除了增、删、改、查功能外,链表还有一项比较重要的操作——链表的逆置。于是对其进行了一番简单的研究。
首先,我们来创建一个链表(含头结点)
#include<stdio.h>
#include<stdlib.h>
typedef struct node{
int data;
struct node *next;
}node;
node *creat(int n){
node *head,*p,*q;
int num;
head = (node*)malloc(sizeof(node));
head->next = NULL;
q = head;
while(n--){
scanf("%d",&num);
p = (node*)malloc(sizeof(node));
p->data = num;
q->next = p;
q = p;
}
q->next = NULL;
return head;
}
之后,介绍我们的第一种逆置方法:头插法
所谓头插法,就是使用一个结构体指针向后遍历,用另一个指针将前一个结点插入到头结点指向的下一个位置
//头插
void reverse_head(node *head){
node *p,*q;
p = head->next; //使用 p 依次向后遍历
head->next = NULL;
while(p){
//前一负责指向插入结点的指针后移
//在第二次 while 循环时看这个操作更清楚
q = p;
//向后遍历
p = p->next;
//将 q 结点插入到头结点后面
q->next = head->next;
head->next = q;
}
return;
}
现在想想,这个命名也是十分恰当了。
第二种方法是:就地逆置法
与头插法十分类似,只不过是从是头结点后的第一个有效结点开始操作,完成后打印时,是从表尾开始打印
//就地逆置
//这里传入的是头结点的下一个有效结点
node *reverse_another(node *head){
if(head == NULL || head->next == NULL) //判定返回
return head;
node *rev,*t,*cur;
rev = NULL;
cur = head;
while(cur != NULL){
//指针后移
t = cur;
//向后遍历
cur = cur->next;
//与头插法类似
t->next = rev;
rev = t;
}
return rev;
}
下面是测试用的主函数
int main(){
node *head;
int n;
printf("请输入数字个数:");
scanf("%d",&n);
head = creat(n);
print(head);
//头插输出
reverse_head(head);
//就地逆置输出,注意传入的是头结点后的第一个有效结点
//head->next = reverse_another(head->next);
print(head);
return 0;
}
本来是要自己画图,但是发现有大佬画的十分清楚的图,便引用一下(懒了):
感谢大佬!!!!!
图片来源:https://blog.csdn.net/qq_42322103/article/details/82668765
图解:
刚开始是这样:
循环前的操作
进入循环,分别用q和p记录第一个和第二个节点
进入第二轮循环,这是发生重大变化的关键时期
这张图调整一下
直到链表为空