单链表基本内容回顾_第四周


单链表内容回顾

已经到大三下学期,有必要对基本数据结构和一些基本算法进行回顾和复习,每周更新,包括基本数据结构以及一些基本算法,这里使用C++语言实现较多,在时间有空余的情况下会更新 Java 实现基本的数据结构和算法。
这里每实现一个函数都会进行一个测试,测试的代码包括已经实现的全部功能,因此文章可能会比较繁琐,文章仅供参考,如有错误,欢迎批评指教。
文章 PDF 链接:

「单链表内容回顾.pdf」https://www.aliyundrive.com/s/vn3XUQwMG7p
点击链接保存,或者复制本段内容,打开「阿里云盘」APP ,无需下载极速在线查看,视频原画倍速播放。

单链表的定义和表示

线性表链式存储结构的特点是:用一组任意的存储单元存储线表的数据元素(这组存储单元可以是连续的,也可以是不连续的)。因此,为了表示每个数据元素 a i a_i ai 与其直接后继数据元素 a i + 1 a_i+1 ai+1 之间的逻辑关系,对数据元素 a i a_i ai 来说,除了存储其本身的信息之外,还需存储一个指示其直接后继的信息(即直接后继的存储位置)。这两部分信息组成数据元素 a i ​ a_i​ ai 的存储映像,称为结点(node)。

单链表:由于链表的每个结点中只包含一个指针域,故称为线性链表单链表

单链表特点

  • 包括两个域:数据域(data),指针域(node)
  • 指针域中存储的信息称为指针或链,存储直接后继
  • n个结点链结成一个链表

单链表图解

结点的实现

关于结点的实现代码如下:

typedef struct LNode
{
	// 这里数据域使用 int 型, 这里使用了递归定义
	int data;
	LNode *next;
}LNode, *LinkList;

说明:

  • 这里对同一结构体指针类型起了两个名称,LinkList 与 * LNode,两者本质等价。习惯上用 LinkList 定义单链表,强调定义的是某个单链表的头指针;用 LNode * 定义指向单链表中任意结点的指针变量。
  • 区分指针变量和结点变量两个不同的概念,若定义 LinkList p 或 LNode *p,则 p 为指向某结点的指针变量,表示结点的地址;而 * p 为对应的结点变量,表示该节点的名称。

头插法创建单链表

头插法又称为前插法,是通过将新结点逐个插入链表的头部(头结点)之后来创建链表,每次申请一个新节点,读入相应的数据元素值,然后将新节点插入到头结点之后。

使用头插法创建单链表时,注意是逆序的。

头插法创建单链表

代码实现:

/**
 * 使用头插法创建单链表,注意是逆置创建
 * 
 * @param &L 指向头结点的头指针
 * @param n 表示插入结点的个数
 * 
 * */
void createHead(LinkList &L, int n)
{
	// 首先将头结点的指针域置空
	L = new LNode;
	L->next = NULL;
	for(int i = 0; i < n; i++)
	{
		// 生成新节点
		LNode *p = new LNode;
		// 输入新节点中的 data
		cin >> p->data;
		// 实现图解 ①
		p->next = L->next;
		// 实现图解 ②
		L->next = p;
	}
}

这里写一个遍历链表的函数进行测试:

/**
 * 遍历单链表
 * 
 * @param &L 表示指向头结点的头指针
 * 
 * */
void getElementAll(LinkList &L)
{
	LNode *p = L->next;
	while(p != NULL)
	{
		cout << p->data << " ";
		p = p->next;
	}
}

测试头插法创建单链表

这里进行测试,以上完整代码如下:

#include <iostream>
using namespace std;

typedef struct LNode
{
	// 这里数据域使用 int 型, 这里使用了递归定义
	int data;
	LNode *next;
}LNode, *LinkList;

/**
 * 使用头插法创建单链表,注意是逆置创建
 * 
 * @param &L 指向头结点的头指针
 * @param n 表示插入结点的个数
 * 
 * */
void createHead(LinkList &L, int n)
{
	// 首先将头结点的指针域置空
	L = new LNode;
	L->next = NULL;
	for(int i = 0; i < n; i++)
	{
		// 生成新节点
		LNode *p = new LNode;
		// 输入新节点中的 data
		cin >> p->data;
		// 实现图解 ①
		p->next = L->next;
		// 实现图解 ②
		L->next = p;
	}
}

/**
 * 遍历单链表
 * 
 * @param &L 表示指向头结点的头指针
 * 
 * */
void getElementAll(LinkList &L)
{
	LNode *p = L->next;
	while(p != NULL)
	{
		cout << p->data << " ";
		p = p->next;
	}
}

int main()
{
	LinkList L;
	cout << "请输入单链表的长度:";
	// n 表示单链表的长度
	int n;
	cin >> n;
	cout << "请输入链表中元素: ";
	// 测试头插法创建单链表
	createHead(L, n);
	// 遍历链表
	getElementAll(L);

	return 0;
}

测试结果如图:
测试头插法创建单链表

尾插法创建单链表

尾插法又称为后插法,是通过将新结点逐个插入到链表的尾部来创建链表。通头插法一样,每次申请一个新节点,读入相应的数据元素值。不同的是,为了使新结点能够插入到表尾,需要增加一个尾指针 r 指向链表的尾结点。

尾插法创建单链表是按正序的

尾插法创建单链表

代码实现:

/**
 * 尾插法创建单链表
 * 
 * @param &L 表示指向头结点的头指针
 * @param n 表示要插入结点个数
 * 
 * */
void createTail(LinkList &L, int n)
{
	L = new LNode;
	L->next = NULL;
	// 首先让指针 r 指向最后一个结点
	LNode *r = L;
	for(int i = 0; i < n; i++)
	{
		LNode *p = new LNode;
		// 实现第 ① 步
		p->next = NULL;
		cin >> p->data;
		// 实现第 ② 步
		r->next = p;
		// 实现第三步
		r = p;
	}
}

测试尾插法创建单链表

这里进行测试,以上完整代码如下:

#include <iostream>
using namespace std;

typedef struct LNode
{
	// 这里数据域使用 int 型, 这里使用了递归定义
	int data;
	LNode *next;
}LNode, *LinkList;

/**
 * 使用头插法创建单链表,注意是逆置创建
 * 
 * @param &L 指向头结点的头指针
 * @param n 表示插入结点的个数
 * 
 * */
void createHead(LinkList &L, int n)
{
	// 首先将头结点的指针域置空
	L = new LNode;
	L->next = NULL;
	for(int i = 0; i < n; i++)
	{
		// 生成新节点
		LNode *p = new LNode;
		// 输入新节点中的 data
		cin >> p->data;
		// 实现图解 ①
		p->next = L->next;
		// 实现图解 ②
		L->next = p;
	}
}

/**
 * 尾插法创建单链表
 * 
 * @param &L 表示指向头结点的头指针
 * @param n 表示要插入结点个数
 * 
 * */
void createTail(LinkList &L, int n)
{
	L = new LNode;
	L->next = NULL;
	// 首先让指针 r 指向最后一个结点
	LNode *r = L;
	for(int i = 0; i < n; i++)
	{
		LNode *p = new LNode;
		// 实现第 ① 步
		p->next = NULL;
		cin >> p->data;
		// 实现第 ② 步
		r->next = p;
		// 实现第三步
		r = p;
	}
}


/**
 * 遍历单链表
 * 
 * @param &L 表示指向头结点的头指针
 * 
 * */
void getElementAll(LinkList &L)
{
	LNode *p = L->next;
	while(p != NULL)
	{
		cout << p->data << " ";
		p = p->next;
	}
}

int main()
{
	LinkList L;
	cout << "请输入单链表的长度:";
	// n 表示单链表的长度
	int n;
	cin >> n;
	cout << "请输入链表中元素: ";
	// 测试头插法创建单链表
	// createHead(L, n);
	
	// 测试尾插法创建单链表
	createTail(L, n);

	// 遍历链表
	getElementAll(L);

	return 0;
}

测试结果如图所示:

测试尾插法创建单链表

单链表的常见操作

取值(根据序号取值)

和顺序表不同,链表中逻辑相邻的结点并没有存储在物理相邻的单元中,这样,根据给定的结点位置序号 i i i ,在链表中获取该结点的值不能像顺序表那样随机访问,而只能从链表的首元结点出发,顺着链域 next 逐个结点向下访问。

取值图解

代码实现:

/**
 * 取值(根据序号取值)
 * 
 * @param &L 表示指向头结点的头指针
 * @param i 表示输入的序号
 * @param &e 通过序号 i 查找到的值,传递回去
 * 
 * */
void getElement(LinkList &L, int i, int &e)
{
	LNode *p = L;
	int j = 0;
	// 这里 j <= i 的时候表示和数组下标相同,都是从 0 开始
	// 如果写成 j < i,表示从 1 开始
	// 为了符合数组从 0 开始的习惯,这里写成 j <= i
	while((p != NULL) && j <= i)
	{
		p = p->next;
		j++;
	}
	e = p->data;
}
测试根据下标取值:

完整代码如下:

#include <iostream>
using namespace std;

typedef struct LNode
{
	// 这里数据域使用 int 型, 这里使用了递归定义
	int data;
	LNode *next;
}LNode, *LinkList;

/**
 * 使用头插法创建单链表,注意是逆置创建
 * 
 * @param &L 指向头结点的头指针
 * @param n 表示插入结点的个数
 * 
 * */
void createHead(LinkList &L, int n)
{
	// 首先将头结点的指针域置空
	L = new LNode;
	L->next = NULL;
	for(int i = 0; i < n; i++)
	{
		// 生成新节点
		LNode *p = new LNode;
		// 输入新节点中的 data
		cin >> p->data;
		// 实现图解 ①
		p->next = L->next;
		// 实现图解 ②
		L->next = p;
	}
}

/**
 * 尾插法创建单链表
 * 
 * @param &L 表示指向头结点的头指针
 * @param n 表示要插入结点个数
 * 
 * */
void createTail(LinkList &L, int n)
{
	L = new LNode;
	L->next = NULL;
	// 首先让指针 r 指向最后一个结点
	LNode *r = L;
	for(int i = 0; i < n; i++)
	{
		LNode *p = new LNode;
		// 实现第 ① 步
		p->next = NULL;
		cin >> p->data;
		// 实现第 ② 步
		r->next = p;
		// 实现第三步
		r = p;
	}
}


/**
 * 遍历单链表
 * 
 * @param &L 表示指向头结点的头指针
 * 
 * */
void getElementAll(LinkList &L)
{
	LNode *p = L->next;
	while(p != NULL)
	{
		cout << p->data << " ";
		p = p->next;
	}
}

/**
 * 取值(根据序号取值)
 * 
 * @param &L 表示指向头结点的头指针
 * @param i 表示输入的序号
 * @param &e 通过序号 i 查找到的值,传递回去
 * 
 * */
void getElement(LinkList &L, int i, int &e)
{
	LNode *p = L;
	int j = 0;
	// 这里 j <= i 的时候表示和数组下标相同,都是从 0 开始
	// 如果写成 j < i,表示从 1 开始
	// 为了符合数组从 0 开始的习惯,这里写成 j <= i
	while((p != NULL) && j <= i)
	{
		p = p->next;
		j++;
	}
	e = p->data;
}

int main()
{
	LinkList L;
	cout << "请输入单链表的长度:";
	// n 表示单链表的长度
	int n;
	cin >> n;
	cout << "请输入链表中元素: ";
	// 测试头插法创建单链表
	// createHead(L, n);

	// 测试尾插法创建单链表
	createTail(L, n);

	// 遍历链表
	// getElementAll(L);

	// 测试根据下标获取元素值
	cout << "请输入下标:";
	int i, e;
	cin >> i;
	getElement(L, i, e);
	cout << "下标" << i << "的值为:" << e << endl;

	return 0;
}

测试结果:

测试根据下标取值

查找(根据给定值查找)

链表中按值查找的过程和顺序表类似,从链表的首元结点出发,依次将结点值和给定值 e 进行比较,返回查询结果。

根据值查找图解

代码实现:

/**
 * 根据给定值查找结点
 * 
 * @param L 表示指向头结点的头指针
 * @param e 表示给定值
 * @return 返回值是结点的地址,如果不存在返回 NULL
 * 
 * */
LNode *LocationElement(LinkList L, int e)
{
	// 首先 p 指向首元结点
	LNode *p = L->next;
	while(p)
	{
		if(p->data == e)
		{
			// 查找成功,返回地址
			return p;
		}
		p = p->next;
	}
	// 查找失败,返回NULL,此时 p == NULL;
	return p;
}
测试根据给定值查找:

完整代码如下:

#include <iostream>
using namespace std;

typedef struct LNode
{
	// 这里数据域使用 int 型, 这里使用了递归定义
	int data;
	LNode *next;
}LNode, *LinkList;

/**
 * 使用头插法创建单链表,注意是逆置创建
 * 
 * @param &L 指向头结点的头指针
 * @param n 表示插入结点的个数
 * 
 * */
void createHead(LinkList &L, int n)
{
	// 首先将头结点的指针域置空
	L = new LNode;
	L->next = NULL;
	for(int i = 0; i < n; i++)
	{
		// 生成新节点
		LNode *p = new LNode;
		// 输入新节点中的 data
		cin >> p->data;
		// 实现图解 ①
		p->next = L->next;
		// 实现图解 ②
		L->next = p;
	}
}

/**
 * 尾插法创建单链表
 * 
 * @param &L 表示指向头结点的头指针
 * @param n 表示要插入结点个数
 * 
 * */
void createTail(LinkList &L, int n)
{
	L = new LNode;
	L->next = NULL;
	// 首先让指针 r 指向最后一个结点
	LNode *r = L;
	for(int i = 0; i < n; i++)
	{
		LNode *p = new LNode;
		// 实现第 ① 步
		p->next = NULL;
		cin >> p->data;
		// 实现第 ② 步
		r->next = p;
		// 实现第三步
		r = p;
	}
}


/**
 * 遍历单链表
 * 
 * @param &L 表示指向头结点的头指针
 * 
 * */
void getElementAll(LinkList &L)
{
	LNode *p = L->next;
	while(p != NULL)
	{
		cout << p->data << " ";
		p = p->next;
	}
}

/**
 * 取值(根据序号取值)
 * 
 * @param &L 表示指向头结点的头指针
 * @param i 表示输入的序号
 * @param &e 通过序号 i 查找到的值,传递回去
 * 
 * */
void getElement(LinkList &L, int i, int &e)
{
	LNode *p = L;
	int j = 0;
	// 这里 j <= i 的时候表示和数组下标相同,都是从 0 开始
	// 如果写成 j < i,表示从 1 开始
	// 为了符合数组从 0 开始的习惯,这里写成 j <= i
	while((p != NULL) && j <= i)
	{
		p = p->next;
		j++;
	}
	e = p->data;
}

/**
 * 根据给定值查找结点
 * 
 * @param L 表示指向头结点的头指针
 * @param e 表示给定值
 * @return 返回值是结点的地址,如果不存在返回 NULL
 * 
 * */
LNode *LocationElement(LinkList L, int e)
{
	// 首先 p 指向首元结点
	LNode *p = L->next;
	while(p)
	{
		if(p->data == e)
		{
			// 查找成功,返回地址
			return p;
		}
		p = p->next;
	}
	// 查找失败,返回NULL,此时 p->next == NULL;
	return p;
}

int main()
{
	LinkList L;
	cout << "请输入单链表的长度:";
	// n 表示单链表的长度
	int n;
	cin >> n;
	cout << "请输入链表中元素: ";
	// 测试头插法创建单链表
	// createHead(L, n);

	// 测试尾插法创建单链表
	createTail(L, n);

	// 遍历链表
	// getElementAll(L);

	// 测试根据下标获取元素值
	// cout << "请输入下标:";
	int i, e;
	// cin >> i;
	// getElement(L, i, e);
	// cout << "下标" << i << "的值为:" << e << endl;


	// 测试给定值获取结点
	cout << "请输入值:";
	cin >> e;
	LNode *p = LocationElement(L, e);
	if(p)
	{
		cout << "此结点值为: " << p->data;
	}
	else 
	{
		cout << "此结点不存在!";
	}

	return 0;
}

测试结果:

测试根据给定值获取结点

插入

要在单链表的两个数据元素之间插入一个元素

插入图解

代码实现:

/**
 * 根据下标,在该下标前插入新结点
 * 
 * @param &L 表示指向头结点的头指针
 * @param i 表示下标,这里下标从 0 开始,和数组一样,便于理解
 * @param e 表示要插入的新结点的 data
 * 
 * */
void insertElement(LinkList &L, int i, int e)
{
	// 首先 r 指向头结点
	LNode *r = L;
	// j 用于定位指向 i 的前一个结点位置
	int j = 0;
	while(j < i)
	{
		r = r->next;
		j++;
	}
	// 创建新节点
	LNode *p = new LNode;
	p->data = e;
	// 实现 ①
	p->next = r->next;
	// 实现 ②
	r->next = p;
}
测试插入

完整代码:

#include <iostream>
using namespace std;

typedef struct LNode
{
	// 这里数据域使用 int 型, 这里使用了递归定义
	int data;
	LNode *next;
}LNode, *LinkList;

/**
 * 使用头插法创建单链表,注意是逆置创建
 * 
 * @param &L 指向头结点的头指针
 * @param n 表示插入结点的个数
 * 
 * */
void createHead(LinkList &L, int n)
{
	// 首先将头结点的指针域置空
	L = new LNode;
	L->next = NULL;
	for(int i = 0; i < n; i++)
	{
		// 生成新节点
		LNode *p = new LNode;
		// 输入新节点中的 data
		cin >> p->data;
		// 实现图解 ①
		p->next = L->next;
		// 实现图解 ②
		L->next = p;
	}
}

/**
 * 尾插法创建单链表
 * 
 * @param &L 表示指向头结点的头指针
 * @param n 表示要插入结点个数
 * 
 * */
void createTail(LinkList &L, int n)
{
	L = new LNode;
	L->next = NULL;
	// 首先让指针 r 指向最后一个结点
	LNode *r = L;
	for(int i = 0; i < n; i++)
	{
		LNode *p = new LNode;
		// 实现第 ① 步
		p->next = NULL;
		cin >> p->data;
		// 实现第 ② 步
		r->next = p;
		// 实现第三步
		r = p;
	}
}


/**
 * 遍历单链表
 * 
 * @param &L 表示指向头结点的头指针
 * 
 * */
void getElementAll(LinkList &L)
{
	LNode *p = L->next;
	while(p != NULL)
	{
		cout << p->data << " ";
		p = p->next;
	}
}

/**
 * 取值(根据序号取值)
 * 
 * @param &L 表示指向头结点的头指针
 * @param i 表示输入的序号
 * @param &e 通过序号 i 查找到的值,传递回去
 * 
 * */
void getElement(LinkList &L, int i, int &e)
{
	LNode *p = L;
	int j = 0;
	// 这里 j <= i 的时候表示和数组下标相同,都是从 0 开始
	// 如果写成 j < i,表示从 1 开始
	// 为了符合数组从 0 开始的习惯,这里写成 j <= i
	while((p != NULL) && j <= i)
	{
		p = p->next;
		j++;
	}
	e = p->data;
}

/**
 * 根据给定值查找结点
 * 
 * @param L 表示指向头结点的头指针
 * @param e 表示给定值
 * @return 返回值是结点的地址,如果不存在返回 NULL
 * 
 * */
LNode *LocationElement(LinkList L, int e)
{
	// 首先 p 指向首元结点
	LNode *p = L->next;
	while(p)
	{
		if(p->data == e)
		{
			// 查找成功,返回地址
			return p;
		}
		p = p->next;
	}
	// 查找失败,返回NULL,此时 p->next == NULL;
	return p;
}

/**
 * 根据下标,在该下标前插入新结点
 * 
 * @param &L 表示指向头结点的头指针
 * @param i 表示下标,这里下标从 0 开始,和数组一样,便于理解
 * @param e 表示要插入的新结点的 data
 * 
 * */
void insertElement(LinkList &L, int i, int e)
{
	// 首先 r 指向头结点
	LNode *r = L;
	// j 用于定位指向 i 的前一个结点位置
	int j = 0;
	while(j < i)
	{
		r = r->next;
		j++;
	}
	// 创建新节点
	LNode *p = new LNode;
	p->data = e;
	// 实现 ①
	p->next = r->next;
	// 实现 ②
	r->next = p;
}

int main()
{
	LinkList L;
	cout << "请输入单链表的长度:";
	// n 表示单链表的长度
	int n;
	cin >> n;
	cout << "请输入链表中元素: ";
	// 测试头插法创建单链表
	// createHead(L, n);

	// 测试尾插法创建单链表
	createTail(L, n);

	// 遍历链表
	// getElementAll(L);

	// 测试根据下标获取元素值
	// cout << "请输入下标:";
	int i, e;
	// cin >> i;
	// getElement(L, i, e);
	// cout << "下标" << i << "的值为:" << e << endl;


	// 测试给定值获取结点
	// cout << "请输入值:";
	// cin >> e;
	// LNode *p = LocationElement(L, e);
	// if(p)
	// {
	// 	cout << "此结点值为: " << p->data;
	// }
	// else 
	// {
	// 	cout << "此结点不存在!";
	// }

	// 测试 根据下标插入新结点
	cout << "请输入下标: ";
	cin >> i;
	cout << "请输入被插入结点的值:";
	cin >> e;
	insertElement(L, i, e);
	getElementAll(L);
	
	return 0;
}

测试结果:

测试插入

删除

根据给定下标删除结点

删除结点图解

代码实现:

/**
 * 根据给定下标删除结点
 * 
 * @param &L 表示指向头结点的头指针
 * @param i 表示下标(下标从 0 开始)
 * 
 * */
void deleteElement(LinkList &L, int i)
{
	LNode *p = L->next;
	int j = 0;
	// 实现 ①
	while(j < i)
	{
		p = p->next;
		j++;
	}
	// 实现 ②
	LNode *q = p->next;
	p->next = q->next;
	// 实现 ③
	delete q;
}
测试删除

完整代码:

#include <iostream>
using namespace std;

typedef struct LNode
{
	// 这里数据域使用 int 型, 这里使用了递归定义
	int data;
	LNode *next;
}LNode, *LinkList;

/**
 * 使用头插法创建单链表,注意是逆置创建
 * 
 * @param &L 指向头结点的头指针
 * @param n 表示插入结点的个数
 * 
 * */
void createHead(LinkList &L, int n)
{
	// 首先将头结点的指针域置空
	L = new LNode;
	L->next = NULL;
	for(int i = 0; i < n; i++)
	{
		// 生成新节点
		LNode *p = new LNode;
		// 输入新节点中的 data
		cin >> p->data;
		// 实现图解 ①
		p->next = L->next;
		// 实现图解 ②
		L->next = p;
	}
}

/**
 * 尾插法创建单链表
 * 
 * @param &L 表示指向头结点的头指针
 * @param n 表示要插入结点个数
 * 
 * */
void createTail(LinkList &L, int n)
{
	L = new LNode;
	L->next = NULL;
	// 首先让指针 r 指向最后一个结点
	LNode *r = L;
	for(int i = 0; i < n; i++)
	{
		LNode *p = new LNode;
		// 实现第 ① 步
		p->next = NULL;
		cin >> p->data;
		// 实现第 ② 步
		r->next = p;
		// 实现第三步
		r = p;
	}
}


/**
 * 遍历单链表
 * 
 * @param &L 表示指向头结点的头指针
 * 
 * */
void getElementAll(LinkList &L)
{
	LNode *p = L->next;
	while(p != NULL)
	{
		cout << p->data << " ";
		p = p->next;
	}
}

/**
 * 取值(根据序号取值)
 * 
 * @param &L 表示指向头结点的头指针
 * @param i 表示输入的序号
 * @param &e 通过序号 i 查找到的值,传递回去
 * 
 * */
void getElement(LinkList &L, int i, int &e)
{
	LNode *p = L;
	int j = 0;
	// 这里 j <= i 的时候表示和数组下标相同,都是从 0 开始
	// 如果写成 j < i,表示从 1 开始
	// 为了符合数组从 0 开始的习惯,这里写成 j <= i
	while((p != NULL) && j <= i)
	{
		p = p->next;
		j++;
	}
	e = p->data;
}

/**
 * 根据给定值查找结点
 * 
 * @param L 表示指向头结点的头指针
 * @param e 表示给定值
 * @return 返回值是结点的地址,如果不存在返回 NULL
 * 
 * */
LNode *LocationElement(LinkList L, int e)
{
	// 首先 p 指向首元结点
	LNode *p = L->next;
	while(p)
	{
		if(p->data == e)
		{
			// 查找成功,返回地址
			return p;
		}
		p = p->next;
	}
	// 查找失败,返回NULL,此时 p->next == NULL;
	return p;
}

/**
 * 根据下标,在该下标前插入新结点
 * 
 * @param &L 表示指向头结点的头指针
 * @param i 表示下标,这里下标从 0 开始,和数组一样,便于理解
 * @param e 表示要插入的新结点的 data
 * 
 * */
void insertElement(LinkList &L, int i, int e)
{
	// 首先 r 指向头结点
	LNode *r = L;
	// j 用于定位指向 i 的前一个结点位置
	int j = 0;
	while(j < i)
	{
		r = r->next;
		j++;
	}
	// 创建新节点
	LNode *p = new LNode;
	p->data = e;
	// 实现 ①
	p->next = r->next;
	// 实现 ②
	r->next = p;
}

/**
 * 根据给定下标删除结点
 * 
 * @param &L 表示指向头结点的头指针
 * @param i 表示下标(下标从 0 开始)
 * 
 * */
void deleteElement(LinkList &L, int i)
{
	LNode *p = L->next;
	int j = 0;
	// 实现 ①
	while(j < i)
	{
		p = p->next;
		j++;
	}
	// 实现 ②
	LNode *q = p->next;
	p->next = q->next;
	// 实现 ③
	delete q;
}


int main()
{
	LinkList L;
	cout << "请输入单链表的长度:";
	// n 表示单链表的长度
	int n;
	cin >> n;
	cout << "请输入链表中元素: ";
	// 测试头插法创建单链表
	// createHead(L, n);

	// 测试尾插法创建单链表
	createTail(L, n);

	// 遍历链表
	// getElementAll(L);

	// 测试根据下标获取元素值
	// cout << "请输入下标:";
	int i, e;
	// cin >> i;
	// getElement(L, i, e);
	// cout << "下标" << i << "的值为:" << e << endl;


	// 测试给定值获取结点
	// cout << "请输入值:";
	// cin >> e;
	// LNode *p = LocationElement(L, e);
	// if(p)
	// {
	// 	cout << "此结点值为: " << p->data;
	// }
	// else 
	// {
	// 	cout << "此结点不存在!";
	// }

	// 测试 根据下标插入新结点
	// cout << "请输入下标: ";
	// cin >> i;
	// cout << "请输入被插入结点的值:";
	// cin >> e;
	// insertElement(L, i, e);
	// getElementAll(L);

	// 测试 删除结点
	cout << "请输入下标: ";
	cin >> i;
	deleteElement(L, i);
	getElementAll(L);
	
	return 0;
}

测试结果:

测试删除

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值