数据结构 -- 链表

1.单链表的按位序插入(带头结点)

struct LNode {   //定义单链表节点类型
	ElemType data;   //每个节点存放一个数据元素
	struct LNode* next;   //指针指向下一个节点
};

struct LNode* p = (struct LNode*)malloc(sizeof(struct LNode));
//增加一个新的结点:在内存中申请一个结点所需空间,并用指针p指向这个结点

完整代码:
#include <iostream>
#include <cstdlib>
using namespace std;

using ElemType = int;

typedef struct LNode {
	ElemType data;
	struct LNode* next;
}LNode,*LinkList;
 

bool ListInsert(LinkList& L, int i, ElemType e) {
	if (i < 1) {
		return false;     //非法位序
	}
	LNode* p = L;     //p指向头结点
	int j = 0;        //j表示p的位序(头结点算0)
	 
	while (p != NULL && j < i - 1) {    // 找到第 i-1 个结点
		p = p->next;  
		j++;
	}
	if (p == NULL) {     // i 过大,链表不够长 
		return false;
	}

	LNode* s = (LNode*)malloc(sizeof(LNode));
	s->data = e;
	s->next = p->next;
	p->next = s;                                 ----解释1

	return true;
}


/* 辅助:初始化一个空链表(只含头结点) */
void InitList(LinkList& L) {
	L = (LNode*)malloc(sizeof(LNode));
	L->next = nullptr;
}

//尾插法建表,方便测试
void CreateListTail(LinkList& L, int n) {
	InitList(L);
	LNode* r = L;
	cout << "请依次输入" << n << "个元素:";
	for (int i = 0;i < n;i++) {
		ElemType x;
		cin >> x;
		LNode* s = (LNode*)malloc(sizeof(LNode));
		s->data = x;
		s->next = NULL;
		r -> next = s;
		r = s;
	}
}                                               ----解释2


//打印链表
void PrintList(LinkList L) {
	LNode* p = L->next;
	cout << "当前链表为:";
	while (p) {
		cout << p->data << (p->next ? "->" : "");
		p = p->next;
	}
	cout << endl;
}                                              ----解释3


/* 测试 */
int main() {
	LinkList L;
	InitList(L);          // 空链表

	/* 手动插入几个元素 */
	ListInsert(L, 1, 10); // 第 1 位插入 10
	ListInsert(L, 2, 20); // 第 2 位插入 20
	ListInsert(L, 1, 5);  // 再插到最前面
	PrintList(L);         // 应输出:5 -> 10 -> 20

	/* 交互式测试 */
	int pos, val;
	cout << "请输入要插入的位序 和 元素值(位序<1结束):";
	while (cin >> pos >> val) {
		if (pos < 1) break;
		if (ListInsert(L, pos, val))
			PrintList(L);
		else
			cout << "插入失败,位序非法或内存不足!" << endl;
	}

	 
	return 0;
}
解释1:
LNode* s = (LNode*)malloc(sizeof(LNode));
s->data = e;
s->next = p->next;
p->next = s;

这四句就是“把一个新生成结点 `s` 插到 `p` 结点后面”的核心四步,逐行解释如下:

1. `LNode* s = (LNode*)malloc(sizeof(LNode));`

   在内存堆里申请一块能装一个 `LNode` 的空间,并把这块空间的首地址赋给指针 `s`。

   通俗讲:造出一个“空结点”,让 `s` 指向它。

2. `s->data = e;`

   把要插入的元素值 `e` 放进这个新结点的数据域。

3. `s->next = p->next;`

   让新结点 `s` 的指针域先指向 `p` 当前的后继结点。

   换句话说:先把 `p` 原来的“下一跳”借过来,变成 `s` 的“下一跳”,这样后面那条链就不会丢。

4. `p->next = s;`

   最后把 `p` 的指针域改成指向 `s`,完成“插队”——从此 `p` 后面不再是旧结点,而是新结点 `s`。

画一条链更直观:

插入前:

… → p →  A  → …

插入后:

… → p →  s  →  A  → …

步骤 3、4 的顺序不能反,如果先执行 `p->next = s` 再执行 `s->next = p->next`,就会把 `A` 的地址丢掉,造成断链。

解释2:
//尾插法建表,方便测试
void CreateListTail(LinkList& L, int n) {
	InitList(L);
	LNode* r = L;
	cout << "请依次输入" << n << "个元素:";
	for (int i = 0;i < n;i++) {
		ElemType x;
		cin >> x;
		LNode* s = (LNode*)malloc(sizeof(LNode));
		s->data = x;
		s->next = NULL;
		r -> next = s;
		r = s;
	}
}

这段代码干的事就是:

“从键盘读 n 个整数,用尾插法把它们依次接成一条带头结点的单链表。”

逐句直译:

1. `InitList(L);`

   先给链表弄个头结点,里面不放数据,`L->next` 现在是 `nullptr`。

2. `LNode* r = L;`

   `r` 叫“尾指针”,永远指向当前链表的最后一个结点。一开始链表只有头结点,所以 `r` 就是 `L`。

3. `for (int i = 0; i < n; ++i)`

   循环 `n` 次,每次读一个数。

4. `cin >> x;`

   从键盘读一个整数到 `x`。

5. `LNode* s = (LNode*)malloc(sizeof(LNode));`

   在堆里新生成一个空结点,让 `s` 指向它。

6. `s->data = x;`

   把刚读到的整数塞进新结点的数据域。

7. `s->next = nullptr;`

   新结点后面暂时没东西,所以先指向空。

8. `r->next = s;`

   把新结点接在尾指针 `r` 的后面。

9. `r = s;`

   尾指针往前挪一步,现在 `r` 又指向“最新”的最后一个结点了。

循环结束后,链表顺序就是键盘输入的顺序,头结点还在最前面,不保存有效数据。

解释3:
//打印链表
void PrintList(LinkList L) {
	LNode* p = L->next;
	cout << "当前链表为:";
	while (p) {
		cout << p->data << (p->next ? "->" : "");
		p = p->next;
	}
	cout << endl;
}

一句话:

“跳过头结点,把链表里的有效数据按顺序打印成  `a -> b -> c`  这种形式。”

逐句解释:

1. `LNode* p = L->next;`

   头结点不存数据,从第一个真正结点开始。

2. `while (p)`

   只要当前结点存在就继续。

3. `cout << p->data`

   输出当前结点的数据。

4. `(p->next ? " -> " : "")`

   如果后面还有结点,就输出箭头,否则什么都不输出(避免末尾多一个 `->`)。

5. `p = p->next;`

   指针往后挪一个。

 

2.单链表的按位序插入(不带头结点)

 

如果不带头结点,则插入,删除第1个元素时,需要更改头指针L;

#include <iostream>
#include <cstdlib>
using namespace std;

using ElemType = int;

typedef struct LNode {
	ElemType data;
	struct LNode* next;
}LNode,*LinkList;
 

bool ListInsert(LinkList& L, int i, ElemType e) {
	if (i < 1) {
		return false;     //非法位序
	}
	LNode* p = L;     //p指向头结点
	int j = 0;        //j表示p的位序(头结点算0)
	 
	while (p != NULL && j < i - 1) {    // 找到第 i-1 个结点
		p = p->next;  
		j++;
	}
	if (p == NULL) {     // i 过大,链表不够长 
		return false;
	}
	LNode* s = (LNode*)malloc(sizeof(LNode));
	s->data = e;
	s->next = p->next;
	p->next = s;
	return true;
}


/* 辅助:初始化一个空链表(只含头结点) */
void InitList(LinkList& L) {
	L = (LNode*)malloc(sizeof(LNode));
	L->next = nullptr;
}

//尾插法建表,方便测试
void CreateListTail(LinkList& L, int n) {
	InitList(L);
	LNode* r = L;
	cout << "请依次输入" << n << "个元素:";
	for (int i = 0;i < n;i++) {
		ElemType x;
		cin >> x;
		LNode* s = (LNode*)malloc(sizeof(LNode));
		s->data = x;
		s->next = NULL;
		r -> next = s;
		r = s;
	}
}


//打印链表
void PrintList(LinkList L) {
	LNode* p = L->next;
	cout << "当前链表为:";
	while (p) {
		cout << p->data << (p->next ? "->" : "");
		p = p->next;
	}
	cout << endl;
}


/* 测试 */
int main() {
	LinkList L;
	InitList(L);          // 空链表

	/* 手动插入几个元素 */
	ListInsert(L, 1, 10); // 第 1 位插入 10
	ListInsert(L, 2, 20); // 第 2 位插入 20
	ListInsert(L, 1, 5);  // 再插到最前面
	PrintList(L);         // 应输出:5 -> 10 -> 20

	/* 交互式测试 */
	int pos, val;
	cout << "请输入要插入的位序 和 元素值(位序<1结束):";
	while (cin >> pos >> val) {
		if (pos < 1) break;
		if (ListInsert(L, pos, val))
			PrintList(L);
		else
			cout << "插入失败,位序非法或内存不足!" << endl;
	}

	 
	return 0;
}

运行结果:

3.单链表的删除

   1.按位序删除(删除第  i  个结点,数据带回)
bool ListDelete(LinkList &L, int i, ElemType &e){
    if (i < 1) return false;          // 位序非法

    LNode *p = L;                     // p 指向头结点(位序 0)
    int j = 0;
    while (p && j < i - 1){           // 找第 i-1 个结点
        p = p->next;
        ++j;
    }
    if (!p || !p->next) return false; // i 过大或已到表尾

    LNode *q = p->next;               // q 指向待删结点(第 i 个)
    e = q->data;                      // 带回数据
    p->next = q->next;                // 跨过待删结点
    free(q);
    return true;
}
2. 指定结点删除(偷后继法,O(1),p 不能是尾结点)
bool DeleteNode(LNode *p){
    if (!p || !p->next) return false; // p 为空或是尾结点则失败
    LNode *q = p->next;               // q 是后继
    p->data = q->data;                // 数据搬上来
    p->next = q->next;                // 跨过后继
    free(q);
    return true;
}

使用示例:

LinkList L;
InitList(L);               // 建带头结点的空表
/* …建表… */

int x;
if (ListDelete(L, 3, x)) cout << "被删元素=" << x << endl;

LNode *p = GetElem(L, 2);  // 拿到已知结点
if (DeleteNode(p)) cout << "指定结点已删除" << endl;

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

遗憾是什么.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值