C++ 入门算法,新手必看之:单向“链表”(一)

俗话说得好,不懂链表的程序员,不配称为C/C++程序员。

为什么呢?

链表的存储主要依据指针来实现,而指针又是C/C++独有的特性,是其他语言没有的。

今天,你点进来看了这篇博客,说明你还是不懂C/C++当中链表的算法。

不懂没关系,看了这篇博客,只要是懂得指针的小伙伴,都会学会使用单向链表


链表是什么?

链表是线性表的链式存储方式,逻辑上相邻的数据在计算机内的存储位置不必须相邻,那么,怎么表示逻辑上的相邻关系呢?可以给每个元素附加一个指针域,指向下一个元素的存储位置。
如图:
在这里插入图片描述
从图中可以看出,每个结点包含两个域:数据域和指针域,指针域存储下一个结点的地址,因此指针指向的类型也是结点类型。

链表的核心要素:

  1. 每个节点由数据域和指针域组成
  2. 指针域指向下一个节点的内存地址

链表分为三种:

  1. 单向链表
  2. 循环链表
  3. 双向链表

今天这篇文章讲的是单向链表。

单向链表其结构体定义:
typedef struct LinkNode{
ElemType data;
struct LinkNode *next;
} LinkList, LinkNode;


单向链表的概念

在这里插入图片描述
如上图就是一条单向链表。链表的节点均单向指向下一个节点,形成一条单向访问的数据链。

链表我们把他分为头节点和节点,头节点默认是链表的头部,不存储数据,节点则存储所有数据。
在这里插入图片描述
如图就是一个典型的单向链表。头节点不存储数据,但是他的指针指向节点1的地址,所以头节点和节点1链接起来了。然后节点1的指针又指向了节点2的地址,使节点1和节点2也链接起来了…最后,节点i的指针指向节点n,最后一个节点,最后一个节点n的指针指向了NULL。至此,一条链条诞生了。

就好比如,头节点知道了节点1的地址,头节点就可以根据地址找到节点1;
就好比如,张三知道了李四家的地址,张三就可以根据地址找到李四。

因为他是单向链表,只能从头节点开始,一直到尾节点,不存在节点1的指针指向头节点,就好比如不存在李四能找到张三,只能张三找到李四。

讲到这里,可能还有很多朋友还是不懂链表,不过没关系,通过下面的例子,你就能完全掌握链表的用法。


单向链表的定义与初始化

// 定义链表
typedef struct Link {
	int date;	// 链表中的数据
	struct Link* next;	// 下一个节点地址
}LinkNode, LinkList;	// LinkNode:节点	LinkList:头节点

// 初始化链表
bool initLink(LinkList* &L) {	// 参数一:单链表的头节点指针的引用
	L = new LinkNode;	// 分配内存

	if (!L) {	// 是否分配失败
		cout << "生成头节点失败!" << endl;
		return false;	// 生成节点失败
	}

	L->next = NULL;	// 因为是初始化,所以头节点指向NULL
	return true;
}

定义链表中,可以看到,他是一个结构体,date是该链表的数据,也可以是其他数据类型;struct Link* next;该条语句是必须的,定义自己结构体的指针,用于存储下一个节点的地址,这也是上面说的指针。
LinkNode, LinkList;,该两条语句是用于定义结构体变量的,两个用法都是一个的,只是取不同的名字,用于区分定义的结构体变量的含义。

我们再来看一下链表的初始化:
它就是要给函数接口,里面为结构体的变量分配内存,因为是初始化,里面没有存储数据,他的指针也就指向了NULL。
初始化
链表的初始化就是这么简单,因为我们定义的链表是指针类型的变量,所以获取里面的数据是需要使用”->“来获取。

链表初始化后,链表仅只有一个头节点,没有其他节点(如上图)。


头插法

好了,链表初始化好后,我们就可以给他插入数据了。现在我们来学习一下头插法,就是在头部插入数据。

注意:他并不是在头节点的位置插入数据,而是在头节点的下一个节点,也就是节点1的位置插入数据。

头插法有两种情况:
情况一:链表中没有其他节点,只有头节点。
在这里插入图片描述
如图,它需要在头节点的后面插入节点。

情况二:链表中已有其他节点。
在这里插入图片描述
链表中有其他节点,头插法也一样还是在头节点的后面插入,插入后,新的节点就成为节点1,而原有的节点位置不变,节点名字加1.

虽然有两种情况,但是他们实现的代码都是一样的:

// 头插法
bool LinkInsert_front(LinkList*& List, LinkNode* Node) {	// 参数一:链表的头节点指针的引用;参数二:待插入的新节点
	if (!List || !Node) {	// 合法性检查
		cout << "链表为空!" << endl;
		return false;
	}

	Node->next = List->next;	// 新插入节点的next指向头节点的下一个节点
	List->next = Node;			// 头节点的next指向新插入的节点

	return true;
}

在这里插入图片描述
如上图就是他的连接过程。
我们需要将新插入的节点的指针next指向头节点的指针next原指向的节点,然后再将头节点的指针next指向新节点,就完成了插入。


尾插法

顾名思义,尾插法就是在链表的最后面插入节点。

在链表尾部插入,必须得先找到尾节点。

他也有两种情况:
情况一:链表中没有其他节点,只有头节点。
在这里插入图片描述
因为只有头节点,所以头节点也就是最后一个节点,可以直接插入。

情况二:链表中已有其他节点。
在这里插入图片描述
我们需要找到尾节点,才可以插入。

虽然有两种情况,但是他们实现的代码都是一样的:

// 尾插法
bool LinkInsert_back(LinkList*& List, LinkNode* Node) {	// 参数一:链表的头节点指针的引用;参数二:待插入的新节点
	if (!List || !Node) {	// 合法性检查
		cout << "链表为空!" << endl;
		return false;
	}

	LinkNode* p = List;		// 定义临时节点指向头节点,用于找到尾节点

	while (p->next) p = p->next;	// 找到尾节点

	Node->next = p->next;	// 新插入节点的next指向NULL,因为尾节点的下一个节点必须是NULL值(也可以写成这样:Node->next = NULL;)
	p->next = Node;			// 旧尾节点的next指向新尾节点

	return true;
}

我们需要定义临时节点,然后遍历指向最后一个节点的位置,就可以利用它插入新的节点了。
p = p->next的意思是:假如p是代表节点1,那么p->next就是节点2,p = p->next就是p要代表节点2了。

while(p->next),当p的下一个节点不为NULL的话,则行循环。当p->next为NULL时,说明p已经在链表最后一个节点的位置了。


任意位置插入

顾名思义,就是可以在链表的任何位置插入节点。
在这里插入图片描述
我们必须得找到插入位置节点的前一个节点,才可以进行插入。

// 任意位置插入
bool LinkInsert(LinkList*& List, int i, int& e) {	// 参数一:链表的头节点指针的引用;参数二:插入的位置;参数三:插入的元素
	if (!List) {	// 合法性检查
		cout << "链表为空!" << endl;
		return false;
	}

	LinkList* p, * s;	// p用于寻找插入位置的前一个节点,s用于创建新节点待插入

	p = List;	// 将头节点赋值给p,用作下面循环查找

	int j = 0;	// 循环条件;因为从头节点开始,所以赋值0

	while (p && j < i - 1) {	// 查找位置为i-1的节点,p指向该节点
		p = p->next;
		j++;
	}

	// 假如i大于链表的个数,则p为NULL,返回false;假如i为负数或者为零,则返回false
	if (!p || j > i - 1) return false;	// i值不合法的情况:i > n || i <= 0

	s = new LinkNode;	// 分配新节点内存

	s->date = e;		// 将元素赋值给新节点
	s->next = p->next;	// 新插入的节点指向插入位置后的下一个节点
	p->next = s;		// 插入位置的前一个节点指向新插入的节点

	return true;
}

任意位置插入的算法难度在于如何找到插入位置的前一个节点。
代码中我们使用while循环进行查找,其中j < i-1,就是找到节点的关键条件。


获取链表中指定节点位置的值

到了这里,如果上面的链表代码都搞懂了的话,至此下面所讲的所有链表的用法都不难了。

获取任意位置节点的值和任意位置插入节点 代码实现都时差不多的,只是指定位置获取节点的值,只需要找到该节点就行了。而任意位置插入节点就是找到节点的上一个节点。

// 获取链表中指定节点位置的值
bool Link_GetElem(LinkList*& List, int i, int& e) {	// 参数一:链表的头节点指针的引用;参数二:查找的位置;参数三:获取它的值
	if (!List || !List->next) {	// 合法性检查
		cout << "链表为空!" << endl;
		return false;
	}

	LinkList* p = List->next;	// 定义临时节点,用于寻找要获取的节点的值处
	int j = 1;	// 循环条件;因为是从第一个节点开始循环查找,所以赋值1

	while (p && j < i) {	// 寻找到i的节点位置,p指向它
		p = p->next;
		j++;
	}

	// i>n,则p为NULL,所以返回false;i<=0,则j肯定是大于i的,所以返回false
	if (!p || j > i) return false;	// i值不合法的情况:i > n || i <= 0

	e = p->date;	// 将寻找到链表中的值赋值给引用变量e返回

	return true;
}

此代码难点也是如何找到该节点位置。


查找该值在链表中节点的位置

就是通过一个值,去遍历整个链表,判断链表中是否有节点存储该值,有则返回该节点的位置。

// 查找该值在链表中节点的位置
bool LinkFindElem(LinkList*& List, int e, int& i) {	// 参数一:链表的头节点指针的引用;参数二:链表中查找的值;参数三:返回查找到的节点位置
	if (!List || !List->next) {	// 合法性检查
		cout << "链表为空!" << endl;
		return false;
	}

	LinkList* p = List->next;	// 定义临时节点,用于寻找要查找的节点的值处
	int j = 1;	// 记录节点;因为是从第一个节点开始循环查找,所以赋值1

	while (p && p->date != e) {	// 当前节点p不为NULL,而且当前节点的值不等于e时,执行循环,还未找到e
		p = p->next;
		j++;
	}

	if (!p) {	// 假如while循环没有找到,那么p为NULL,查无此值
		i = 0;	// 将i赋值零,查无此值
		return false;
	}

	i = j;	// 则行到这一步,说明找到了,将节点的位置赋值给i值返回
	return true;
}

都是和上面差不多的代码,只是将位置条件判断换成了值的判断。


修改指定位置节点的值

修改链表中节点的值。

代码就是和 ’获取链表中指定节点位置的值‘ 当中的代码一模一样,只是找到后,就将节点的值修改掉。

// 修改指定位置节点的值
bool LinkAlterValue(LinkList*& List, int i, int e) {	// 参数二:节点的位置;参数三:修改的值
	if (!List || !List->next) {	// 合法性检查
		cout << "链表为空!" << endl;
		return false;
	}

	LinkList* p = List->next;	// 定义临时节点指向头节点的下一个节点
	int j = 1;	// 用于找到节点位置

	while (p && j < i) {	// 找到节点位置,p指向它
		p = p->next;
		j++;
	}

	// i>n,触发条件一;i<=0,触发条件二
	if (!p || j > i) return false;	// i值不合法的情况:i > n || i <= 0

	// 执行到这一步说明已经找到了需要修改值的节点,p指向它
	p->date = e;	// 将e值赋值给p的date	
	return true;
}

删除链表中的一个节点

它也有两种情况:
情况一:根据节点的位置删除

// 删除链表中的一个节点:1.根据节点的位置删除
bool LinkDelete_1(LinkList*& List, int i) {	// 参数一:链表的头节点指针的引用;参数二:待删除节点的位置
	if (!List || !List->next) {	// 合法性检查
		cout << "链表为空!" << endl;
		return false;
	}

	LinkList* p, * q;	// p用于寻找删除位置的前一个节点,q用于辅助删除和释放被删除节点的内存

	p = List;	// 将头节点赋值给p,用作下面循环查找
	int j = 0;	// 循环条件;因为从头节点开始,所以赋值0

	while (p->next && j < i - 1) {	// 寻找到待删除节点的前一个节点位置,p指向它
		p = p->next;
		j++;
	}	// 循环结束时,p指向最后一个节点

	// 当i>n || i<1,p的下一个节点为NULL,返回flase;当i<1,则为不合法,不可能删除头节点和负数位置的节点,返回false
	if (!(p->next) || j > i - 1) return false;	// 当i>n || i<1时,删除位置不合理

	q = p->next;	// 将待删除节点赋值给q
	p->next = q->next;	// 待删除节点的前一个节点指向待删除节点的后一个节点位置
	delete q;		// 释放掉待删除节点的内存
	
	return true;
}

找到待删除节点的前一个节点,就可以删除了。
在这里插入图片描述
定义临时节点指向节点1,然后将节点1的next指向节点3,就完成了节点2的删除,最后定义临时节点指向节点2,将节点2的内存释放掉就行了。

情况二:根据节点的值删除

都一样的代码,找到待删除节点的前一个节点根据值判断寻找。就可以进行删除操作了。

// 删除链表中的一个节点:2.根据节点的值删除
bool LinkDelete_2(LinkList*& List, int e) {	// 参数一:链表的头节点指针的引用;参数二:待删除节点的值
	if (!List || !List->next) {	// 合法性检查
		cout << "链表为空!" << endl;
		return false;
	}

	LinkList* p, * q;	// p用于寻找删除位置的前一个节点,p用于辅助删除和释放被删除节点的内存

	p = List;	// 将头节点赋值给p,用作下面循环查找

	while (p->next && ((p->next)->date) != e) {	// 寻找到待删除节点的前一个节点位置,p指向它
		p = p->next;
	}	// 循环结束时,p指向最后一个节点

	if (!(p->next)) return false;	// 如果p的下一个节点为NULL,则没有找到该值对应的节点

	q = p->next;	// 将待删除节点赋值给q
	p->next = q->next;	// 待删除节点的前一个节点指向待删除节点的后一个节点位置
	delete q;		// 释放掉待删除节点的内存

	return true;
}

销毁单链表

既然你使用了链表,那么就涉及到从堆栈非配内存的相关问题,当程序结束时,需要将链表的内存释放掉。

// 销毁单链表
void LinkDestroy(LinkList*& List) {
	if (!List) {	// 合法性检查
		cout << "链表为NULL!" << endl;
		return;
	}
	cout << "链表销毁!" << endl;

	LinkList* p = List;	// 定义临时节点指向头节点,用于释放节点的内存

	while (p) {	// 如果p为真,则继续则行循环,直到链表全部释放掉为止
		cout << "删除节点:" << p->date << endl;
		List = List->next;	// 向下一个节点
		delete p;			// 释放掉当前节点的内存
		p = List;			// p移动到下一个节点
	}
}

定义临时节点指向头节点,当临时节点不为NULL时,先将头节点指向下一个节点,然后释放掉临时节点指向的节点内存,再将头节点指向的下一个节点赋值给临时节点。如此循环释放,就可以将链表完整的释放掉了。


链表输出

输出链表中的元素。

// 链表输出
void LinkPrint(LinkList*& List) {	// 链表
	if (!List) {	// 合法性检查
		cout << "链表为空!" << endl;
		return;
	}

	LinkNode* p = List->next;	// 定义临时链表指向头节点的下一个节点

	while (p) {	// 如果不为空,执行
		cout << p->date << "\t";	

		p = p->next;	// p指向己的下一个节点
	}
}

完整测试代码:

#include <iostream>
#include <Windows.h>

using namespace std;

// 定义链表
typedef struct Link {
	int date;	// 链表中的数据
	struct Link* next;	// 下一个节点地址
}LinkNode, LinkList;	// LinkNode:节点	LinkList:头节点

// 初始化链表
bool initLink(LinkList* &L) {	// 参数一:单链表的头节点指针的引用
	L = new LinkNode;	// 分配内存

	if (!L) {	// 是否分配失败
		cout << "生成头节点失败!" << endl;
		return false;	// 生成节点失败
	}

	L->next = NULL;	// 因为是初始化,所以头节点指向NULL
	return true;
}

// 头插法
bool LinkInsert_front(LinkList*& List, LinkNode* Node) {	// 参数一:链表的头节点指针的引用;参数二:待插入的新节点
	if (!List || !Node) {	// 合法性检查
		cout << "链表为空!" << endl;
		return false;
	}

	Node->next = List->next;	// 新插入节点的next指向头节点的下一个节点
	List->next = Node;			// 头节点的next指向新插入的节点

	return true;
}

// 尾插法
bool LinkInsert_back(LinkList*& List, LinkNode* Node) {	// 参数一:链表的头节点指针的引用;参数二:待插入的新节点
	if (!List || !Node) {	// 合法性检查
		cout << "链表为空!" << endl;
		return false;
	}

	LinkNode* p = List;		// 定义临时节点指向头节点,用于找到尾节点

	while (p->next) p = p->next;	// 找到尾节点

	Node->next = p->next;	// 新插入节点的next指向NULL,因为尾节点的下一个节点必须是NULL值(也可以写成这样:Node->next = NULL;)
	p->next = Node;			// 旧尾节点的next指向新尾节点

	return true;
}

// 任意位置插入
bool LinkInsert(LinkList*& List, int i, int& e) {	// 参数一:链表的头节点指针的引用;参数二:插入的位置;参数三:插入的元素
	if (!List) {	// 合法性检查
		cout << "链表为空!" << endl;
		return false;
	}

	LinkList* p, * s;	// p用于寻找插入位置的前一个节点,s用于创建新节点待插入

	p = List;	// 将头节点赋值给p,用作下面循环查找

	int j = 0;	// 循环条件;因为从头节点开始,所以赋值0

	while (p && j < i - 1) {	// 查找位置为i-1的节点,p指向该节点
		p = p->next;
		j++;
	}

	// 假如i大于链表的个数,则p为NULL,返回false;假如i为负数或者为零,则返回false
	if (!p || j > i - 1) return false;	// i值不合法的情况:i > n || i <= 0

	s = new LinkNode;	// 分配新节点内存

	s->date = e;		// 将元素赋值给新节点
	s->next = p->next;	// 新插入的节点指向插入位置后的下一个节点
	p->next = s;		// 插入位置的前一个节点指向新插入的节点

	return true;
}

// 获取链表中指定节点位置的值
bool Link_GetElem(LinkList*& List, int i, int& e) {	// 参数一:链表的头节点指针的引用;参数二:查找的位置;参数三:获取它的值
	if (!List || !List->next) {	// 合法性检查
		cout << "链表为空!" << endl;
		return false;
	}

	LinkList* p = List->next;	// 定义临时节点,用于寻找要获取的节点的值处
	int j = 1;	// 循环条件;因为是从第一个节点开始循环查找,所以赋值1

	while (p && j < i) {	// 寻找到i的节点位置,p指向它
		p = p->next;
		j++;
	}

	// i>n,则p为NULL,所以返回false;i<=0,则j肯定是大于i的,所以返回false
	if (!p || j > i) return false;	// i值不合法的情况:i > n || i <= 0

	e = p->date;	// 将寻找到链表中的值赋值给引用变量e返回

	return true;
}

// 查找该值在链表中节点的位置
bool LinkFindElem(LinkList*& List, int e, int& i) {	// 参数一:链表的头节点指针的引用;参数二:链表中查找的值;参数三:返回查找到的节点位置
	if (!List || !List->next) {	// 合法性检查
		cout << "链表为空!" << endl;
		return false;
	}

	LinkList* p = List->next;	// 定义临时节点,用于寻找要查找的节点的值处
	int j = 1;	// 记录节点;因为是从第一个节点开始循环查找,所以赋值1

	while (p && p->date != e) {	// 当前节点p不为NULL,而且当前节点的值不等于e时,执行循环,还未找到e
		p = p->next;
		j++;
	}

	if (!p) {	// 假如while循环没有找到,那么p为NULL,查无此值
		i = 0;	// 将i赋值零,查无此值
		return false;
	}

	i = j;	// 则行到这一步,说明找到了,将节点的位置赋值给i值返回
	return true;
}

// 修改指定位置节点的值
bool LinkAlterValue(LinkList*& List, int i, int e) {	// 参数二:节点的位置;参数三:修改的值
	if (!List || !List->next) {	// 合法性检查
		cout << "链表为空!" << endl;
		return false;
	}

	LinkList* p = List->next;	// 定义临时节点指向头节点的下一个节点
	int j = 1;	// 用于找到节点位置

	while (p && j < i) {	// 找到节点位置,p指向它
		p = p->next;
		j++;
	}

	// i>n,触发条件一;i<=0,触发条件二
	if (!p || j > i) return false;	// i值不合法的情况:i > n || i <= 0

	// 执行到这一步说明已经找到了需要修改值的节点,p指向它
	p->date = e;	// 将e值赋值给p的date	
	return true;
}

// 删除链表中的一个节点:1.根据节点的位置删除
bool LinkDelete_1(LinkList*& List, int i) {	// 参数一:链表的头节点指针的引用;参数二:待删除节点的位置
	if (!List || !List->next) {	// 合法性检查
		cout << "链表为空!" << endl;
		return false;
	}

	LinkList* p, * q;	// p用于寻找删除位置的前一个节点,q用于辅助删除和释放被删除节点的内存

	p = List;	// 将头节点赋值给p,用作下面循环查找
	int j = 0;	// 循环条件;因为从头节点开始,所以赋值0

	while (p->next && j < i - 1) {	// 寻找到待删除节点的前一个节点位置,p指向它
		p = p->next;
		j++;
	}	// 循环结束时,p指向最后一个节点

	// 当i>n || i<1,p的下一个节点为NULL,返回flase;当i<1,则为不合法,不可能删除头节点和负数位置的节点,返回false
	if (!(p->next) || j > i - 1) return false;	// 当i>n || i<1时,删除位置不合理

	q = p->next;	// 将待删除节点赋值给q
	p->next = q->next;	// 待删除节点的前一个节点指向待删除节点的后一个节点位置
	delete q;		// 释放掉待删除节点的内存
	
	return true;
}

// 删除链表中的一个节点:2.根据节点的值删除
bool LinkDelete_2(LinkList*& List, int e) {	// 参数一:链表的头节点指针的引用;参数二:待删除节点的值
	if (!List || !List->next) {	// 合法性检查
		cout << "链表为空!" << endl;
		return false;
	}

	LinkList* p, * q;	// p用于寻找删除位置的前一个节点,p用于辅助删除和释放被删除节点的内存

	p = List;	// 将头节点赋值给p,用作下面循环查找

	while (p->next && ((p->next)->date) != e) {	// 寻找到待删除节点的前一个节点位置,p指向它
		p = p->next;
	}	// 循环结束时,p指向最后一个节点

	if (!(p->next)) return false;	// 如果p的下一个节点为NULL,则没有找到该值对应的节点

	q = p->next;	// 将待删除节点赋值给q
	p->next = q->next;	// 待删除节点的前一个节点指向待删除节点的后一个节点位置
	delete q;		// 释放掉待删除节点的内存

	return true;
}

// 销毁单链表
void LinkDestroy(LinkList*& List) {
	if (!List) {	// 合法性检查
		cout << "链表为NULL!" << endl;
		return;
	}
	cout << "链表销毁!" << endl;

	LinkList* p = List;	// 定义临时节点指向头节点,用于释放节点的内存

	while (p) {	// 如果p为真,则继续则行循环,直到链表全部释放掉为止
		cout << "删除节点:" << p->date << endl;
		List = List->next;	// 向下一个节点
		delete p;			// 释放掉当前节点的内存
		p = List;			// p移动到下一个节点
	}
}

// 链表输出
void LinkPrint(LinkList*& List) {	// 链表
	if (!List) {	// 合法性检查
		cout << "链表为空!" << endl;
		return;
	}

	LinkNode* p = List->next;	// 定义临时链表指向头节点的下一个节点

	while (p) {	// 如果不为空,执行
		cout << p->date << "\t";	

		p = p->next;	// p指向己的下一个节点
	}
}

int main(void) {
	LinkList* list = NULL;	// 链表头节点
	LinkNode* node = NULL;	// 新节点

	// 初始化链表
	if (initLink(list)) {
		cout << "初始化成功!" << endl;
	} else {
		cout << "初始化失败!" << endl;
	}


	// 头插法
	int n = 0;
	cout << "请输入头插法需要插入的个数n:";
	cin >> n;
	while (n > 0) {
		node = new LinkNode;	// 分配一个节点内存

		cin >> node->date;
		LinkInsert_front(list, node);	// 插入

		n--;
	}

	// 尾插法
	int nn = 0;
	cout << "请输入尾插法需要插入的个数nn:";
	cin >> nn;
	while(nn > 0) {
		node = new LinkNode;	// 分配新节点内存

		cin >> node->date;
		LinkInsert_back(list, node);

		nn--;
	}

	LinkPrint(list);
	cout << endl;

	// 任意位置插入
	int nnn;
	int i, date;
	cout << "请输入任意位置需要插入的元素个数nnn:";
	cin >> nnn;
	while (nnn > 0) {
		cout << "请输入插入位置和插入的元素:";
		cin >> i >> date;

		if (LinkInsert(list, i, date)) {
			cout << "插入成功!" << endl;
		} else {
			cout << "插入失败!" << endl;
		}

		LinkPrint(list);
		cout << endl;

		nnn--;
	}

	// 获取链表中指定节点的值
	int e = 0;
	if (Link_GetElem(list, 3, e)) {
		cout << "获取节点3成功,值为:" << e << endl;
	} else {
		cout << "获取节点3失败!" << endl;
	}

	// 查找链表中的值
	if (LinkFindElem(list, 5, e)) {
		cout << "查找值为5成功,节点位置为:" << e << endl;
	} else {
		cout << "查找值为5失败,节点位置返回:" << e << endl;
	}

	// 删除链表中的一个节点:1.根据节点的位置删除
	if (LinkDelete_1(list, 3)) {
		cout << "删除节点位置3成功!" << endl;
	} else {
		cout << "删除节点位置3失败!" << endl;
	}

	LinkPrint(list);
	cout << endl;

	// 删除链表中的一个节点:2.根据节点的值删除
	if (LinkDelete_2(list, 1)) {
		cout << "删除值为1的节点成功!" << endl;
	} else {
		cout << "删除值为1的节点失败!" << endl;
	}

	LinkPrint(list);
	cout << endl;

	// 修改指定位置节点的值
	if (LinkAlterValue(list, 1, 44)) {
		cout << "修改第一个节点的值成功!" << endl;
		LinkPrint(list);
	} else {
		cout << "修改第一个节点的值失败!" << endl;
		LinkPrint(list);
	}

	// 销毁单链表
	LinkDestroy(list);

	system("pause");
	return 0;
}

总结:
链表其实不难,只需要认真学好我前面所讲的那几个操作,后面的那些都时融汇贯通的。

发布了50 篇原创文章 · 获赞 46 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/cpp_learner/article/details/105015219