数据结构 笔记

2020-11-19  本文已影响0人  gaookey

数据结构绪论

按照视点的不同,数据结构分为逻辑结构和物理结构。

逻辑结构:是指数据对象中数据元素之间的相互关系。
物理结构:是指数据的逻辑结构在计算机中的存储形式。

数据类型:是指一组性质相同的值的集合及定义在此集合上的一些操作的总称。

算法

算法是解决特定问题求解步骤的描述,在计算机中表现为指令的有限序列,并且每条指令表示一个或多个操作。

算法的特性

算法设计的要求

算法效率的度量方法

int i, sum = 0, n = 100;       // 执行一次
for (i = 1; i <= n; i ++) {    // 执行了n+1次
    sum = sum + i;             // 执行n次
}
printf("%d", sum);             // 执行1次
int sum = 0, n = 100;     // 执行1次
sum = (1 + n) * n / 2;    // 执行1次
printf("%d", sum);        // 执行1次

函数的渐近增长

给定两个函数f(n)和g(n),如果存在一个整数N,使得对于所有的n>N,f(n)总是比g(n)大,那么,我们就说f(n)的增长渐近快于g(n)。
判断一个算法的效率时,函数中的常数和其他次要项常常可以忽略,而更应该关注主项(最高阶项)的阶数。

算法时间复杂度

常见的时间复杂度及所耗费的时间从小到大依次是:

O(1) < O(logn) < O(n) < O(nlogn) < O(n²) < O(n³) < O(2ⁿ) < O(n!) < O(nⁿ)

线性表

零个或多个数据元素的有限序列。

顺序存储结构:

指的是用一段地址连续的存储单元依次存储线性表的数据元素。

优缺点

链式存储结构

头指针:
头结点:

结点由存放数据元素的数据域和存放后继结点地址的指针域组成。

单链表结构与顺序存储结构的优缺点

存储分配方式:
时间性能:
空间性能 :
结论:

静态链表

用数组描述的链表叫做静态链表

静态链表的优缺点:

循环链表

将单链表中终端结点的指针端由空指针改为指向头结点,就使整个单链表形成一个环,这种头尾相接的单链表称为单循环链表,简称循环链表。

双向链表

双向链表是在单链表的每个结点中,再设置一个指向其前驱结点的指针域。

栈与队列

栈是限定仅在表尾进行插入和删除操作的线性表。

允许插入和删除的一端称为栈顶,另一端称为栈底,不含任何数据元素的栈称为空栈。栈又称为后进先出的线性表,简称LIFO结构。

栈的顺序存储结构

既然栈是线性表的特例,那么栈的顺序存储其实也是线性表顺序存储的简化,我们简称为顺序栈。

栈的链式存储结构

栈的链式存储结构,简称为链栈。

如果栈的使用过程中元素变化不可预料,有时很小,有时非常大,那么最好是用链栈,反之,如果它的变化在可控范围内,建议使用顺序栈会更好一些。

栈的引入简化了程序设计的问题,划分了不同关注层次,使得思考范围缩小,更加聚焦于我们要解决的问题核心。

栈的应用

递归

我们把一个直接调用自己或者通过一系列的调用语句间接地调用自己的函数,称做递归函数。

递归和迭代的区别:

斐波那契数列

#include <stdio.h>

int Fbi(int i) {
    if (i < 2) {
        return i == 0 ? 0 : 1;
    }
    return Fbi(i - 1) + Fbi(i - 2);
}

int main(int argc, const char * argv[]) {
    
    int i;
    printf("递归显示斐波那契数列:\n");
    
    for (i = 0; i < 20; i ++) {
        printf("%d ", Fbi(i));
    }
    
    return 0;
}
四则运算表达式求值
后缀(逆波兰)表示法

正常数学表达式:9 + (3 - 1) * 3 + 10 / 2

后缀表达式: 9 3 1 - 3 * + 10 2 / +

队列

队列是只允许在一端进行插入操作、而在另一端进行删除操作的线性表。

队列是一种先进先出的线性表,简称FIFO。允许插入的一端称为队尾,允许删除的一端称为对头。

循环队列

我们把队列的这种头尾相接的顺序存储结构称为循环队列。

链式存储结构

队列的链式存储结构,其实就是线性表的单链表,只不过它只能尾进头出而已,我们把它简称为链队列。

串是由零个或多个字符组成的有限序列,又叫字符串。

树是n(n>=0)个结点的有限集。n=0时称为空树。在任意一棵非空树中:
1.有且仅有一个特定的称为根的结点
2.当n>1时,其余结点可分为m(m>0)个互不相交的有限集T₁、T ₂、...Tₘ、,其中每一个集合本身又是一棵树,并且称为根的子树。

结点拥有的子树数称为结点的度。度为0的结点称为叶结点或终端结点;度不为0的结点称为非终端结点或分支结点。除根节点之外,分支结点也称为内部结点。树的度是树内各结点的度的最大值。

树中结点的最大层次称为树的深度或高度。

如果将树中结点的各子树看成从左至右是有次序的,不能互换的,则称该树为有序树,否则称为无序树。

线性表与树的结构区别

二叉树

二叉树是n(n≥0)个结点的有限集合,该集合或者为空集(称为空二叉树),或者由一个根节点和两棵互不相交的、分别称为根节点的左子树和右子树的二叉树组成。

二叉树的特点

二叉树五种基本形态

  1. 空二叉树
  2. 只有一个根节点
  3. 根节点只有左子树
  4. 根节点只有右子树
  5. 根节点既有左子树又有右子树
特殊二叉树
斜树

所有的结点都只有左子树的二叉树叫左斜树。所有结点都只有右子树的二叉树叫右斜树。这两者统称为斜树。

满二叉树

在一棵二叉树中,如果所有分支结点都存在左子树和右子树,并且所有叶子都在同一层上,这样的二叉树称为满二叉树。

满二叉树的特点:

  1. 叶子只能出现在最下一层。出现在其它层就不可能达到平衡。
  2. 非叶子结点的度一定是2.
  3. 在同样深度的二叉树中,满二叉树的结点个数最多,叶子数最多。
完全二叉树

对一棵具有n个结点的二叉树按层序编号,如果编号为i(1≤i≤n) 的结点与同样深度的满二叉树中编号为i的结点在二叉树中位置完全相同,则这棵二叉树称为完全二叉树。

完全二叉树的特点:

  1. 叶子结点只能出现在最下两层。
  2. 最下层的叶子一定集中在左部连续位置。
  3. 倒数两层,若有叶子结点,一定都在右部连续位置。
  4. 如果结点度为1,则该结点只有左孩子,即不存在只有右子树的情况。
  5. 同样结点数的二叉树,完全二叉树的深度最小。

满二叉树一定是一棵完全二叉树,但完全二叉树不一定是满的。

二叉树的性质
  1. 在二叉树的第 i 层至多有 2ⁱ⁻¹ 个结点 (i≥1)
  2. 深度为 i 的二叉树至多有 2ⁱ-1 个结点 (i≥1)
  3. 对任何一棵二叉树 T ,如果其终端结点数为 n₀,度为 2 的结点数为 n₂,则 n₀=n₂+1
  4. 具有 n 个结点的完全二叉树的深度为 ⎣log₂n⎦+1⎣x⎦ 表示不大于 x 的最大整数)。
  5. 如果对一棵有 n 个结点的完全二叉树(其深度为 ⎣log₂n⎦+1)的结点按层序编号(从第一层到第 ⎣log₂n⎦+1 层,每层从左到右),对任一结点 i1≤i≤n)有:
    5.1. 如果 i=1,则结点 i 是二叉树的根,无双亲;如果 i>1,则其双亲是结点 ⎣i/2⎦
    5.2. 如果 2i>n,则结点 i 无左孩子(结点 i 为叶子结点);否则其左孩子是结点 2i
    5.3. 如果 2i+1>n,则结点 i 无右孩子;否则其右孩子是结点 2i+1

二叉树的存储结构

遍历二叉树

二叉树的遍历是指从根节点出发,按照某中次序依次访问二叉树中的所有节点,使得每个结点被访问一次且仅被访问一次。

二叉树的遍历方法
image.png
前序遍历

规则是若二叉树为空,则空操作返回,否则先访问根节点,然后前序遍历左子树,再前序遍历右子树。
遍历的顺序为ABDGHCEIF。

中序遍历

规则是若树为空,则空操作返回,否则从根节点开始(注意并不是先访问根节点),中序遍历根节点的左子树,然后是访问根节点,最后中序遍历右子树。
遍历的顺序为GDHBAEICF。

后序遍历

规则是若树为空,则空操作返回,否则从左到右先叶子后结点的方式遍历访问左右子树,最后是访问根节点。
遍历的顺序为GHDBIEFCA。

层序遍历

规则是若树为空,则空操作返回,否则从树的第一层,也就是根节点开始访问,从上而下逐层遍历,在同一层中,按从左到右的顺序对结点逐个访问。
遍历的顺序为ABCDEFGHI。

二叉树遍历的性质

已知前序和后序遍历,是不能确定一棵二叉树的。

线索二叉树

指向前驱和后继的指针称为线索,加上线索的二叉链表称为线索链表,相应的二叉树就称为线索二叉树。

对二叉树以某种次序遍历使其变为线索二叉树的过程称做是线索化。

如果所用的二叉树需经常遍历或查找结点时需要某种遍历序列中的前驱和后继,那么采用线索二叉链表的存储结构就是非常不错的选择。

......

上一篇下一篇

猜你喜欢

热点阅读