数据结构
数据结构是为了提高程序的简洁性、高效性。
数据结构研究的是数据的逻辑结构、存储结构及其操作。
逻辑结构:
1对1的线性关系;1对多的树形关系;多对多的网状结构;
存储结构:
顺序存储—顺序表;链式存储—链表;索引存储;hash存储—hash表;
操作:
创建、插入、显示、删除、修改、查找
1、存储结构
1.1顺序表
特点
- 顺序并且连续存储,访问方便。
- 大小固定,无法修改。
- 表满不能存,表空不能取。
数据元素类型
typedef int data_type;//数据类型
typedef struct shop
{
数据;
}datatype;
顺序表类型
typedef struct list
{
data_type arr[];
int count;
}
顺序表的创建
顺序表的插入
顺序表的删除
顺序表的显示
顺序表的销毁
注:顺序表的销毁需要二级指针,因为需要将指向表头的指针在指针被销毁后指向NULL区域,放置变成野指针。
1.2链表
链表的分类:
有没有头结点:带头结点的链表、不带头结点的链表。
指针域是双向还是单向的:单向链表、双向链表。
尾结点是否指向头结点:循环链表、不循环链表。
特点:
- 申请的空间可以不连续。
- 访问不方便。
- 插入、删除不需要移动元素。
1.2.3单向链表
数据元素类型:
typedef int data_type;
结构体类型:
typedef struct linkNode
{
数据域;
指针域;
}Link;
链表的创建
链表的插入
头插法:
尾插法:
中间插入:
链表的显示
链表的删除
头删法:
尾删法:
中间删除:
销毁链表
1.2.4双向链表
数据元素类型:
typedef int data_type;
结构体类型:
typedef struct linkNode
{
指针域;
数据域;
指针域;
}Link;
链表的创建
插入链表
删除链表
1.3特殊的线性表
1.3.1栈
特征
- 限制在一端进行插入操作和删除操作的线性表。
- 先入后出
定义顺序栈
数据元素类型:
typedef int data_type;
结构体类型:
typedef struct linkNode
{
data_type arr[];
int top;//存储栈顶下标
}Link
创建顺序栈
入栈
出栈
定义链式栈
数据元素类型:
typedef int data_type;
结构体类型:
typedef struct node
{
data_type data;
struct node *next;//存储栈顶下标
}linkstack;//链式类型定义
1.3.2队列
特征:
- 队列允许在两端进行操作,在队尾插入,在队头删除。
- 先进先出
顺序队列
数据元素类型:
typedef int data_type;
结构体类型:
typedef struct node
{
data_type arr[];
int front;//队头的下标
int rear;//队尾的下标
}Queue;//链式类型定义
创建队列
进入队列
取出队列
2、树
树(Tree)是 n(n≥0)个节点的有限集合T,它满足两个条件 :
- 有且仅有一个特定的称为根(Root)的节点;
- 其余的节点可以分为m(m≥0)个互不相交的有限集合T1、T2、……、Tm,其中每一个集合又是一棵树,并称为其根的子树(Subtree)。
度数:一个节点的子树的个数称为该节点的度数,一棵树的度数是指该树中节点的最大度数。
深度:节点的层数等于父节点的层数加一,根节点的层数定义为一。树中节点层数的最大值称为该树的高度或深度。
边数:一个节点系列k1,k2, ……,ki,ki+1, ……,kj,并满足ki是ki+1的父节点,就称为一条从k1到kj的路径,路径的长度为j-1,即路径中的边数。
2.1二叉树
定义:二叉树(Binary Tree)是n(n≥0)个节点的有限集合,它或者是空集(n=0),或者是由一个根节点以及两棵互不相交的、分别称为左子树和右子树的二叉树组成。 - 二叉树第i(i≥1)层上的节点最多为2i-1个。
- 深度为k(k≥1)的二叉树最多有2k-1个节点。
- 在任意一棵二叉树中,树叶的数目比度数为2的节点的数目多1。
满二叉树
深度为k时,有2k-1个结点的二叉树。
完全二叉树
只有最下面两层有度数小于2的节点,且最下面一层的叶节点集中在最左边的若干位置上。
2.2二叉树存储
顺序存储
链式存储
定义二叉树
数据元素类型:
typedef int data_type;
结构体类型:
typedef struct linkNode
{
指针域;
数据域;
指针域;
}bitree;
二叉树节点插入(递归进行实现)
二叉树遍历
哈夫曼树
结点的带权路径长度指的是从树根到该结点的路径长度和结点上权的乘积。树的带权路径长度是指所有叶子节点的带权路径长度之和,记作 WPL 。WPL最小的二叉树就是最优二叉树,又称为哈夫曼树。
- 算法
程序就是算法和数据结构的组合。
算法:一个有穷规则(或者语句、指令)的有序集合。
算法的设计: 取决于选定的逻辑结构 (1对1(线性表),1对多(树),多对多)
算法的实现: 依赖于采用的存储结构 (顺序存储,链式存储,索引存储,散列存储)
4.1算法的特性 - 有穷性 —— 算法执行的步骤(或规则)是有限的;
- 确定性 —— 每个计算步骤无二义性;
- 可行性 —— 每个计算步骤能够在有限的时间内完成;
- 输入 —— 算法有一个或多个外部输入;
- 输出 —— 算法有一个或多个输出;
4.2如何评价一个算法的好坏
1、时间复杂度
算法消耗时间,它是问题规模的函数 T(n) - 根据位置规模n写出表达式 f(n)=n^2/2+n/2
- 如果有常数项,将其置为1 (当f(n)表达式中只有常数项的时候,有意义)
- 只保留最高项,其他项舍去 f(n)=n^2/2
- 如果最高项系数不为1,将其置为1 f(n)=n^2
- T(n)=O(n^2) ---->平方级
2、空间复杂度
3、容易理解、容易编程和调试、容易维护。
4、查找算法
1.顺序查找
2.二分查找/折半查找(必须在有序序列中查找)
3.Hash查找
4.解决冲突的办法
①线性探查法
② 链地址法
5.Hash相关操作
数据类型定义
typedef int data_type;
结构体类型:
typedef struct linkNode
{
数据域;
指针域;
}LinkNode;
typedef struct linkNode
{
指针数组的首地址;
表的大小;
}Hash;
创建Hash表
查找hash表
插入hash表
排序算法
- 快速排序
- 插入排序
- 快速排序(递归排序)
IO
linux下的文件属性
普通文件:-
目录文件:d
符号链接文件:l
字符设备文件:c
块设备文件:b
管道文件:p
套接字文件:s
标准IO
概念
通过文件流(FILE*)操作文件,打开文件时,系统会自动将文件信息定义为结构体struct FIEL。因此可以通过FIEL *指针操作文件。
C标准,只要支持c库即可运行
因此,移植性很高。
操作的一般是普通文件
拥有输出输入缓冲区,减少切换操作,属于高级IO。
流
文件打开时创建的结构体被称为“流”。
1、有源头:APP
2、有目的性:缓冲区
3、持续性:不断放入数据到缓冲区
默认流:stdin (标准输入)、stdout(标准输出)、stderr(标准出错)。
其余文件流:最多1021个
缓冲机制
全缓冲:缓冲区放满、程序结束都会强制刷新缓冲区
行缓冲:缓冲区放满、程序结束、强制刷新,遇到换行符也会刷新缓冲区。
不带缓冲:不存在缓冲区。eg:stderr。
操作函数
fopen、fclose
fopen打开文件流
fopen(“文件名”,“打开方式”)返回打开的文件流结构体指针。
r或rd:只读文件,文件必须存在
r+或r+d:读写文件,文件必须存在
w或wd:只读文件,文件可以不存在,若存在则刷新其中内容,若不存在则创建该文件。
w+或w+d:读写文件,其余同w。
a或ad:只读文件,文件可以不存在,若存在则在文件末尾新添内容。若文件不存在,则会创建该文件。
a+或a+d:读写文件,其余同a。
fclose关闭文件流
fgetc、fputc
fgetc从文件流中获取一个字符
fgetc(“文件流指针”)返回字符的ASCII值
fputc输出一个字符到指定文件流
fputs(“ASCII值”,”文件流指针“)
fgets、fputs
获取一个字符串从指定的文件流中
fgets(”缓冲区“,”读取长度“,”源文件流“)
返回存储内容空间首地址
1、遇到\n
2、读取带size-1时,返回
fputs输出一个字符串到指定文件流
fputs(”缓冲区“,”输入长度“,”目标文件流“)
fread、fwrite
fread从源文件流读取一块内容
fread(”读取内容存储地址“,”每读取大小“,”读取数量“,”源文件流“)
每次读取大小推荐设置为:1
每次读取数量:sizeof(变量)
返回成功读取的块的个数
fwrite将一块内容写入一个文件流
fwrite(“需要写入的内容的地址”,“每次写入的大小”,“每次写入的数量”,“目标文件流”)
特性函数
ftell():返回当前的文件位置。
rewind();将设定流的位置置于文件首。
fseek(“文件流指针”,“偏移量”,“位置设置”)
位置设置:
SEEK_SET:置于文件首部。
SEEK_CUR:置于当前位置。
SEEK_END:置于文件末尾。
将文件位置置于想要的位置。
文件IO
操作函数
open
打开指定文件流
open(“文件路径指针”,“操作参数”,“设置权限”)
返回一个该文件的标识符,为int类型
flags:
主标志:
O_RDONLY:只读方式打开。
O_WRONLY:只写方式打开
O_RDWR:读写方式打开
以上三个参数互斥。
副标志:
O_CREAT:若文件不存在则创建一个新文件夹,并用第三参数设置权限。
O_EXCL:若文件存在创建失败,则返回其错误信息。
O_NOCTTY:如果欲打开的文件为终端机设备时, 则不会将该终端设备当成进程控制终端机
O_TRUNC:若文件已经存在,则刷新文件中原有信息
O_APPEND:若文件已存在,在原有文件的末尾追加信息
权限设置使用8进制法设置。
close
关闭文件流
close(“文件标识符”)
read
从指定的文件流里读取内容
read(“要读取文件标识符”,“读取到的内容首地址”,“读取个数”)
返回读取的字节个数
若读到文件结束则返回0
write
将指定的内容写到目标的文件流
write(“要去写入文件标识符”,“写入的内容的首地址”,“写入的个数”)
返回写入的字节个数
若写入完成则返回0
lseek
lseek(“文件标识符”,“偏移量”,“位置设置”)
位置设置同fseek
空洞文件
空洞文件的创建
1、以只写或读写方式打开文件
2、通过lseek或fseek移动到指示位置
3、移动完后末尾写入一个字节
4、关闭文件
操作目录
opendir
打开一个目录
返回一个DIR * 目录流指针作为readdir的参数
readdir
操作目录readir(DIR drip)
返回值为struct dirent指针,即所要读取的目录的信息
closedir
关闭文件流
返回一个int类型的数据
测试文件属性
stat
获取文件的属性
stat(“文件路径”,“接收信息的结构体(struct stat *)”)
若文件路径为软连接文件,则返回软连接指向的文件属性
fstat
获取文件属性
fstat(“文件标识符”,“接收信息的结构体”)
lstat
获取文件属性
lstate(“文件路径”,“接收信息的结构体”)
若文件路径为软连接文件,则返回软连接文件的属性
库
是一些成熟的,已经完成的代码,可以帮助人们减少代码的开发量。但是在使用期间必须遵守许可协议。
静态库
优点:在编译时期即完成链接,程序运行时无需加载库,运行速度更快。
缺点:占用更多的磁盘和内存空间,静态库升级后需要重新编译链接。
动态库
优点:程序运行时才加载链接,因此代码中不包含库中代码,体积较小,库升级方便,无需重新编译。
缺点:运行时需要加载共享库