这几天开始了对数据结构的学习,内容较多较杂,理解起来也颇为困难,故记录下学习中的笔记
数据结构是一门研究数据之间关系和操作 的学科,而非计算方法
数据结构的基本概念
数据结构的三个方面
逻辑结构和存储结构
集合:数据元素同属于一个集体,但元素之间没有任何关系
线性结构:数据元素之间存在一对一关系
树形结构:数据元素之间存在一对多关系(倒悬树)
图型结构:数据元素之间存在多对多关系(地铁图)
数据的物理结构
顺序结构:数据元素存储在连续的内存中,用数据元素的相对位置来表示关系
优点:能够随机访问(想访问那个就访问那个) 访问效率极高(速度快)
缺点:空间利用率低,对内存要求比较高,插入、删除不方便
链式结构:数据元素存储在彼此独立的内存空间中,每个独立的元素也叫节点,每个数据元素中增加一个数据项用于存储其他元素的地址,用来表示元素之间的关系
优点:插入删除非常方便,空间利用率高
缺点:不能随机访问(只能由前到后逐个访问)
逻辑结构和物理结构的对应关系
表 顺序 链式
树 链式 顺序
图 顺序+链式
每种逻辑结构采用什么物理结构存储并没有明确的规定,通常根据实际的难易程度以及空间、时间方面的要求,来选择最合适的物理存储结构,也有可能采用多种物理结构复合存储
数据结构的和运算
1 建立数据结构 create
2 销毁数据结构 destory
3 清空数据结构 clean
4 数据结构排序 sort
5 删除元素 delete
6 插入元素 insert
7 访问元素 access
8 修改元素 modify
9 查询元素 query
10遍历数据结构 ergodic (show printf)
顺序表和链式表的实现
顺序表:
数据项
1存储的内存首地址
2表的容量
3元素的数量
运算:
创建 销毁 清空 插入 删除 访问 修改 查询 排序 遍历
注意
1 越界问题(不要越界)
2 要保持元素的连续性
优点:支持随机访问,修改 查询 排序 效率高,大块连续的内存不易产生内存碎片
缺点:对内存的要求比较高(内存连续、大块内存) 插入删除元素时不方便 且效率低
链式表:
元素的数据项:
数据域:可以是各种类型的若干个数据项
指针域:指向下一个元素
由若干个元素通过指针域链接在一起形成链式表
不带头节点:第一个元素存储的数据域就是有效的数据
添加删除是可以修改头节点指针,参数需要使用二级指针
同时需要获取到上一个节点的指针,而头节点没有上一个节点
带头节点:第一个元素只代表头 不使用
进行插入、删除操作时会比不带头节点的链表方便
注意:其他操作时要从第二个节点开始
对表的结构加以限制,形成特殊的表结构
四种顺序栈
top 初值 0 入栈 top++ 空增栈
top 初值 -1 top++ 入栈 满增栈
top 初值max-1 入栈 top-- 空减栈
top 初值max top-- 入栈 满减栈
先进后出
顺序栈:
数据域:
存储元素的内存首地址
栈的容量
栈顶位置
运算:
创建、入栈、出栈、栈满、栈空、栈顶、销毁
链式栈:
数据域
栈顶
节点数量
运算
创建、销毁
栈的应用:
1 函数的调用(栈内存)
2 生产者与消费者模型(栈当做仓库)
3 表达式解析(中缀表达式后缀表达式)
常见笔试面试题
某序列为栈的入栈顺序,某序列是否是出栈顺序(边入边出)
1 2 3 4 5
3 1 2 4 5假!
实现一个函数,判断序列b是否是a的出栈顺序
bool is_popstack(int* a,int* b,size_t len);
{
// 创建一个栈
// 按a的顺序入栈
// 按b的顺序出栈
// 最后判断如果栈为空,则b是a的出栈顺序
}
bool is_popstack(int* a,int* b,size_t len)
{
// 创建一个栈
ArrayStack* stack = create_array_stack(len);
// 按a的顺序入栈
for(int i=0,j=0; i<len; i++)
{
push_array_stack(stack,a[i]);
// 按b的顺序出栈
int val = 0;
while(top_array_stack(stack,&val) && val==b[j])
{
pop_array_stack(stack);
j++;
}
}
// 最后判断如果栈为空,则b是a的出栈顺序
bool flag = empty_array_stack(stack);
destory_array_stack(stack);
return flag;
}
顺序队列:
由一维数组+队头位置+队尾位置rear 组成,入队时rear+1,出队时front+1 为了让队列反复使用要把一维数组想象成环形.为了让rear和front加1后要用链表的容量求余
rear = rear+1 % cal
front = front+1 % cal
如何判断队列为空: front == rear
如何判断队列为满: front == rear+1
代价就是要空一个位置不能用,或者再添加一个数据项用来标记是否是空还是满
如何计算元素的数量:
(rear-front+cal)%cal
数据项:
存储元素的内存首地址
队头指针
队尾 即将入队的位置
容量
运算:创建、销毁、入栈、出栈、队空、队满、队头、队尾、元素数量
链式队列
由若干个节点组成的队列
数据项:
队头指针
队尾指针
节点数量
运算:创建、销毁、队空、(链式一般不满) 、入队、出队、队头、队尾、数量
队列应用:
1 消息排队
2 树的层序遍历
3 图的广度优先遍历
4 封装线程池、数据池
常见的笔试面试题:
使用两个栈来模拟一个队列的功能
从栈1到栈2一个不留
如栈2不空则栈1不到栈2
尾添加效率低,非法下标的判断效率也非常低
数据项:
头指针
尾指针
节点数量
节点:
数据域
游标
静态链表的节点存储在连续的内存,通过游标来访问下一个节点
这种链表在插入删除时只需要修改游标的值,而不用申请、释放内存达到一种链式结构
但是也牺牲了随机访问的功能,是给没有指针的编程语言实现的一种单链表
节点:
前驱指针
数据域
后驱指针
数据项:
头节点
节点数量
// 逆序输出
void anti_show_list(Node* head)
{
Node* n1 = head;
Node* n2 = head->next;
for(Node* n3=NULL;NULL == n2;n2=n2->next)
{
n1->next = n3;
n3=n1;
n1=n2;
}
}
数组:存储空间链接的表结构
矩阵:带二维信息的数据,一般使用二维数据来存储矩阵
特殊矩阵
上三角形矩阵:
[1][2][3][4]
[ ][5][6][7]
[ ][ ][8][9]
[ ][ ][ ][x]
压缩方法:用一维数组进行存储 (1 25 368 479x)
数组长度 (n+1)*n/2
对应关系 (j+1)×j/2+i
i和j要满足 i<=j
下三角形矩阵:
[x][ ][ ][ ]
[x][x][ ][ ]
[x][x][x][ ]
[x][x][x][x]
数组长度 (n+1)*n/2
对应关系 (i+1)*i/2+j
i和j要满足 j<=i
对称矩阵:沿着(0,0) (1,1) (2,2)...对称
压缩方法:用一维矩阵存储把他当做上三角或下三角
对角矩阵:(带状矩阵)沿着(0,0) (1,1) (2,2)...对角线两边有数据
用一维数组存储
数组长度 3*n-2
对应关系 2*i+j
i和j要满足 abs(i-j) <= 1
稀疏矩阵:有效信息不多,绝大多数都是无效信息,不需要存储,这种二维数组,没有特定的标准,全凭感觉
压缩方式:
三元组
有三个数据项 行 列 值 构成一个整体存储在一维数组中
//十字链表
这些矩阵如果使用二维数组来存储的话,会非常浪费存储空间,为了节约空间,我们可以对这些矩阵进行压缩