一、数据结构概述
1、什么是数据结构
数据结构是计算机科学中的一种组织、管理和存储数据的方式,旨在高效地进行数据的 访 问、插入、删除和处理。不同的数据结构适用于不同的问题,提高算法的性能和效率。
2、数据结构
1.逻辑结构
- 线性结构:表(数据之间位一对一的关系)
- 非线性结构:树(数据间为一对多的关系),图(数据间为多对多的关系)
2.存储结构
存储结构有顺序存储(数组)、链式存储(链表)、散列存储(哈希表)、索引存储(数、图)
3.常见数据结构
顺序表 | 链式表 | 顺序栈 | 链式栈 | 二叉树 |
顺序队列 | 链式队列 | 邻接表 | 邻接矩阵 |
二、链表
1、概念
链表是一种常见的线性数据结构,由一系列节点组成。每个节点包含数据部分和指向下一个节点的指针(或引用)
2、链表与顺序表的特点对比
数据结构 | 特点 |
顺序表 |
存储数据连续 访问元素方便 无法利用小空间,不需用连续的大空间 顺序表的元素是有限的 插入和删除效率低 |
链表 |
存储空间不需要连续 可以利用一些小的存储空间 访问元素不方便 链表元素可以没有上限 插入和删除效率高 |
3、链表分类
-
单向链表:只能通过链表节找到后一个链表,访问链表元素的方向是单向的
-
双向链表:能通过链表节找到前一个与后一个链表节,访问链表元素是单向的
-
循环链表:在双向链表的基础上,能通过第一个链表节找到最后一链表节,也能通过最后一个链表节找到第一个链表节,实现链表元素的循环访问
-
有头链表:第一个节点不存放数据,操作较简单(二级指针使用少)
-
无头链表:第一个节点存放数据,操作较复杂(要用到较多的二级指针)
三、单向链表及其相关操作(以有头链表为例)
1、定义链表节点类型
如下代码所示,定义链表节点类型 linknode 其中 data 存放链节的数据,*pnext 存放下一个链节的地址
typedef 数据类型 datatype;
typedef struct node{
datatype data;
struct node *pnext;
}linknode;
2、空链表的创建
空链表要求:
data不需要赋值(最好赋值),空白节点不存放数据,主要为了保证链表操作的便利性
pnext必须赋值为NULL,表示该节点为最后一个节点
实现代码:
linknode *create_emp_linklist(void)
{
linknode *tmplist = NULL;
tmplist = malloc(sizeof(linknode)); //向堆区申请空间
if(NULL == tmplist) //判断是否申请成功
{
return NULL;
}
tmplist->pnext = NULL; //最后一个链表的标志
return tmplist;
}
3、链表的插入
头插法思路:
- 创建新节点
- 为新节点赋值,pnext 等于头节点中存储的下一个节点地址
- 更新链表头节点中地址,使其指向新节点
头插法代码实现:
int insert_linklist(linknode *phead,datatype tmp)
{
linknode *tmplist = NULL;
tmplist = malloc(sizeof(linknode));
if(NULL == tmplist)
{
printf("error");
return -1;
}
tmplist->pnext = phead->pnext; //插入链节下一个链接指向前一链节初始指向
tmplist->data = tmp; //给插入链结赋值
phead->next = tmplist; //前一链节指向插入链结
}
尾插法思路:
- 创建新节点
- 为新节点赋值,新节点中 pnext 指向NULL,data 等于所赋初值
- 找到尾节点,将其 next 指向修改为新节点
尾插法代码实现:
void insert_tail(linknode *phead,datatype tmp)
{
linknode *ptail = NULL;
linknode *ptmplist = NULL;
ptmplist = malloc(sizeof(linknode)); //创建新节点
if(ptmplist == NULL)
{
printf("error");
}
ptmplist->data = tmp; //为新节点赋值
ptmplist->pnext = NULL;
ptail = phead; //找到尾节点
while(ptail->pnext != NULL)
{
ptail = ptail->next;
}
ptail->next = ptmplist; //修改节点指向
}
4、链节数据显示
思路:
通过前一链结中存储的下一链节地址找到下一个地址中数据并打印
代码实现:
void show_listdata(linknode *phead)
{
linknode *tmplist = NULL;
tmplist = phead;
while(tmplist != NULL) //判断是否到达尾链节
{
printf("%d ",tmplist->data);
tmplist = tmplist->pnext;
}
}
5、链节的删除
思路:
- 定义前驱节点找到需要删除的节点
- 定义定位节点跟在前驱节点后,在释放节点空间后为前驱节点复位
代码实现:
//tmp:要删除的数据
int delete_linklist(linknode *phead,datatype tmp)
{
linknode *prelist = NULL;
linknode *loclist = NULL;
prelist = phead->pnext;
loclist = phead;
while(prelist != NULL)
{
if(prelist->data == tmp)
{
loclist->pnext = prelist->pnext; //定位下一个链节地址
free(prelist); //删除目标节点
prelist = loclist->pnext; //复位先驱节点
}
else
{
prelist = prelist->pnext;
loclist = loclist->pnext;
}
}
}
6、链表的销毁
思路:
-
定义两个指针 ploct 和 pfree,ploct用于定位,pfree 用于空间释放
-
让两个指针都指向头节点,让 ploct 节点等于其指向的节点,释放 pfree 所在的节点空间
-
让 pfree指向节点 等于 ploct指向借点
-
重复前两步,ploct 指向NULL
代码实现:
void destory_lintnode(**pphead)
{
listnode *ploct = NULL;
listnode *pfree = NULL;
ploct = *pphead;
pfree = *pphead;
while(ploct != NULL)
{
ploct = ploct->pnext;
free(pfree); //节点空间释放
pfree = ploct;
}
*phead = NULL;
return;
}