插入和删除是对数据结构的两种基本操作。此外还有查找、分类、合并、分解、复制和修改等。
顺序表
顺序表是线性表的顺序存储结构,指的是用一组地址连续的存储单元依次存储线性表的数据元素。 顺序表具备如下两个基本特征:
链表
链表指线性表的链式存储结构。一组任意的存储单元存储线性表的数据元素,因此,为了表示每个数据元素 ai 与其直接后继数据元素 ai+1 之间的逻辑关系,对数据元素 ai 来说,除了存储其本身的信息(数据域)之外,还需存储一个变量指示其直接后继的信息(指针域)。这两部分信息组成数据元素 ai 的存储映象,称为结点。N个结点链结成一个链表。该链表就是传统的单向链表。
有时,我们在单链表的第一个结点之前附设一个结点,称之为头结点,它指向表中第一个结点。头结点的数据域可 以不存储任何信息,也可存储如线性表的长度等类的附加信息,头结点的指针域存储指向第一个结点的指针。在单链表中,取得第 I 个数据元素必须从头指针出发寻找,因此,链表是非随机存取的存储结构。
以上提到的链表指针域只包括一个指针,指向下一个数据的地址,如果我们将链表最后一个结点指针域的指针指向链表的头结点地址,就构成了一个环状的存储结构,我们称作循环链表。
当然我们可以给每个结点的指针域再添加一个指针,使其指向前一个数据结点的地址,这样就构成了双向链表,而将头结点的前一个结点指向尾结点,同时将尾结点的下一个结点指向头结点就构成了双向循环链表。如果链表的尾结点的指针域指向了该链表之前的任意一个结点,我们称该链表为有环链表。环形链表就是其中一个特例
顺序表常见操作(括号中为算法平均时间复杂度,没有写明的具体复杂度依赖不同算法和运算规则):
索引
索引存储除建立存储结点信息外,还建立附加的索引表来标识结点的地址。索引表由若干索引项组成。对于索引的理解最好的例子就是《新华字典》,它建立的2套索引表(拼音、部首)。字典的正文就是从“啊”到“做”的每个字的解释,有上千页,就是是数据。而前面的拼音/部首就是索引表,索引表告诉你某个读音/部首在第几页,这就好比是指向数据地址的指针。而索引表可以有一级的也可以是多级的,比如字典中的部首索引就是两级的。索引存储结构是用结点的索引号来确定结点存储地址,其优点是检索速度快,缺点是增加了附加的索引表,会占用较多的存储空间。
散列
散列存储,又称哈希(hash)存储,是一种力图将数据元素的存储位置(预留连续存储区域)与关键码之间建立确定对应关系的查找技术。散列法存储的基本思想是由结点的关键码值决定结点的存储地址。散列技术除了可以用于存储外,还可以用于查找。散列以数据中每个元素的关键字 K 为自变量,通过散列函数 H(k)计算出函数值,以该函数值作为一块连续存储空间的的单元地址,将该元素存储到函数值对应的单元中。由于该函数值唯一,所以查找时间复杂度为 O(1)
线性表
线性表满足以下特征:
栈
栈实际上也是一个线性表,只不过是一种特殊的线性表。栈是只能在表的一端进行插入和删除运算的线性表,通常称插入、删除这一端为栈顶(TOP),另一端为栈底(BOTTOM)。当表中没有元素时称为栈空。 栈顶元素总是后被插入(入栈)的元素,从而也是最先被移除(出栈)的元素;栈底元素总是最先被插入的元素,从而也是最后才能被移除的元素。所以栈是个 后进先出(LIFO) 的数据结构
栈的基本运算有三种:入栈、出栈与读栈顶,时间复杂度都是O(1)
一个算法执行所耗费的时间,从理论上是不能算出来的,必须上机运行测试才能知道。但我们不可能也没有必要对每个算法都上机测试,只需知道哪个算法花费的时间多,哪个算法花费的时间少就可以了。并且一个算法花费的时间与算法中语句的执行次数成正比例,哪个算法中语句执行次数多,它花费时间就多。一个算法中的语句执行次数称为语句频度或时间频度。记为T(n)。
(2)时间复杂度
一般情况下,算法中基本操作重复执行的次数是问题规模n的某个函数,用T(n)表示,若有某个辅助函数f(n),使得当n趋近于无穷大时,T(n)/f(n)的极限值为不等于零的常数,则称f(n)是T(n)的同数量级函数。记作T(n)=O(f(n)),称O(f(n)) 为算法的渐进时间复杂度,简称时间复杂度。在T(n)=4n²-2n+2中,就有f(n)=n²,使得T(n)/f(n)的极限值为4,那么O(f(n)),也就是时间复杂度为O(n²)即n趋于无穷,算法所需时间呈平方增长
队列
队列是只允许在一端删除,在另一端插入的顺序表,允许删除的一端叫做队头,用对头指针 front指向对头元素的下一个元素,允许插入的一端叫做队尾,用队尾指针 rear指向队列中的队尾元素,因此,从排头指针 front 指向的下一个位置直到队尾指针 rear 指向的位置之间所有的元素均为队列中的元素。队列的修改是 先进先出(FIFO) 。往队尾插入一个元素称为入队运算。从对头删除一个元素称为退队运算。
队列主要有两种基本运算:入队运算和退队运算,复杂度都是O(1)
循环队列
在实际应用中,队列的顺序存储结构一般采用循环队列的形式。所谓循环队列,就是将队列存储空间的最后一个 位置绕到第一个位置,形成逻辑上的环状空间。在实际使用循环队列时,为了能区分队满还是队列空,通常需要增加一个标志 S。
循环队列主要有两种基本运算:入队运算和退队运算,复杂度都是O(1)入队运算
指在循环队列的队尾加入一个新元素,首先 rear=rear+1, 当 rear=m+1 时,置 rear=1,然后将新元素插入到队尾指针 指向的位置。当 S=1,rear=front,说明队列已满,不能进行入队运算,称为“上溢”。
指在循环队列的排头位置退出一个元素并赋给指定的变量。首先 front=front+1, 并当 front=m+1时,置 front=1 然后 将排头指针指向的元素赋给指定的变量。当循环队列为空 S=0,不能进行退队运算,这种情况成为“下溢”。
树
树是一种简单的非线性结构。树型结构具有以下特点:
二叉树
二叉树是一种树型结构,通常采用链式存储结构,满足以下特性:
二叉树的基本性质
二叉树的遍历
就是遵从某种次序,访问二叉树中的所有结点,使得每个结点仅被访问一次。分为以下几种:
此外图的遍历也可以用在树上,包括:
除此之外还有很多有特点的特殊二叉树:
堆
最常见的完全二叉树就是 **堆** 了。堆满足以下条件
将根结点最大的堆叫做 最大堆 或 大根堆 ,根结点最小的堆叫做 最小堆 或 小根堆 。
堆具有以下基本操作:
图有两种定义:
图的分类
图有不同的分类规则,具体如下:
分类1
- 有向图: 如果图中顶点之间关系不仅仅是连通与不连通,而且区分两边的顶点的出入(存在出边和入边),则为有向图。
- 无向图: 如果图中顶点之间关系仅仅是连通与不连通,而不区分两边顶点的出入(不存在出边和入边),则为无向图。
单图
分类2
- 有环图: 单向遍历回可以到已遍历的点,比如有环链表
- 无环图: 单向遍历不能回到已遍历的点,比如树
分类3
- 带权图: 图的具有边带有关于该边信息的权值,比如地图中两点间距离
- 无权图: 图的每个边都不具有有关于该边信息的权值,其仅表示是否连通
其他
- 单图: 一个图如果任意两顶点之间只有一条边且边集中不含环,则称为单图
图的表示采用邻接矩阵和类似树的形式(顶点指针域是个指针数组)的形式,其具有以下特点:
图的遍历
图的相关性质:
算法基本概念
算法复杂度
复杂度表示方法: 使用大写 O 表示:O(n)表示时间复杂度时指 n个数据处理完成使用 n个单位的时间;表示空间复杂度时指 n个数据处理完成使用了 n个单位的辅助空间。
字符串算法
字符串算法除了增删改查以外,还有很多匹配算法,比如最耳熟能详的 KMP 算法(不属于基础部分),这里整理一些相关算法的性质:
排序算法
排序算法实际上可以分为内排序和外排序:
排序算法时间复杂度
排序算法分为以下几类: