保姆级单链表教程

一:什么是数据结构

数据结构:数据结构是计算机存储、组织数据的方式。数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。通常情况下,精心选择的数据结构可以带来更高的运行或者存储效率。数据结构往往同高效的检索算法和索引技术有关。
简单的说数据结构就是存取数据的一种结构

二:数据结构中结构体的写法

在写数据结构中我们显而易见要用到结构体,那么一个数据结构的结构体应该怎么写呢?
结构体是描述数据结构的基本单元,那么我们用抽象单一个体的方式就可以将数据结构的基本单元抽象出来这样就等得到了该数据结构的结构体。
举例:
我们以这篇链表(链式结构为例)
首先我们要知道链式结构就是结构体变量和结构体变量连接到一起
在这里插入图片描述
如上图就是一个链表的结构,通过其结构我们不难发现。该链表是由一个一个的节点连接而成的。
在这里插入图片描述
这个节点就是该链表结构的单一个体。那么我们的结构体来将该节点描述出来即可。
节点的结构:
节点有一个数据域成员data用于存储数据内容,一个指针域成员next用于指向下一个节点来实现节点的连接。(这里的数据域成员data类型多样(可以是另一个结构体变量),数量不限,这里用一个data表示所有要存取的数据成员)
下面就可以将结构体抽象出来了。

struct Node
{
	int data;
	struct Node* next;
};

三:有表头单链表

单链表可以有表头也可以没有表头
区别:有表头链表的链表首节点不存放数据,无表头链表的表头存放数据。

(一):有表头链表的创建

创建链表即是创建节点的过程,我们通常用链表结构的首个节点来表示整个链表。
这就和站队一样,我们假设每个队都有一面旗帜。站队时只要找到了所在队的旗帜就相当于找到了队伍。然后根据你前后人的所在位置去找到你应该站在哪。这里旗帜的位置并没有人,它只起一个标记作用,“指向”队伍的第一个人(数据)。

//1.创建有表头链表 用表头指针表示整个链表
struct Node* createList()
{
	struct Node* headNode = (struct Node*)malloc(sizeof(struct Node));
	//有表头链表表头不存放数据
	headNode->next = NULL;
	return headNode;
}

这里我们用headNode来代表整个链表。

(二): 创建节点

创建节点的过程和创建链表(表头)的过程一样:

//2.创建节点
struct Node* createNode(int data)
{
	struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
	newNode->data = data;//数据的初始化
	newNode->next = NULL;
	return newNode;
}

(三):插入

1:头插

我们用图形来描述一下这个过程:
在这里插入图片描述
注意:1和2别写反了

//3.表头法插入
void insertByHead(struct Node* headNode, int data)
{
	struct Node* newNode = createNode(data);
	newNode->next = headNode->next;
	headNode->next = newNode;
}

2:尾插

做尾插入的时候,我们的关键是找到尾节点。找到尾节点后,将尾节点指向要插入的节点即可
找尾节点的过程和打印链表的过程一样,我们需要一个移动的节点,从头往后找。

//4.尾插
void insertByTail(struct Node* headNode, int data)
{
	struct Node* newNode = createNode(data);
	struct Node* pMode = headNode;
	while (pMode->next != NULL)
	{
		pMode = pMode->next;
	}
	//跳出循环后,pMode即是尾节点
	pMode->next = newNode;
}

3:指定位置前插入

尾插法的主要任务也是首要任务是找到尾节点,而指定位置插入的首要任务当然是找到指定的节点位置了。(做插入操作时主要是找到插入的位置,实现插入很简单,只需要进行“连线”即可,我们看到首插入很简单,这是因为首节点的位置是知道的)
我们这里进行的是指定位置前插入,既然这样,我们要知道指定的位置和指定位置的前一个节点。
注意:指定位置前插入和指定位置后插入有区别
我们需要两个移动的指针,其中一个来找指定的数据并指向数据的节点,另一个指针来记录指定位置的前一个节点即可。然而指定位置后插入的话,找到指定位置即可,我们可以通过posNode->next得到指定位置的后一个节点。
过程:
在这里插入图片描述](https://img-blog.csdnimg.cn/20191121103405994.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDc5NTgzOQ==,size_16,color_FFFFFF,t_70)

代码:

//5.指定位置插入  在指定位置的前面插入
void insertByAppoin(struct Node* headNode, int posData,int data)
{
	struct Node* posFrontNode = headNode;
	struct Node* posNode = headNode->next;
	while (posNode->data != posData)
	{
		posFrontNode = posNode;
		posNode = posNode->next;
		if (posNode == NULL)
		{
			break;//找遍链表仍然没有找到指定数据
		}
	}
	//分析posNode的状态
	if (posNode == NULL)
	{
		printf("未找到指定数据,无法进行插入");
	}
	else
	{
		struct Node* newNode = createNode(data);//找到指定位置了,确认可以进行插入了,才创建节点。防止创建了节点,而没有地方插
		newNode->next = posNode;
		posFrontNode->next = newNode;
	}
}

4:指定位置后插入

这个就容易了:

//6.指定位置后插入
void insertByAppoinTail(struct Node* headNode, int posData, int data)
{
	struct Node* posNode = headNode->next;
	while (posNode->data != posData)
	{
		posNode = posNode->next;
		if (posNode == NULL)
		{
			break;
		}
	}
	if (posNode == NULL)
	{
		printf("未找到指定数据,无法进行插入");
	}
	else
	{
		struct Node* newNode = createNode(data);
		newNode->next = posNode->next;
		posNode->next = newNode;
	}
}

(四):删除

1:头删除

在这里插入图片描述

//7.表头删除
void deleteNodeByHead(struct Node* headNode)
{
	struct Node* nextNode = headNode->next;
	if (nextNode == NULL)
	{
		printf("链表为空,无法删除\n");
	}
	else
	{
		headNode->next = nextNode->next;
		free(nextNode);
		nextNode = NULL;
	}
}

2:尾删除

在这里插入图片描述

//8.表尾删除
void deleteNodeByTail(struct Node* headNode)
{
	struct Node* tailFrontNode = headNode;
	struct Node* tailNode = headNode->next;
	if (tailNode == NULL)
	{
		printf("链表为空,无法删除!");
		return;
	}
	while (tailNode->next != NULL)
	{
		tailFrontNode = tailNode;
		tailNode = tailNode->next;
	}
	tailFrontNode->next = NULL;   //删除后节点后要将表尾的指向置空
	free(tailNode);
	tailNode = NULL;
}

3:指定位置删除

//9.指定位置删除
void deleteNodeByAppion(struct Node* headNode, int posData)
{
	struct Node* posFrontNode = headNode;
	struct Node* posNode = headNode->next;
	while (posNode->data != posData)
	{
		posFrontNode = posNode;
		posNode = posNode->next;
		if (posNode == NULL)
		{
			break;
		}
	}
	if (posNode == NULL)
	{
		printf("未找到指定数据,无法删除\n");
	}
	else
	{
		posFrontNode->next = posNode->next;
		free(posNode);
		posNode = NULL;
	}
}

(五):链表的打印

在这里插入图片描述
如上:我们需要定义一个移动的指针,由于有表头链表的首节点没有数据,我们起始让它指向第二个节点。依次打印,往后移动,直到最后即可遍历链表上的数据。

//10.打印
void printList(struct Node* headNode)
{
	//有表头链表从第二个节点开始打印
	struct Node* pMove = headNode->next;
	while (pMove != NULL)
	{
		printf("%d ", pMove->data);
		pMove = pMove->next;
	}
	printf("\n");
}

(六):完整测试代码

#include<stdio.h>
#include<stdlib.h>
struct Node
{
	int data;
	struct Node* next;
};
//1.创建有表头链表 用表头指针表示整个链表
struct Node* createList()
{
	struct Node* headNode = (struct Node*)malloc(sizeof(struct Node));
	//有表头链表表头不存放数据
	headNode->next = NULL;
	return headNode;
}
//2.创建节点
struct Node* createNode(int data)
{
	struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
	newNode->data = data;//数据的初始化
	newNode->next = NULL;
	return newNode;
}
//3.表头法插入
void insertByHead(struct Node* headNode, int data)
{
	struct Node* newNode = createNode(data);
	newNode->next = headNode->next;
	headNode->next = newNode;
}

//4.尾插
void insertByTail(struct Node* headNode, int data)
{
	struct Node* newNode = createNode(data);
	struct Node* pMode = headNode;
	while (pMode->next != NULL)
	{
		pMode = pMode->next;
	}
	//跳出循环后,pMode即是尾节点
	pMode->next = newNode;
}
//5.指定位置插入  在指定位置的前面插入
void insertByAppoinFront(struct Node* headNode, int posData,int data)
{
	struct Node* posFrontNode = headNode;
	struct Node* posNode = headNode->next;
	while (posNode->data != posData)
	{
		posFrontNode = posNode;
		posNode = posNode->next;
		if (posNode == NULL)
		{
			break;//找遍链表仍然没有找到指定数据
		}
	}
	//分析posNode的状态
	if (posNode == NULL)
	{
		printf("未找到指定数据,无法进行插入");
	}
	else
	{
		struct Node* newNode = createNode(data);//找到指定位置了,确认可以进行插入了,才创建节点。防止创建了节点,而没有地方插
		newNode->next = posNode;
		posFrontNode->next = newNode;
	}
}
//6.指定位置后插入
void insertByAppoinTail(struct Node* headNode, int posData, int data)
{
	struct Node* posNode = headNode->next;
	while (posNode->data != posData)
	{
		posNode = posNode->next;
		if (posNode == NULL)
		{
			break;
		}
	}
	if (posNode == NULL)
	{
		printf("未找到指定数据,无法进行插入");
	}
	else
	{
		struct Node* newNode = createNode(data);
		newNode->next = posNode->next;
		posNode->next = newNode;
	}
}
//7.表头删除
void deleteNodeByHead(struct Node* headNode)
{
	struct Node* nextNode = headNode->next;
	if (nextNode == NULL)
	{
		printf("链表为空,无法删除\n");
	}
	else
	{
		headNode->next = nextNode->next;
		free(nextNode);
		nextNode = NULL;
	}
}
//8.表尾删除
void deleteNodeByTail(struct Node* headNode)
{
	struct Node* tailFrontNode = headNode;
	struct Node* tailNode = headNode->next;
	if (tailNode == NULL)
	{
		printf("链表为空,无法删除!");
		return;
	}
	while (tailNode->next != NULL)
	{
		tailFrontNode = tailNode;
		tailNode = tailNode->next;
	}
	tailFrontNode->next = NULL;   //删除后节点后要将表尾的指向置空
	free(tailNode);
	tailNode = NULL;
}
//9.指定位置删除
void deleteNodeByAppion(struct Node* headNode, int posData)
{
	struct Node* posFrontNode = headNode;
	struct Node* posNode = headNode->next;
	while (posNode->data != posData)
	{
		posFrontNode = posNode;
		posNode = posNode->next;
		if (posNode == NULL)
		{
			break;
		}
	}
	if (posNode == NULL)
	{
		printf("未找到指定数据,无法删除\n");
	}
	else
	{
		posFrontNode->next = posNode->next;
		free(posNode);
		posNode = NULL;
	}
}
//10.打印
void printList(struct Node* headNode)
{
	//有表头链表从第二个节点开始打印
	struct Node* pMove = headNode->next;
	while (pMove != NULL)
	{
		printf("%d ", pMove->data);
		pMove = pMove->next;
	}
	printf("\n");
}
int main()
{
	struct Node* list = createList();
	insertByHead(list, 1);
	insertByHead(list, 2);
	insertByHead(list, 3);
	insertByTail(list, -1);
	insertByAppoinFront(list, -1, 0); 
	insertByAppoinTail(list, -1, -2);
	insertByAppoinTail(list, -2, -3);
	deleteNodeByHead(list);
	deleteNodeByTail(list);
	deleteNodeByAppion(list, 2);
	printList(list);
	return 0;
}

四:无表头链表(二级指针)

无表头链表就是链表的首节点存放数据,虽然说没有表头,但是我们需要一个指针来进行标记,以方便后续的打印遍历,插入,删除操作,该指针一直指向链表的首节点。这样该指针就起到了标记链表的做作用了。

这里的无表头链表的就像站队时,队首放的不是旗帜,而是用一名队长站在队首作为这整个队伍的标记。

(一):创建节点

由于没有表头,每个节点的形态都一样,故我们只需要创建一种节点即可
如下:

//1.创建节点
//无表头链表的所有节点都存有数据,故没有表头(链表的首节点)与节点的区别.不需要写创建链表的过程
struct Node* createNode(int data)
{
	struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
	newNode->data = data;
	newNode->next = NULL;
	return newNode;
}

(二):插入

1:头插

如上所说:我们需要一个指针来作为标记:标记:struct Node *list=NULL,该指针为节点指针,其始终指向链表的首节点(初始时链表为空,list指向NULL)。即做头插时,每插入一个新的节点我们都要使list指向新的节点newNode(list永远站在队首),我们在函数中要改变一级指针的值就要为函数传入二级指针
在这里插入图片描述
如上过程:
新节点的next指向list,然后list指向新节点newNode
代码:

//2.表头法插入
//无表头链表插入时,list始终指向首节点也就是新插入的节点newNode,故在头插法插入节点时,每次插入时都要改变list的指向,也就是list的值
//这里我们要通过函数改变struct Node* list的值,需要传入struct Node* list的地址,否者进行操作的是拷贝本
void insertByHead(struct Node** list, int data)
{
	struct Node* newNode = createNode(data);
	newNode->next = *list;
	*list = newNode;
}

2:指定位置插入

与前面有表头链表的插入一样,做指定位置插入时首要任务是找到指定位置。我们需要两个相邻的节点指针从首节点来比较对应数据,直到两节点找到指定数据或者找到末尾。
过程与前面无差别,需要注意的是;当指定位置的数据等于首节点数据时,此时进行的是头插,需要改变list的指向

//3.指定位置插入
void insertByAppoin(struct Node** list, int posData, int data)
{
	struct Node* posFrontNode = *list;
	struct Node* posNode = (*list)->next;
	if (posFrontNode->data == posData)
	{
		insertByHead(list, data);//当首节点的数据为posData时调用头插法
	}
	else
	{
		while (posNode->data != posData)
		{
			posFrontNode = posNode;
			posNode = posNode->next;                                    
			if (posNode == NULL)//当posNode==NULL时,说明没有找到匹配的数据
			{
				break;
			}
		}
		if (posNode == NULL)//判断posNode的状态
		{
			printf("未找到指定数据,无法进行插入");
			return;
		}
		else
		{
			//在posNode前插入节点
			struct Node* newNode = createNode(data);
			newNode->next = posNode;
			posFrontNode->next = newNode;
		}
	}
}

(三):删除

1:表头删除

做表头删除时,我们需要注意:清空list的数据后,要将list仍指向首节点。所以删除前我们要保存一下第二个节点,改变list的指向我们需要传入二级指针

//4.表头删除
void deleteNodeByHead(struct Node** list)//改变list的指向,我们要传入二级指针
{
	struct Node* nextNode = (*list)->next;//保存一下第二个节点
	if (*list == NULL)
	{
		printf("链表为空,无法删除\n");
	}
	else
	{
		free(*list);//清空首节点的指向
		*list = nextNode;//将list一直指向首节点
	}
}

2:指定位置删除

注意点:指定位置为首节点时:头删除,改变list的指向

//5.指定位置删除
void deleteNodeByAppoin(struct Node** list, int posData)//当指定位置是首节点时,头删除,需改变list的指向,传二级指针
{
	struct Node* posFrontNode = *list;//移动指针寻找指定位置
	struct Node* posNode = (*list)->next;
	if (posFrontNode->data == posData)
	{
		deleteNodeByHead(list);//头删除
	}
	else
	{
		while (posNode->data != posData)
		{
			posFrontNode = posNode;
			posNode = posNode->next;
			if (posNode == NULL)
			{
				break;
			}
		}
		if (posNode == NULL)//判断posNode的状态
		{
			printf("未找到指定数据,无法进行删除\n");
		}
		else
		{
			posFrontNode->next = posNode->next;
			free(posNode);
			posNode = NULL;
		}
	}
}

(四):链表的打印

//5.打印链表
void printList(struct Node* list)
{
	struct Node* pMove = list;//首节点存储数据,则从首节点开始打印
	while (pMove)
	{
		printf("%d ", pMove->data);
		pMove = pMove->next;
	}
	printf("\n");
}

(五):完整测试代码

#include<stdio.h>
#include<stdlib.h>
//使用二级指针的的方法
//无表头链表:第一个节点中存放数据
struct Node
{
	int data;
	struct Node* next;
};
//1.创建节点
//无表头链表的所有节点都存有数据,故没有表头(链表的首节点)与节点的区别.不需要写创建链表的过程
struct Node* createNode(int data)
{
	struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
	newNode->data = data;
	newNode->next = NULL;
	return newNode;
}
//2.表头法插入
//无表头链表插入时,list始终指向首节点也就是新插入的节点newNode,故在头插法插入节点时,每次插入时都要改变list的指向,也就是list的值
//这里我们要通过函数改变struct Node* list的值,需要传入struct Node* list的地址,否者进行操作的是拷贝本
void insertByHead(struct Node** list, int data)
{
	struct Node* newNode = createNode(data);
	newNode->next = *list;
	*list = newNode;
}
//3.指定位置插入
void insertByAppoin(struct Node** list, int posData, int data)
{
	struct Node* posFrontNode = *list;
	struct Node* posNode = (*list)->next;
	if (posFrontNode->data == posData)
	{
		insertByHead(list, data);//当首节点的数据为posData时调用头插法
	}
	else
	{
		while (posNode->data != posData)
		{
			posFrontNode = posNode;
			posNode = posNode->next;                                    
			if (posNode == NULL)//当posNode==NULL时,说明没有找到匹配的数据
			{
				break;
			}
		}
		if (posNode == NULL)//判断posNode的状态
		{
			printf("未找到指定数据,无法进行插入");
			return;
		}
		else
		{
			//在posNode前插入节点
			struct Node* newNode = createNode(data);
			newNode->next = posNode;
			posFrontNode->next = newNode;
		}
	}
}
//4.表头删除
void deleteNodeByHead(struct Node** list)//改变list的指向,我们要传入二级指针
{
	struct Node* nextNode = (*list)->next;//保存一下第二个节点
	if (*list == NULL)
	{
		printf("链表为空,无法删除\n");
	}
	else
	{
		free(*list);//清空首节点的指向
		*list = nextNode;//将list一直指向首节点
	}
}
//5.指定位置删除
void deleteNodeByAppoin(struct Node** list, int posData)//当指定位置是首节点时,头删除,需改变list的指向,传二级指针
{
	struct Node* posFrontNode = *list;//移动指针寻找指定位置
	struct Node* posNode = (*list)->next;
	if (posFrontNode->data == posData)
	{
		deleteNodeByHead(list);//头删除
	}
	else
	{
		while (posNode->data != posData)
		{
			posFrontNode = posNode;
			posNode = posNode->next;
			if (posNode == NULL)
			{
				break;
			}
		}
		if (posNode == NULL)//判断posNode的状态
		{
			printf("未找到指定数据,无法进行删除\n");
		}
		else
		{
			posFrontNode->next = posNode->next;
			free(posNode);
			posNode = NULL;
		}
	}
}
//5.打印链表
void printList(struct Node* list)
{
	struct Node* pMove = list;//首节点存储数据,则从首节点开始打印
	while (pMove)
	{
		printf("%d ", pMove->data);
		pMove = pMove->next;
	}
	printf("\n");
}
int main()
{ 
	//用一个指针指向表头,表示整个链表
	//list永远都指向表头  list永远都指向新的节点
	struct Node* list = NULL; 
	insertByHead(&list,1);
	insertByHead(&list,2);
	insertByHead(&list,3);
	printList(list);
	insertByAppoin(&list, 3, 5);
	insertByAppoin(&list, 3, 4);
	printList(list);
	deleteNodeByHead(&list);
	deleteNodeByHead(&list);
	printList(list);
	deleteNodeByAppoin(&list,3);
	deleteNodeByAppoin(&list,2);
	deleteNodeByAppoin(&list,1);
	printList(list);
	return 0;
}

五:无表头链表的再封装法

用结构体封装的方法去描述一种结构
将链表这一数据结构抽象出来,找其共有属性:表首frontNode,表尾tailNode,存储数据个数size

//0.结构体描述链表
struct List
{
	//抽象一种数据属性,共有属性
	struct Node* frontNode;
	struct Node* tailNode;
	int size;
};

(一):创建节点

//1.创建节点
struct Node* createNode(int data)
{
	struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
	newNode->data = data;
	newNode->next = NULL;
	return newNode;
}

(二):创建链表

//2.创建链表
struct List* createList()
{
	struct List* list = (struct List*)malloc(sizeof(struct List));
	list->frontNode = list->tailNode = NULL;
	list->size = 0;
	return list;
}

(三):插入

1:头插入

//3.头插法
void insertNodeByHead(struct List* list, int data)
{
	struct Node* newNode = createNode(data);
	if (list->size == 0)
	{
		list->frontNode = newNode;
		list->tailNode = newNode;
	}
	else
	{
		newNode->next = list->frontNode;
		list->frontNode = newNode;
	}
	list->size++;
}

2:尾插

再封装后,首节点和尾节点都是已知的,因此做一些操作时比较简单。

//4.尾插法
void insertNodeByTail(struct List* list, int data)
{
	struct Node* newNode = createNode(data);
	if (list->size == 0)//当元素个数为零时,首尾节点都指向newNode
	{
		list->frontNode = newNode;
		list->tailNode = newNode;
	}
	else
	{
		list->tailNode->next = newNode;//做连接
		list->tailNode = newNode;//始终将list->frontNode指向首节点
	}
	list->size++;//元素个数增加1
}

3:指定位置插入

//5.指定位置插入
void insertNodeByAppoin(struct List* list, int posData, int data)
{
	struct Node* posFrontNode = list->frontNode;
	struct Node* posNode = list->frontNode->next;
	if (posFrontNode->data == posData)//采用头插法
	{
		insertNodeByHead(list,data);
	}
	else
	{
		while (posNode->data != posData)
		{
			posFrontNode = posNode;
			posNode = posNode->next;
			if (posNode == NULL)
			{
				break;
			}
		}
		if (posNode == NULL)//判断pMode的状态
		{
			printf("未找到指定位置,无法插入\n");
		}
		else
		{
			struct Node* newNode = createNode(data);//等找到指定数据的时候,再创建newNode
			newNode->next = posNode;
			posFrontNode->next = newNode;
			list->size++;//元素个数增加1
		}
	}
}

(四):删除

1:头删

//6.表头删除
void deleteNodeByHead(struct List* list)
{
	if (list->size == 0)
	{
		printf("链表为空,无法删除\n");
		return;
	}
	else
	{
		struct Node* nextNode = list->frontNode->next;//记录下第二个节点
		free(list->frontNode);
		list->frontNode = nextNode;
		list->size--;//元素个数减1
	}
}

2:尾删

注意点:最后pMove的next并不指向NULL,所以需要将list->tail->next置空

//7.表尾删除
void deleteNodeByTail(struct List* list)
{
	if (list->size == 0)
	{
		printf("链表为空,无法删除\n");
		return;
	}
	else
	{
		struct Node* pMove = list->frontNode;
		while (pMove->next != list->tailNode)
		{
			pMove = pMove->next;
		}
		//循环出来后    pMove->next=list->tailNode
		//即pMove指向链表尾节点的前一个节点
		free(list->tailNode);
		list->tailNode = pMove;//注意注意注意:::这时的pMove的next的指向不为NULL
		list->tailNode->next = NULL;//注意
		list->size--;
	}
}

(五):打印

//8.打印链表
void printList(struct List* list)
{
	struct Node* pMove = list->frontNode;
	if (list->size == 0)
	{
		printf("链表为空,无法打印\n");
	}
	while (pMove)
	{
		printf("%d ", pMove->data);
		pMove = pMove->next;
	}
	printf("\n");
}

(六):完整测试代码

#include<stdio.h>
#include<stdlib.h>
struct Node
{
	int data;
	struct Node* next;
};
//0.结构体描述链表
struct List
{
	//抽象一种数据属性,共有属性
	struct Node* frontNode;
	struct Node* tailNode;
	int size;
};
//1.创建节点
struct Node* createNode(int data)
{
	struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
	newNode->data = data;
	newNode->next = NULL;
	return newNode;
}
//2.创建链表
struct List* createList()
{
	struct List* list = (struct List*)malloc(sizeof(struct List));
	list->frontNode = list->tailNode = NULL;
	list->size = 0;
	return list;
}
//3.头插法
void insertNodeByHead(struct List* list, int data)
{
	struct Node* newNode = createNode(data);
	if (list->size == 0)//当元素个数为零时,首尾节点都指向newNode
	{
		list->frontNode = newNode;
		list->tailNode = newNode;
	}
	else
	{
		newNode->next = list->frontNode;//做连接
		list->frontNode = newNode;//始终将list->frontNode指向首节点
	}
	list->size++;//元素个数增加1
}
//4.尾插法
void insertNodeByTail(struct List* list, int data)
{
	struct Node* newNode = createNode(data);
	if (list->size == 0)//当元素个数为零时,首尾节点都指向newNode
	{
		list->frontNode = newNode;
		list->tailNode = newNode;
	}
	else
	{
		list->tailNode->next = newNode;//做连接
		list->tailNode = newNode;//始终将list->frontNode指向首节点
	}
	list->size++;//元素个数增加1
}

//5.指定位置插入
void insertNodeByAppoin(struct List* list, int posData, int data)
{
	struct Node* posFrontNode = list->frontNode;
	struct Node* posNode = list->frontNode->next;
	if (posFrontNode->data == posData)//采用头插法
	{
		insertNodeByHead(list,data);
	}
	else
	{
		while (posNode->data != posData)
		{
			posFrontNode = posNode;
			posNode = posNode->next;
			if (posNode == NULL)
			{
				break;
			}
		}
		if (posNode == NULL)//判断pMode的状态
		{
			printf("未找到指定位置,无法插入\n");
		}
		else
		{
			struct Node* newNode = createNode(data);//等找到指定数据的时候,再创建newNode
			newNode->next = posNode;
			posFrontNode->next = newNode;
			list->size++;//元素个数增加1
		}
	}
}
//6.表头删除
void deleteNodeByHead(struct List* list)
{
	if (list->size == 0)
	{
		printf("链表为空,无法删除\n");
		return;
	}
	else
	{
		struct Node* nextNode = list->frontNode->next;//记录下第二个节点
		free(list->frontNode);
		list->frontNode = nextNode;
		list->size--;//元素个数减1
	}
}
//7.表尾删除
void deleteNodeByTail(struct List* list)
{
	if (list->size == 0)
	{
		printf("链表为空,无法删除\n");
		return;
	}
	else
	{
		struct Node* pMove = list->frontNode;
		while (pMove->next != list->tailNode)
		{
			pMove = pMove->next;
		}
		//循环出来后    pMove->next=list->tailNode
		//即pMove指向链表尾节点的前一个节点
		free(list->tailNode);
		list->tailNode = pMove;//注意注意注意:::这时的pMove的next的指向不为NULL
		list->tailNode->next = NULL;//注意
		list->size--;
	}
}
//8.打印链表
void printList(struct List* list)
{
	struct Node* pMove = list->frontNode;
	if (list->size == 0)
	{
		printf("链表为空,无法打印\n");
	}
	while (pMove)
	{
		printf("%d ", pMove->data);
		pMove = pMove->next;
	}
	printf("\n");
}
int main()
{
	struct List* list = createList();
	insertNodeByHead(list, 1);
	insertNodeByHead(list, 2);
	insertNodeByHead(list, 3);
	printList(list);
	insertNodeByTail(list, 0);
	insertNodeByTail(list, -1);
	printList(list);
	deleteNodeByHead(list);
	deleteNodeByHead(list);
	printList(list);
	deleteNodeByTail(list);
	printList(list);
	insertNodeByAppoin(list, 1, 2);
	printList(list);
	system("pause");
	return 0;
}

未完……持续更新数据结构

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
当然,我可以为您提供 VSCode 的保姆安装教程。请按照以下步骤操作: 步骤1:下载 VSCode 首先,您需要访问 Visual Studio Code 的官方网站(https://code.visualstudio.com/)并点击下载按钮。根据您的操作系统选择相应的版本进行下载。 步骤2:安装 VSCode 下载完成后,双击安装程序并按照提示进行安装。根据您的操作系统可能会有所不同,但通常情况下只需按照默认设置一路点击"下一步"即可完成安装。 步骤3:启动 VSCode 安装完成后,您可以通过在开始菜单、桌面或应用程序目录中找到 Visual Studio Code 来启动它。 步骤4:配置 VSCode 在初次启动 VSCode 时,您可能需要进行一些基本配置。例如,选择默认编程语言、设置主题、选择插件等。这些设置可以在 VSCode 的设置菜单中进行调整,以满足您的需求。 步骤5:安装常用插件 VSCode 支持大量的插件扩展,您可以根据自己的需求进行安装。一些常用的插件包括: - Prettier:代码格式化工具 - ESLint:JavaScript 代码检查工具 - GitLens:Git 可视化工具 - Bracket Pair Colorizer:彩色显示括号对 - Live Server:实时预览网页 - Python:Python 开发相关插件 您可以在 VSCode 的扩展商店中搜索并安装这些插件。 步骤6:开始使用 VSCode 现在,您已经完成了 VSCode 的安装和配置。您可以打开您的项目文件夹或编写新的代码文件,并开始使用 VSCode 进行开发和编辑。 希望这个保姆安装教程对您有所帮助!如果您还有其他问题,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值