您的位置:首页 > 其它

链表常见操作:逆置(反转) .

2014-05-27 20:35 239 查看
链表中的一个很常见的操作是:链表的逆置,也叫链表的反转。

如:1->3->5->7->9 反转后是 9->7->5->3->1

方法一:使用指针



红色的箭头是新的变换,明白了操作原理就很好写代码了。

使用了三个指针:pre(前驱) cur(当前) rear(后继),经过以上的四步变换,目地是,使cur指向的节点成功逆置(反转)指向pre所指向的节点。后面的节点的逆置,是同样的。

代码是:

[cpp]
view plaincopyprint?





void reverse(Node *&head)
{
if (head == NULL || head->next==NULL) //空或者只有一个元素不用逆置
return;
Node *pre, *cur, *rear;
pre = head;
cur = head->next;
while (cur)
{
rear = cur->next;
cur->next = pre;
pre = cur;
cur = rear;
}
//以下两步,很重要
head->next = NULL; //这一步会使新的尾节点的链域置空
head = pre; //head指针指向新的一头
}

void reverse(Node *&head)
{
	if (head == NULL || head->next==NULL)  //空或者只有一个元素不用逆置
		return;
	Node *pre, *cur, *rear;
	pre = head;
	cur = head->next;
	while (cur)
	{
		rear = cur->next;
		cur->next = pre;
		pre = cur;
		cur = rear;
	}
	//以下两步,很重要
	head->next = NULL;   //这一步会使新的尾节点的链域置空
	head = pre;   //head指针指向新的一头
}


写一个完整的测试用例

[cpp]
view plaincopyprint?





#include<stdio.h>
#include<stdlib.h>
typedef struct node //节点类型定义
{
int data;
struct node *next;
}Node;
void printlist(Node *head) //打印链表
{
Node *p = head;
while (p->next)
{
printf("%-4d->", p->data);
p = p->next;
}
printf("%-4d\n", p->data);
}
void reverse(Node *&head) //逆置
{
if (head == NULL || head->next == NULL) //空或者只有一个元素不用逆置
return;
Node *pre, *cur, *rear;
pre = head;
cur = head->next;
while (cur)
{
rear = cur->next;
cur->next = pre;
pre = cur;
cur = rear;
}
//以下两步,很重要
head->next = NULL;
head = pre;
}
int main()
{
Node p9={ 9, NULL };
Node p7={ 7, &p9 };
Node p5={ 5, &p7 };
Node p3={ 3, &p5 };
Node p1={ 1, &p3 };
Node *head = &p1;
printf("原表是\n");
printlist(head);
printf("逆置\n");
reverse(head);
printlist(head);
system("pause");
return 0;
}

#include<stdio.h>  
#include<stdlib.h>
typedef struct node  //节点类型定义  
{
	int data;
	struct node *next;
}Node;
void printlist(Node *head)   //打印链表  
{
	Node *p = head;
	while (p->next)
	{
		printf("%-4d->", p->data);
		p = p->next;
	}
	printf("%-4d\n", p->data);
}
void reverse(Node *&head)   //逆置  
{
	if (head == NULL || head->next == NULL)  //空或者只有一个元素不用逆置  
		return;
	Node *pre, *cur, *rear;
	pre = head;
	cur = head->next;
	while (cur)
	{
		rear = cur->next;
		cur->next = pre;
		pre = cur;
		cur = rear;
	}
	//以下两步,很重要  
	head->next = NULL;
	head = pre;
}
int main()
{
	Node p9{ 9, NULL };
	Node p7{ 7, &p9 };
	Node p5{ 5, &p7 };
	Node p3{ 3, &p5 };
	Node p1{ 1, &p3 };
	Node *head = &p1;
	printf("原表是\n");
	printlist(head);
	printf("逆置\n");
	reverse(head);
	printlist(head);
	system("pause");
	return 0;
}


运行:



思考:我们知道链表一般是带有头节点的,而这里我们没有使用头节点。那么,我们如何对一个带有头节点的链表进行逆置呢?逆置后的链表也是要带有头节点的哦。大家可以动手试试,不妨把代码写在评论里,互相参考下,看有什么细节的不同。(楼主的一个写法,在一楼,欢迎不吝赐教,thanks)

方法二:在递归中逆置(反转)

思路:在对当前节点逆置时,先递归地逆置其后继节点,然后将后继节点指向当前节点。

直接给一个测试用例:

[cpp]
view plaincopyprint?





#include<stdio.h>
#include<stdlib.h>
typedef struct node
{
int data;
struct node *next;
}Node;
void printlist(Node *head)
{
Node *p = head;
while (p->next)
{
printf("%-4d->", p->data);
p = p->next;
}
printf("%-4d\n", p->data);
}
void reverseWithRecursion(Node *&head, Node *cur) //递归逆置
{
if (cur->next == NULL) //最后一个元素是递归终止条件
{
head = cur;
return;
}
Node *rear = cur->next;
reverseWithRecursion(head, rear);
rear->next = cur;
//cur->next = NULL; //句一,这一句可以注释掉

}
void reverse(Node *&head)
{
if (head == NULL || head->next == NULL) //为空或只有一个元素就结束
return;
Node *cur=head;
reverseWithRecursion(head, cur);
cur->next = NULL; //句二,这一句可以注释掉,不过句一和句二必须保留一句
}
int main()
{
Node *head = NULL;
Node p8{ 8, NULL };
Node p6{ 6, &p8 };
Node p4{ 4, &p6 };
Node p2{ 2, &p4 };
Node p0{ 0, &p2 };
head = &p0;
printf("原链表\n");
printlist(head);
printf("逆置\n");
reverse(head);
printlist(head);
system("pause");
return 0;
}

#include<stdio.h>
#include<stdlib.h>
typedef struct node
{
	int data;
	struct node *next;
}Node;
void printlist(Node *head)
{
	Node *p = head;
	while (p->next)
	{
		printf("%-4d->", p->data);
		p = p->next;
	}
	printf("%-4d\n", p->data);
}
void reverseWithRecursion(Node *&head, Node *cur)    //递归逆置
{
	if (cur->next == NULL)   //最后一个元素是递归终止条件
	{
		head = cur;
		return;
	}
	Node *rear = cur->next;
	reverseWithRecursion(head, rear);
	rear->next = cur;
	//cur->next = NULL;    //句一,这一句可以注释掉
}
void reverse(Node *&head)
{
	if (head == NULL || head->next == NULL)   //为空或只有一个元素就结束 
		return;
	Node *cur=head;
	reverseWithRecursion(head, cur);
	cur->next = NULL;    //句二,这一句可以注释掉,不过句一和句二必须保留一句
}
int main()
{
	Node *head = NULL;
	Node p8{ 8, NULL };
	Node p6{ 6, &p8 };
	Node p4{ 4, &p6 };
	Node p2{ 2, &p4 };
	Node p0{ 0, &p2 };
	head = &p0;
	printf("原链表\n");
	printlist(head);
	printf("逆置\n");
	reverse(head);
	printlist(head);
	system("pause");
	return 0;
}


运行:



对递归逆置函数的理解是最重要的,也是最难理解的。难点有:

句一的作用。这个初看很难让人明白,甚至误解。我们知道原链表的第一个节点的链域在最后要置空,但这里每次递归都置空,是否会出问题?画个图后,发现不会。我们对递归的过程要理解,并且得明白次的递归函数中的rear指向的正是这一次的cur,(它俩指向同一节点)。所以即使被置空了,当递归返回上一层时,该链域依然会被修改。当然,除了原链表的第一个节点(想想为什么?),这也是这句话的真实目地:让原链表的第一个节点的链域在最后被置空。
由上一点的分析,我们可以想到:为何不直接修改原链表第一个节点的链域呢?何必费心一路递归修改,这样会造成很多的操作是没必要的。事实上的确可以,这就是句二的作用,那就是说不用句一,只用句二就可以了。但它俩至少要有一个,从效率上看,推荐只使用句二。
第一个参数被设计成引用类型,它的意义:在递归到原链表的尾节点时,把head指向该曾经的尾节点(也就是新链表的第一个节点)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: