数据结构(三)——链表

​ 一、线性表的链式表示------链表

线性表链式存储结构的特点是:用一组任意的存储单元存储线性表的数据元素(这组存储单元可以是连续的,也可以是不连续的)

为了表示每个数据元素ai与其后继数据元素ai+1之间的逻辑关系,对数据元素ai来说,除了其本身的信息之外,还需要存储一个指示其直接后继的信息(直接后继的存储位置)。这两部分信息组成数据元素ai的存储映像,称为节点。

节点包括两个域,其中存储数据元素信息的称为数据域,存储直接后继存储位置有域称为指针域。指针域中存储的信息称为指针或链。

n个节点[ai(1<=i<=n)的存储映像]链接成一个链表,即为线性表(a1,a2,a3,...,an)

链表存储结构:

二、单链表

1.单链表------初始化

2.单链表(单个方向)------头插法

每一次把数据都插在头节点的后面

cpp 复制代码
#include<stdio.h>
#include <string.h>
#include<stdlib.h>
#define MAXSIZE 100

typedef int ElemType;

//链表存储结构
typedef struct node{
	ElemType data;
	struct node *next;
}Node;

//单链表初始化
Node* initList()
{
	Node *head = (Node*)malloc(sizeof(Node));
	head->data = 0;
	head->next = NULL;
	return head;
}

//单链表头插法
int insertHead(Node* L,ElemType e)
{
	Node *p = (Node*)malloc(sizeof(Node));
	p->data = e;
	p->next = L->next;
	L->next = p;
	return 0;
}

int main()
{
	Node *list = initList(); //初始化链表
	free(list); //释放内存
	insertHead(list,10);
	insertHead(list,20);
	return 1;
}

原理:

3.单链表------遍历

cs 复制代码
#include<stdio.h>
#include <string.h>
#include<stdlib.h>
#define MAXSIZE 100

typedef int ElemType;

//链表存储结构
typedef struct node{
	ElemType data;
	struct node *next;
}Node;

//单链表初始化
Node* initList()
{
	Node *head = (Node*)malloc(sizeof(Node));
	head->data = 0;
	head->next = NULL;
	return head;
}

//单链表头插法
int insertHead(Node* L,ElemType e)
{
	Node *p = (Node*)malloc(sizeof(Node));
	p->data = e;
	p->next = L->next;
	L->next = p;
	return 1;
}

//单链表遍历
void listNode(Node* L)
{
	Node *p = L->next; //第一个节点赋值给p指针
	while(p != NULL) //p不为空,即链表有数据
	{
		printf("%d ",p->data); //输出数据
		p = p->next; //指向下一个节点
	}
	printf("\n");
}

int main(int argc,char const *argv[])
{
	Node *list = initList(); //初始化链表
	insertHead(list,10);
	insertHead(list,20);
	insertHead(list,30);
	listNode(list);
	return 0;
}

运行:

头插法的顺序和排列的顺序是相反的

4.单链表------尾插法

cpp 复制代码
#include<stdio.h>
#include <string.h>
#include<stdlib.h>
#define MAXSIZE 100

typedef int ElemType;

//链表存储结构
typedef struct node{
	ElemType data;
	struct node *next;
}Node;

//单链表初始化
Node* initList()
{
	Node *head = (Node*)malloc(sizeof(Node));
	head->data = 0;
	head->next = NULL;
	return head;
}

//单链表头插法
int insertHead(Node* L,ElemType e)
{
	Node *p = (Node*)malloc(sizeof(Node));
	p->data = e;
	p->next = L->next;
	L->next = p;
	return 1;
}

//单链表遍历
void listNode(Node* L)
{
	Node *p = L->next; //第一个节点赋值给p指针
	while(p != NULL) //p不为空,即链表有数据
	{
		printf("%d ",p->data); //输出数据
		p = p->next; //指向下一个节点
	}
	printf("\n");
}

//获取尾节点的地址
Node* get_tail(Node *L)
{
	Node *p = L; //把L链表赋值给p指针
	while(p->next != NULL)
	{
		p = p->next;
	}
	return p;
}

//单链表尾插法
Node* insertTail(Node *tail,ElemType e)
{
	Node *p = (Node*)malloc(sizeof(Node)); //创建一块空间并赋值给指针
	p->data = e; //数据域
	tail->next = p;
	p->next = NULL; //使新创建的p节点成为新的尾节点 
	return p;
}

int main(int argc,char const *argv[])
{
	Node *list = initList(); //初始化链表
	Node *tail = get_tail(list);
	tail = insertTail(tail,10);
	tail = insertTail(tail,20);
	tail = insertTail(tail,30);
	listNode(list);
	return 0;
}

运行:

原理:

①当前尾节点指向新创建的一个节点

②新创建的节点指向NULL

  1. 在指定位置插入数据
cpp 复制代码
#include<stdio.h>
#include <string.h>
#include<stdlib.h>
#define MAXSIZE 100

typedef int ElemType;

//链表存储结构
typedef struct node{
	ElemType data;
	struct node *next;
}Node;

//单链表初始化
Node* initList()
{
	Node *head = (Node*)malloc(sizeof(Node));
	head->data = 0;
	head->next = NULL;
	return head;
}

//单链表头插法
int insertHead(Node* L,ElemType e)
{
	Node *p = (Node*)malloc(sizeof(Node));
	p->data = e;
	p->next = L->next;
	L->next = p;
	return 1;
}

//单链表遍历
void listNode(Node* L)
{
	Node *p = L->next; //第一个节点赋值给p指针
	while(p != NULL) //p不为空,即链表有数据
	{
		printf("%d ",p->data); //输出数据
		p = p->next; //指向下一个节点
	}
	printf("\n");
}

//获取尾节点的地址
Node* get_tail(Node *L)
{
	Node *p = L; //把L链表赋值给p指针
	while(p->next != NULL)
	{
		p = p->next;
	}
	return p;
}

//单链表尾插法
Node* insertTail(Node *tail,ElemType e)
{
	Node *p = (Node*)malloc(sizeof(Node)); //创建一块空间并赋值给指针
	p->data = e; //数据域
	tail->next = p;
	p->next = NULL; //使新创建的p节点成为新的尾节点 
	return p;
}

//在指定位置插入数据
int insertNode(Node *L,int pos,ElemType e)
{
	//用来保存插入位置的前驱节点
	Node *p = L;
	int i = 0;
	//遍历这个链表找到插入位置的前驱节点
	while(i < pos - 1)  
	{
		p = p->next;
		i++;
		if(p == NULL)
		{
			return 0;
		}
	}
	
	Node *q = (Node*)malloc(sizeof(Node)); //要插入的新节点
	q->data = e;
	q->next = p->next; //插入位置前一个节点的下一个位置赋值给新节点的下一个位置
	p->next = q; //把新节点赋值给前一个位置的next
	return 1;
}

int main(int argc,char const *argv[])
{
	Node *list = initList(); //初始化链表
	Node *tail = get_tail(list);
	tail = insertTail(tail,10);
	tail = insertTail(tail,20);
	tail = insertTail(tail,30);
	listNode(list);
	insertNode(list,2,15);
	listNode(list); s
	return 0;
}

运行:

原理:

②先找到插入位置之前的那个位置,然后指向那个位置所指向的下一个位置

(先找到70,再指向80)

③70指向新的节点

6.删除节点

cpp 复制代码
#include<stdio.h>
#include <string.h>
#include<stdlib.h>
#define MAXSIZE 100

typedef int ElemType;

//链表存储结构
typedef struct node{
	ElemType data;
	struct node *next;
}Node;

//单链表初始化
Node* initList()
{
	Node *head = (Node*)malloc(sizeof(Node));
	head->data = 0;
	head->next = NULL;
	return head;
}

//单链表头插法
int insertHead(Node* L,ElemType e)
{
	Node *p = (Node*)malloc(sizeof(Node));
	p->data = e;
	p->next = L->next;
	L->next = p;
	return 1;
}

//单链表遍历
void listNode(Node* L)
{
	Node *p = L->next; //第一个节点赋值给p指针
	while(p != NULL) //p不为空,即链表有数据
	{
		printf("%d ",p->data); //输出数据
		p = p->next; //指向下一个节点
	}
	printf("\n");
}

//获取尾节点的地址
Node* get_tail(Node *L)
{
	Node *p = L; //把L链表赋值给p指针
	while(p->next != NULL)
	{
		p = p->next;
	}
	return p;
}

//单链表尾插法
Node* insertTail(Node *tail,ElemType e)
{
	Node *p = (Node*)malloc(sizeof(Node)); //创建一块空间并赋值给指针
	p->data = e; //数据域
	tail->next = p;
	p->next = NULL; //使新创建的p节点成为新的尾节点 
	return p;
}

//在指定位置插入数据
int insertNode(Node *L,int pos,ElemType e)
{
	//用来保存插入位置的前驱节点
	Node *p = L;
	int i = 0;
	//遍历这个链表找到插入位置的前驱节点
	while(i < pos - 1)  
	{
		p = p->next;
		i++;
		if(p == NULL)
		{
			return 0;
		}
	}
	
	Node *q = (Node*)malloc(sizeof(Node)); //要插入的新节点
	q->data = e;
	q->next = p->next; //插入位置前一个节点的下一个位置赋值给新节点的下一个位置
	p->next = q; //把新节点赋值给前一个位置的next
	return 1;
}

//删除指定位置的节点
int deleteNode(Node *L,int pos)
{
	Node *p = L; //要删除节点的前驱
	int i = 0;
	//遍历链表,找到要删除节点的前驱
	while(i < pos - 1)
	{
		p = p->next;
		i++;
		if(p == NULL)
		{
			return 0;
		}
	}
	if(p->next == NULL)
	{
		printf("要删除的位置错误\n");
		return 0;
	}
	
	Node *q = p->next; //q指向要删除的节点
	p->next = q->next; //让要删除节点的前驱指向要删除节点的后继
	free(q); //释放要删除节点的内存空间
	return 1;
}

int main(int argc,char const *argv[])
{
	Node *list = initList(); //初始化链表
	Node *tail = get_tail(list);
	tail = insertTail(tail,10);
	tail = insertTail(tail,20);
	tail = insertTail(tail,30);
	listNode(list);
	insertNode(list,2,15);
	listNode(list); 
	deleteNode(list,2);
	listNode(list);
	return 0;
}

运行:

原理:

①删除70这个节点

②找到要删除节点的前置节点p

③用指针q记录要删除的节点

④通过改变p的后继节点实现删除

(原本p指向的是70,现在使70直接指向80即可)

⑤释放删除节点的空间

free(q);

7.获取链表长度

cpp 复制代码
#include<stdio.h>
#include <string.h>
#include<stdlib.h>
#define MAXSIZE 100

typedef int ElemType;

//链表存储结构
typedef struct node{
	ElemType data;
	struct node *next;
}Node;

//单链表初始化
Node* initList()
{
	Node *head = (Node*)malloc(sizeof(Node));
	head->data = 0;
	head->next = NULL;
	return head;
}

//单链表头插法
int insertHead(Node* L,ElemType e)
{
	Node *p = (Node*)malloc(sizeof(Node));
	p->data = e;
	p->next = L->next;
	L->next = p;
	return 1;
}

//单链表遍历
void listNode(Node* L)
{
	Node *p = L->next; //第一个节点赋值给p指针
	while(p != NULL) //p不为空,即链表有数据
	{
		printf("%d ",p->data); //输出数据
		p = p->next; //指向下一个节点
	}
	printf("\n");
}

//获取尾节点的地址
Node* get_tail(Node *L)
{
	Node *p = L; //把L链表赋值给p指针
	while(p->next != NULL)
	{
		p = p->next;
	}
	return p;
}

//单链表尾插法
Node* insertTail(Node *tail,ElemType e)
{
	Node *p = (Node*)malloc(sizeof(Node)); //创建一块空间并赋值给指针
	p->data = e; //数据域
	tail->next = p;
	p->next = NULL; //使新创建的p节点成为新的尾节点 
	return p;
}

//在指定位置插入数据
int insertNode(Node *L,int pos,ElemType e)
{
	//用来保存插入位置的前驱节点
	Node *p = L;
	int i = 0;
	//遍历这个链表找到插入位置的前驱节点
	while(i < pos - 1)  
	{
		p = p->next;
		i++;
		if(p == NULL)
		{
			return 0;
		}
	}
	
	Node *q = (Node*)malloc(sizeof(Node)); //要插入的新节点
	q->data = e;
	q->next = p->next; //插入位置前一个节点的下一个位置赋值给新节点的下一个位置
	p->next = q; //把新节点赋值给前一个位置的next
	return 1;
}

//删除指定位置的节点
int deleteNode(Node *L,int pos)
{
	Node *p = L; //要删除节点的前驱
	int i = 0;
	//遍历链表,找到要删除节点的前驱
	while(i < pos - 1)
	{
		p = p->next;
		i++;
		if(p == NULL)
		{
			return 0;
		}
	}
	if(p->next == NULL)
	{
		printf("要删除的位置错误\n");
		return 0;
	}
	
	Node *q = p->next; //q指向要删除的节点
	p->next = q->next; //让要删除节点的前驱指向要删除节点的后继
	free(q); //释放要删除节点的内存空间
	return 1;
}

//获取链表长度
int listLength(Node *L)
{
	Node *p = L;
	int len = 0;
	//从头节点循环到尾节点
	while(p != NULL)
	{
		p = p->next;
		len++;
	}
	return len;
}

int main(int argc,char const *argv[])
{
	Node *list = initList(); //初始化链表
	Node *tail = get_tail(list);
	tail = insertTail(tail,10);
	tail = insertTail(tail,20);
	tail = insertTail(tail,30);
	listNode(list);
	insertNode(list,2,15);
	listNode(list); 
	deleteNode(list,2);
	listNode(list);
	printf("%d\n",listLength(list));
	return 0;
}

运行:

8.释放链表(除头节点之外的所有内容全都释放)

cpp 复制代码
#include<stdio.h>
#include <string.h>
#include<stdlib.h>
#define MAXSIZE 100

typedef int ElemType;

//链表存储结构
typedef struct node{
	ElemType data;
	struct node *next;
}Node;

//单链表初始化
Node* initList()
{
	Node *head = (Node*)malloc(sizeof(Node));
	head->data = 0;
	head->next = NULL;
	return head;
}

//单链表头插法
int insertHead(Node* L,ElemType e)
{
	Node *p = (Node*)malloc(sizeof(Node));
	p->data = e;
	p->next = L->next;
	L->next = p;
	return 1;
}

//单链表遍历
void listNode(Node* L)
{
	Node *p = L->next; //第一个节点赋值给p指针
	while(p != NULL) //p不为空,即链表有数据
	{
		printf("%d ",p->data); //输出数据
		p = p->next; //指向下一个节点
	}
	printf("\n");
}

//获取尾节点的地址
Node* get_tail(Node *L)
{
	Node *p = L; //把L链表赋值给p指针
	while(p->next != NULL)
	{
		p = p->next;
	}
	return p;
}

//单链表尾插法
Node* insertTail(Node *tail,ElemType e)
{
	Node *p = (Node*)malloc(sizeof(Node)); //创建一块空间并赋值给指针
	p->data = e; //数据域
	tail->next = p;
	p->next = NULL; //使新创建的p节点成为新的尾节点 
	return p;
}

//在指定位置插入数据
int insertNode(Node *L,int pos,ElemType e)
{
	//用来保存插入位置的前驱节点
	Node *p = L;
	int i = 0;
	//遍历这个链表找到插入位置的前驱节点
	while(i < pos - 1)  
	{
		p = p->next;
		i++;
		if(p == NULL)
		{
			return 0;
		}
	}
	
	Node *q = (Node*)malloc(sizeof(Node)); //要插入的新节点
	q->data = e;
	q->next = p->next; //插入位置前一个节点的下一个位置赋值给新节点的下一个位置
	p->next = q; //把新节点赋值给前一个位置的next
	return 1;
}

//删除指定位置的节点
int deleteNode(Node *L,int pos)
{
	Node *p = L; //要删除节点的前驱
	int i = 0;
	//遍历链表,找到要删除节点的前驱
	while(i < pos - 1)
	{
		p = p->next;
		i++;
		if(p == NULL)
		{
			return 0;
		}
	}
	if(p->next == NULL)
	{
		printf("要删除的位置错误\n");
		return 0;
	}
	
	Node *q = p->next; //q指向要删除的节点
	p->next = q->next; //让要删除节点的前驱指向要删除节点的后继
	free(q); //释放要删除节点的内存空间
	return 1;
}

//获取链表长度
int listLength(Node *L)
{
	//头节点赋值给p
	Node *p = L;
	int len = 0;
	//从头节点循环到尾节点
	while(p != NULL)
	{
		p = p->next;
		len++;
	}
	return len;
}

//释放链表
void freeList(Node *L)
{
	Node *p = L->next; //头节点的next赋值给p指针
	Node *q; //声明一个新节点
	
	while(p != NULL)
	{
		q = p->next;
		free(p);
		p = q;
	}
	L->next = NULL;
}

int main(int argc,char const *argv[])
{
	Node *list = initList(); //初始化链表
	Node *tail = get_tail(list);
	tail = insertTail(tail,10);
	tail = insertTail(tail,20);
	tail = insertTail(tail,30);
	listNode(list);
	insertNode(list,2,15);
	listNode(list); 
	deleteNode(list,2);
	listNode(list);
	printf("%d\n",listLength(list));
	freeList(list);
	printf("%d\n",listLength(list));
	return 0;
}

运行:

原理:

①指针p指向头节点后的第一个节点

②判断指针p是否指向空节点

③如果p不为空,用指针q记录指针p的后继节点

④释放指针p指向的节点

⑤指针p和指针q指向同一个节点,循环上面的操作

相关推荐
☆璇43 分钟前
【数据结构】栈和队列
c语言·数据结构
chao_7894 小时前
回溯题解——子集【LeetCode】二进制枚举法
开发语言·数据结构·python·算法·leetcode
秋说4 小时前
【PTA数据结构 | C语言版】将数组中元素反转存放
c语言·数据结构·算法
qqxhb5 小时前
零基础数据结构与算法——第四章:基础算法-排序(中)
数据结构·算法·排序算法·归并·快排·堆排
木叶丸6 小时前
编程开发中,那些你必须掌握的基本概念
前端·数据结构·编程语言
Y1nhl7 小时前
力扣_链表_python版本
开发语言·python·算法·leetcode·链表·职场和发展
手握风云-8 小时前
优选算法的链脉之韵:链表专题
数据结构·算法·链表
老虎06279 小时前
数据结构(Java)--位运算
java·开发语言·数据结构
小汉堡编程13 小时前
数据结构——vector数组c++(超详细)
数据结构·c++
雾里看山16 小时前
顺序表VS单链表VS带头双向循环链表
数据结构·链表