数据结构_总结
数据结构是什么,是人话但可能不太准确的一种总结:数据结构是针对数组或链表的特定操作而已;各种数据结构实现的功能无非是增删改查而已。
主要内容包括 线性表(栈、队列、串、广义表等)、树、图、排序,其中后三者是重点难点。
1、数据结构
数据结构:具有结构的数据元素的集合。
分类:
逻辑结构(原理):数据元素间的逻辑关系。
1、集合
2、线性结构:一对一,除首末节点外前驱后继唯一,如栈、队列等
3、非线性结构:
层次结构——树(一对多)
网状结构——图(多对多)
物理结构或存储结构(实现):数据结构在计算机中的表示或映像。
1、顺序结构(数组):简单、占用内存少、查询快;须预先分配内存、插入删除慢。
2、非顺序结构:
链式结构(链表):单链表(线性链表)、双链表、循环链表、跳表、树的二叉链表、三叉链表等。不需预先分配内存、插入删除快;查询慢、由于有指针额外占用内存多。
索引
散列
数据结构的基本存储方式就是顺序和链式(索引和散列本质上也与链式类似)两种,基本操作就是增删查改,遍历方式无非就是迭代和递归。
算法:针对某个特定问题的有限指令集合。分为数值算法(方程等)和非数值算法(排序等)。从理论上说,任何递归算法都可以通过循环或利用堆栈、队列等转为非递归算法。
算法的设计取决于选定的逻辑结构,实现则依赖于采用的存储结构。
算法的描述:伪代码描述、程序描述、图描述、自然语言描述等。
算法五要素:输入、输出、有穷性(在有限步骤内结束)、确定性(每条指令无二义)、有效性(每条指令可执行)。
算法优劣的评价:时间、空间、简洁、可读性等。
程序:程序设计的本质是针对已确定的问题采用好的数据结构从而设计好的算法。程序=数据结构+算法。
2、线性表
线性表分类:
顺序存储:顺序表
链式存储:单链表或称线性链表(单向)、双向链表(双向)、循环链表。
数组
定义:下标与值的数偶的有限集合。
元素地址:(k为一个元素所占内存单位数)
一维:locate(ai)=locate(a1)+(i-1)*k
二维:(物理上按行存时)locate(aij)=locate(a11)+[(i-1)*n+j-1]*k,n为列数 或 (物理上按列存时)locate(aij)=locate(a11)+[(j-1)*m+i-1]*k,m为行数
方阵(n*n矩阵)的压缩存储(二维坐标映射成一维,用数组存储):
1、对称阵(上三角形或下三角形同样可用此法存):k= { i(i-1)/2+j-1, i≥j , j(j-1)/2+i-1, i<j },其中k∈[0, n(n+1)/2-1]、aij=aji(i,j∈[1,n])
2、对角阵:n阶m对角阵的元素个数为mn-(m2-1)/4,m为奇数; n阶三对角阵元素个数3n-2、k=2i+j-3、(i , j)=( (k+1)/3+1, (k+1)/3+(1+k)mod3 ),其中k从0起,i、j从1起。
3、稀疏阵:三元组(顺序存储)、十字链表法(链式存储)
串、广义表
特殊线性表:栈、队列
栈(亦称下推表)、队列
存储:顺序存储(顺序堆栈、顺序队列)、链式存储(链接堆栈、链接队列)。
基本操作(6个):初始化、判空、判满(针对顺序存储)、进、出、取头元素
(顺序存储有初始容量M限制所以会满、链式存储则不会,除非为循环队列。)
顺序存储的初始化 链式存储的初始化 何时空 何时满(顺序存储)
栈 top=-1 top=NULL top==-1或NULL top==M-1
队列 front=rear=-1 front=rear=NULL front==rear时 rear==M-1
循环队列 front=rear=0 front=rear=NULL front==rear时 (rear+1)%M==front,其实没有真满还剩一个空位
上述中,对于栈top表示栈顶元素的位置,对于队列rear表示队尾元素位置、front表示最近一个已取出的元素的位置(即队头元素的前面一个元素的位置)。
3、树
4、图
5、文件及查找与排序
文件:性质相同的记录组成的集合。根据记录类型的不同分为操作系统文件和数据库文件。
基本概念:属性、关键字、物理记录(计算机通过一条IO命令进行读写的基本数据单位)、逻辑记录、物理结构(即存储结构——逻辑记录在存储介质上的组织方式)、逻辑结构(逻辑记录间的逻辑关系)
基本操作:查找、维护(插入、删除、更新)、排序。维护操作依赖于查找操作。
顺序文件:文件记录在物理结构中的排列顺序与逻辑结构中的排列顺序一致的文件(记录的物理顺序与逻辑顺序一致的文件)。
| 顺序文件 |
| 逻辑 | 排序 | 一般 |
| 物理 | 连续 | 链接 |
四种顺序文件:排序连续文件、排序链接文件、(一般)连续文件、(一般)链接文件。
操作:只有排序连续文件能用二分查找,其他的只能顺序查找。一般连续顺序文件的有序化(转为排序连续顺序文件)即为后面要说的排序。
查找
关于查找一个记录时的比较次数(n为数据个数或记录条数):
(顺序存储):
顺序查找平均: (1+n)/2 ;
二分查找(要求序列有序,可通过判定树即该有序序列的平衡二叉查找树来分析需要的查找次数):平均 [(n+1)lg(n+1) ]/n-1 、最多次数为树高 ceil(lg(n+1)) ;参阅书P290
关于二叉查找算法可参阅https://www.cnblogs.com/z-sm/p/6589803.html
(链式存储):二叉查找树平均: (3n+2IPL)/(2n+1) ;参看特殊二叉树之二叉查找树-MarchOn
(索引):非稠密索引块的平均比较次数(块内顺序查找):(下一节介绍)
查找块时用顺序查找: (floor(n/s)+1 +1 )/2+(1+s)/2 =( floor(n/s)+s+1 )/2+1 ,可见当分块数为sqrt(n)时查找次数最少
查找块时用二分查找:lg(n/s+1)+(s+1)/2
(散列):散列表内部的平均查找次数:(下一节介绍)与装填因子正相关,与表长无关。
排序
定义:将文件的记录按记录关键字值递增或递减顺序重新组织,得到有序的文件记录。通常指的是连续顺序文件的排序,当然链接顺序文件也可;当记录只包含关键字时即为元素的排序。
分类:
分类法1:内排序、外排序(外排序用于数据量大而无法一次全装入内存的数据文件的排序,通常用归并法)。
分类法2:连续顺序文件排序、链接顺序文件排序
分类法3:稳定排序、不稳定排序:关键字值一样的文件记录在排序前后相对位置保持不变的排序是稳定排序。
内排序:见 数据结构_排序总结-MarchOn。
6、索引
索引文件=索引表+主表(或称基本文件)。索引表由计算机系统自动建立和维护,里面的索引项给出了主表中的记录的关键字值与该记录的存储位置间的对应关系。
分类:
稠密索引文件:稠密索引+基本文件。对基本文件的每条记录的关键字建立索引项
非稠密索引块文件:非稠密索引块+基本文件。将基本文件的记录排序后分块,对各块内关键字值最大的记录建立索引项。查找一个记录时的平均比较次数为( floor(n/s)+s+1 )/2+1(顺序查找索引块) 或lg(n/s+1)+(s+1)/2(二分查找索引块)
多级索引文件:二叉排序树多级索引 -> 多分树索引B树 -> 多分树索引B+树。
B树和B+树都是平衡多路查找树且自底向上增长(二叉查找树则是自顶向下增长的),其区别:
B树可以看成是二叉查找树的扩展只不过是多叉且平衡的,即每个节点都有关键字、关键字对应记录的存储位置信息、上层出现的关键字不会出现在下层、小于关键字的往左大于的往右;支持随机任意查找。
而B+树只在叶节点存记录的存储位置、非叶节点只作为叶节点的索引(或称路标)不存关键字对应记录的存储位置、上层出现的关键字下层还会出现、小于的左边大于等于的右边;支持随机任意查找、顺序查找。
一级索引、二级索引等:对主表的索引为一级索引、对一级索引的索引为二级索引,以此类推,如B、B+树中上层节点是对下层节点的索引。
7、散列
散列也称作哈希,英文名为Hash。
散列文件/哈希文件/杂凑文件:按照散列方法建立的数据文件。
散列值(散列地址)、散列表(散列值值域)、散列冲突
设计一个散列表包含三方面内容:
1、确定散列函数值域
2、构造散列函数,好的散列函数应使散列地址尽可能均匀分布在事先已知的散列空间上且函数尽可能简单。直接定址法、数字分析法、平方取中法、叠加法、基数转换法、取余法(最常用,除数最好为质数或不包含小于20的质数因子的合数)、随机数法。
3、选择冲突处理的有效办法。
开放定址法(散列表未满时取“下一个”空位置):线性探测再散列、二次探测再散列、伪随机探测再散列。
再散列法(用不同散列函数再求新散列地址)
链地址法(桶加链表,即java HashMap中采用的方法)
散列表内部的平均查找次数:与装填因子正相关,与表长无关。
Hash函数通常对于任意长度的输入数据都生成相同长度的Hash值。
工业界中使用的Hash算法通常追求的有 加密性(不可逆向破解)、高性能(计算Hash值速度快)、冲突率低(即Hash值分布比较均匀) 几点。其中第三点是必须的,而前两点通常比较难兼得,即要做到加密性则计算的速度通常比无加密性的算法慢。
工业界常见的Hash算法(生成固定长度的值)
MD系列(MD5)、SHA系列(SHA-1)、CRC,甚至JDK hashCode()也是哈希算法的一种。可以将他们分成三代:
第一代:MD5(1992),SHA-1(1993),CRC(1975),Lookup3(2006)
第二代:MurmurHash(2008)
第三代:CityHash, SpookyHash(2011)
可分为加密型、非加密型算法:
加密型:MD系列(MD5)、SHA系列(SHA-1)
非加密型:CRC、MurmurHash
其中非加密型的MurmurHash算法及其变种由Google研发出,性能强悍(官方测试 MurMurHash3 128位版本的计算速度是 MD5 的十倍,且碰撞率比MD5低很得多),被广泛应用于各开源产品,如 Redis、Memcached、Cassandra、Hadoop、HBase、Lucene、Spark、Nginx 等。Redis “Hash”数据类型的底层存储用散列表,其就是用MurmurHash来计算散列值的。
常用的有MD5、MurmurHash3,前者生成 128bit(16个字节,32个十六进制字符)固定长度的值,后者可生成 32bit 或 128bit 固定长度的值。32bit 较短且能表示的个数(约43亿个)足够多了,故在很多地方(如短链系统中)用到。
参考资料:《数据结构与教程 第二版》(北航出版社)