最近看了很多对于链表的操作,对链表的操作无非就是对指针的操作,因此,经常会使人晕头转账,今天跟大家分享一个逻辑不是很复杂,但是对指针的操作较为复杂的一个例子----倒置链表。
顾名思义,倒置链表就是将一个链表里的数据颠倒过来,使得原来的头成为尾部,原来的尾部变成第一个节点。当然,一个简单的方法,可以每次都取得当前链表的最后一个节点,将其前插到新生成的链表的第一个节点的位置,再将该节点释放,最后把新生成的链表的头结点首地址赋给原先链表的头结点首地址。但是,这样做不管是时间复杂度还是空间复杂度,都是比较高的,而如果可以只更改链表里的每个节点的next成员的指向,效率则会大大提高。
首先我们分析它的手工过程:我们首先要明确链表里的数值遍历的原理以及各个节点的指向关系。我们用一幅图来清晰地表示。
完成倒置链表有以下几点需要注意:
1、我们现在要做的就是使head的值变成#5,#1中next成员的值变为NULL,#2中next成员的值变为#1,#3中next成员的值变为#2... ...同时还要满足能够遍历整个链表,依次更改每一个节点中next的值,不能出现更改后无法找到下一个节点的情况。比如#2的next成员更改为了#1,当希望继续找到#3时,原来next空间中存储的#3的地址值已经被覆盖为#1了,因此就不能继续修改#3中next成员的值了,而且还会造成内存泄漏。
2、我们发现需要将当前节点的next成员修改为当前节点的前一个节点的首地址,这就要求我们每次修改完能够将当前节点的首地址存储起来。而当前节点的首地址又是它的前驱节点的next成员没有更改时的值。
3、第一个节点和最后一个节点情况较为特殊,因为第一个节点的next成员被更改为NULL,而末节点的首地址又应该赋值给head。
知道了以上三点我们就可以开始编程了:
1、我们需要一个变量now用来遍历链表,而更改这个变量的时机必须在我们修改当前节点的next成员之前。
2、需要一个用来存储当前节点的前驱节点的地址的变量preNode。
3、每次应先将preNode的值赋值给now的next成员,再将now的值赋值给preNode。
4、preNode的初值应为NULL,而不应该是#1,因为我们遍历肯定是从第一个节点开始遍历,第一个节点的前一个节点当然应该是空,这样做,一是符合语义性,二是根据我们之前所说的操作“每次将preNode的值赋值给now的next成员”,可以不用单独处理第一个节点,就能够保证第一个节点的next成员的值为NULL。
5、在最后,我们应该将now的值赋值给head,因为循环结束时,now的值一定是原先链表的最后一个节点。
具体代码如下:
#include <stdio.h>
#include <malloc.h>
typedef struct NODE{
int data;
struct NODE *next;
}NODE;
void initLine(NODE **head);
void destoryLine(NODE **head);
void showLine(NODE *head);
void upSideDownLine(NODE **head);
void upSideDownLine(NODE **head) {
NODE *now = *head;
NODE *preNode = NULL;
NODE *temp;
while (now != NULL) {
temp = now->next;
now->next = preNode;
preNode = now;
now = temp;
}
*head = preNode;
}
void showLine(NODE *head) {
NODE *p;
printf("\n");
for (p = head; p != NULL; p = p->next) {
printf("%d ", p->data);
}
printf("\n");
}
void destoryLine(NODE **head) {
NODE *p;
if (*head == NULL) {
return;
}
while(*head != NULL) {
p = *head;
*head = p->next;
free(p);
}
}
void initLine(NODE **head) {
int num;
NODE *p = *head;
if (*head != NULL) {
return;
}
printf("请输入一个数(-1表示结束输入):");
scanf_s("%d", &num);
while (num != -1) {
if (*head == NULL) {
*head = (NODE *)calloc(1, sizeof(NODE));
(*head)->data = num;
p = *head;
}
else {
p->next = (NODE *)calloc(1, sizeof(NODE));
p->next->data = num;
p = p->next;
}
printf("请输入一个数(-1表示结束):");
scanf_s("%d", &num);
}
}
void main(void) {
NODE *head = NULL;
initLine(&head);
showLine(head);
upSideDownLine(&head);
showLine(head);
destoryLine(&head);
fflush(stdin);
getchar();
}