链表的就地逆置(多图详解)头插法和更改指向法


此处说的是单链表,双链表就没逆置这说法了,循环链表可以按照单链表的操作实现

头插法改变指向

图解

原本链表状态是这样的
在这里插入图片描述
想要成为这样在这里插入图片描述
把头部断开,剩下的数据部分再用一遍头插法就好了

请添加图片描述

具体操作

首先需要两个工作指针,p和remain
p用于指向当前操作结点
remain用于指向剩下待操作的结点
头结点后继指空
在这里插入图片描述

首先让p指向remain所指向的结点,也就是剩下结点的第一个,让remain,指向自己的后继
在这里插入图片描述
接着让p操作的结点,指向头结点所指,此处为null,不明显,下一轮就明显了
再让头结点指向当前操作结点
在这里插入图片描述

明显的第二轮操作

  1. p指向remain在这里插入图片描述

  2. remain指向自己的后继在这里插入图片描述

  3. p指向头结点的后继在这里插入图片描述

  4. 头结点后继更新为p在这里插入图片描述
    以此循环,直到remain指向为空

代码实现


#include <iostream>
using namespace std;

struct Node {
	int data;
	Node* next;
};//创建结构体——结点

void show(Node* head) {
	head = head->next;
	while (head != nullptr) {
		cout << head->data << '\t';
		head = head->next;
	}
}

void reverse(Node* head) {
	Node* remain, * p, * q;
	remain = head->next;//指向剩下的链表
	head->next = nullptr;//
	while (remain != nullptr) {
		p = remain;//取出剩下结点的第一个
		remain = remain->next;//remain指向自己的后继
		p->next = head->next;//当前结点指向头结点后继
		head->next = p;//更新头结点后继为当前结点
	}
}

int main()
{
	int num;
	Node* first, * p, * rear;
	first = new Node;
	first->next = nullptr;//头结点
	rear = first;

	cout << "请输入数据:   0代表输入结束" << '\n';
	cin >> num;
	while (num != 0) {
		p = new Node;
		p->data = num;
		p->next = nullptr;//新节点

		rear->next = p;
		rear = rear->next;//尾指针重新指向尾部
		cin >> num;
	}//以上是尾插法构建初始链表
	cout << "原链表为:";
	show(first);//显示原链表

	reverse(first);
	cout << "逆置链表为:";
	show(first);//显示新链表
}

注释很详细了,我就不细讲了

运行结果

在这里插入图片描述

更改相邻结点

思考方向

本来链表应该是这样的逻辑结构
在这里插入图片描述

最终应该是这样的逻辑结构
在这里插入图片描述

这样一个工作指针显然是不够的,改变方向后指向原来的通道就断了,毕竟单链表逻辑上是单向的

在链表已有first指针的基础上,引入2个工作指针p、q 在这里插入图片描述

在确保first改变指向(指向p)的时候,q能保存剩余链表的指向(其实还需要一个保存first地址,但不是主要作用)

由于结点1最终指向空,所以我就不拿他具体演示了,直接拿结点2演示,更有普遍性
在操作结点2时,初始状态应该是这样在这里插入图片描述
first结点的指针 指向 p指向的结点(first->next=p)
在这里插入图片描述

p移到first的位置(p=first)
在这里插入图片描述

first移到q的位置(first=q)
在这里插入图片描述

q指向q的后继(q=q->next)在这里插入图片描述
这样一次循环就完成了,循环条件是first指向不为空
这样就确保每个结点都改变了指向

最后头结点指向p所指的结点,first重新指头结点即可

具体代码

首先是结点,数据域我就用int代替了

struct Node{
	int data;
	Node* next;
};//创建结构体——结点

其次是链表类

class LinkList {
private:
	Node* first;
public:
	LinkList();
	LinkList(int a[], int n);
	void reverse();
};
LinkList::LinkList() {
	first = new Node;
	first->next = nullptr;
}
LinkList::LinkList(int a[], int n) {
	first = new Node;
	Node* p = first, * q = first;
	for (int i = 0; i < n; i++) {
		p->next = new Node;
		p = p->next;
		p->data = a[i];
	}
	p->next = nullptr;
	q = q->next;//让p指向第一个结点
	cout << "链表已创建完毕,依次存储:" << endl;
	while (q != nullptr) {
		cout << q->data << '\t';
		q = q->next;
	}
	cout << '\n';
}

最后就是逆序功能的具体实现啦,就是按照上文思路图来写的

void LinkList::reverse() {
	Node* p = nullptr, * q, * a = first;
	first = first->next;
	q = first->next;
	while (1) {
		first->next = p;
		p = first;
		first = q;
		if (first == nullptr) break;
		q = q->next;
	}
	a->next = p;
	first = a;//逆序已全部完成,以下为遍历输出
	cout << "已完成逆序,现在链表存储数据顺序为:" << endl;
	Node* b = first->next;
	while(b != nullptr) {
		cout << b->data << '\t';
		b = b->next;
	}
}

值得一提的是,我这里判断语句并没有放进while循环开头,原因在于最后一次操作时,q已经指向空,再执行q=q->next;就会发生异常在这里插入图片描述
主函数用1 2 3 4 5测试的

int main() {
	int a[5] = { 1,2,3,4,5 };
	LinkList l(a, 5);
	l.reverse();
}

运行结果如图在这里插入图片描述
感谢能读到这里,留个赞再走呗在这里插入图片描述

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值