数据结构(C)

来源:互联网 发布:网络机柜中u表示什么 编辑:程序博客网 时间:2024/05/21 06:54

整理自传智扫地僧教学视频

概念

程序 = 数据结构 + 算法

  • 数据项 -> 数据元素 -> 数据对象
  • 数据对象: 性质相同的数据元素的集合
  • 数据结构: 研究数据元素之间的关系。 (结点与结点之间的关系; 数组/链表/树/图)
    • 数据的逻辑结构
      • 集合
      • 线性结构: 1对1
      • 树形结构: 1对多
      • 图状结构: 多对多
    • 数据的物理结构(存储结构)
      • 顺序存储
      • 链式存储
      • 索引
      • 散列
  • 算法: 特定问题求解步骤的描述。 指令的有限序列。
  • 算法的度量
    • 时间复杂度
    • 空间复杂度
    • O表示法: O(1) < O(logn) < O(n) < O(nlogn) < O(n2) < O(n3) < O(2n) < O(n!) < O(nn)
    • 时间换空间、空间换时间

线性表

  • 通用操作: 创建、销毁、获取长度、清空(回到初始化即刚创建的状态)、获取数据元素、插入数据元素、删除数据元素。
  • 线性表顺序存储: 指针数组。提前确定好容量,存储每个数据元素的地址。
    • 优点: 不需要为表中的逻辑关系增加额外的空间;可以快速获得合法位置的元素
    • 缺点: 插入和删除需要移动大量的元素。
  • 线性表链式存储 (单向链表)
    链表算法和具体业务模型的分离 3个结构体:结点指针域、头结点、业务结点
  • 循环链表
    链表头新增数据项:游标
    新增操作
    • 直接指定删除链表中的某个数据元素
      CircleListNode *CircleList_DeleteNode(CircleList *list, CircleListNode *node);
    • 游标(slider)操作
      • 重置游标,指向第一个元素
        CircleListNode *CricleList_Reset(CircleList *list);
      • 获取游标指向的数据元素
        CircleListNode *CircleList_Current(CircleList *list);
      • 返回当前指向的数据元素,然后移动指向下一个数据元素
        CircleListNode *CircleList_Next(CircleList *list);
  • 双向链表
    数据元素中新增数据项: 指向前一数据元素的指针
    新增操作: 游标前移
  • 链表的操作逻辑和辅助指针变量
    指针指向谁,就把谁的地址赋给指针;不断的给指针赋值,就是不断的改变指针的指向。
    使用带有头部的链表,辅助指针变量指向头部,链表编号从零开始,辅助指针移动N次,指向第(N-1)个数据元素,第(N-1)个数据元素中保存了第N个数据元素的位置。使用带有头部,位置编号从0开始(头部不算)的单链表

栈是一种特殊的线性表,只能在一端(栈顶)进行操作。

  • 操作: 创建、销毁、清空、获取栈的大小、入栈、出栈、获取栈顶元素
    模型(开口方向)的选择
  • 栈的顺序存储: 用链表的顺序存储模拟。
  • 栈的链式存储: 用单向链表模拟。 栈的业务结点转换为链表的业务结点
    入栈的结点都是通过malloc()分配的,在出栈、清空栈、销毁栈时要通过free()释放内存
  • 栈的应用
    • 就近匹配
    • 中缀、后缀表达式
      • 中缀转后缀
        从左到右遍历中缀表达式,若为数字直接输出;若为操作符: 比较与栈顶元素的优先级
        1. 左括号’(’ 直接入栈,当遇到右括号’)’时,将栈中的操作符弹出并输出,直到遇到左括号’(‘。左括号’(‘只弹出不输出。除非处理右括号,否则绝不从栈中移走左括号
        2. 优先级高于栈顶的操作符,入栈。否则,弹出栈顶操作符输出直到优先级低于当前操作符,当前操作符入栈。
        3. 遍历表达式结束后,弹出栈中操作符到输出。
      • 后缀表达式的计算
        从左向右遍历表达式,遇到数字,入栈;遇到操作符时,弹出右操作数,弹出左操作数,计算出结果后入栈,直到遍历结束,栈中唯一的元素即运算结果。

队列

队列也是一种线性表,只允许在一端进行插入,在另一端进行删除。

  • 操作: 创建、销毁、清除、入队、出队、获取队列的长度、获取队头元素
  • 队列的顺序存储: 用链表的顺序存储模拟。
  • 队列的链式存储: 用单链表模拟。

树 (链表+递归)

  • 术语
    • 根:没有前驱
    • 叶子:没有后继
    • 森林: 不相交的树的集合
    • 有序树
    • 无序树
    • 结点的度:结点挂接的子树数,即直接后继的个数
    • 树的度:所有结点度中的最大值 Max{各结点的度}
    • 树的深度(高度)
    • 后继结点: 该结点中序遍历的下一个结点。
    • 前驱结点: 该结点中序遍历的上一个结点。
  • 二叉树
    • 性质
      • 在第N层上最多有2N-1个结点
      • 深度为K的二叉树,最多有2K-1个结点,至少有K个结点
      • 任何一颗二叉树,若度为2的结点有n个,则叶子结点的个数为n+1。
    • 完全二叉树: 共有K层,前K-1层与满二叉树一样,第K层结点靠左,中间不能有空。
      • n个结点的完全二叉树,深度为 [log2n]+1
      • 完全二叉树,从上到下,从左到右,从1开始编号,编号为i的结点,其左子编号为(2*i),右子编号为(2*i+1),父结点编号为(i/2), i=1为根,除外。顺序存储
    • 非二叉树转换为二叉树 “左孩子右兄弟表示法”
      • 将结点的所有兄弟结点连接起来。
      • 对每个结点,只保留与左子结点的连接,与其他子结点的连接均删除。
  • 树的表示法 链式存储
    • 二叉链表示
    • 三叉链表法 (包含指向双亲的指针)
    • 双亲链表表示 两张表(结点表、结点关系表);
  • 二叉树的遍历
    • 遍历顺序 D:根; L:左子树; R:右子树 规定先左后右,相对于根结点
      • 先序遍历 DLR –前缀表达式
      • 中序遍历 LDR –中缀表达式
      • 后序遍历 LRD –后缀表达式
    • 递归遍历
      • 三种遍历的本质:访问路径相同,访问结点的时机不同。将printf()语句去掉,从递归的角度看,三种算法相同。每个结点都经过3次
        • 第一次经过时访问 –> 先序遍历
        • 第二次经过时访问 –> 中序遍历
        • 第三次经过时访问 –> 后序遍历
    • 非递归遍历 二叉树的中序遍历 栈实现
      • 步骤1:
        • 如果结点有左子树,该结点入栈;否则,访问该结点
      • 步骤二:
        • 如果结点有右子树,重复步骤1;
        • 如果结点没有右子树(结点访问完毕),根据栈顶元素回退(访问栈顶元素并出栈),再访问右子树,重复步骤1.
        • 栈为空时,遍历结束。入栈的结点表示,本身没有被访问过,同时右子树也没有被访问过。
  • 树的创建和释放

    • #号法: NULL位置设为#
    • 根据遍历结果确定树
      • 先序遍历和中序遍历可以确定一棵树;
      • 后序遍历和中序遍历可以确定一棵树;
      • 先序遍历和后序遍历不能确定一颗树。
    • 树的释放
      • 后序遍历释放
  • 树的线索化

    • 方法1: 结点增加两个域,标识lchild和rchild是否线索化。当lchild或rchild为NULL时,设置对应的线索化标识,lchild指向前驱,rchild指向后继
    • 方法2: 通过链表。遍历访问结点时,将结点插入链表,遍历结束后,整个树线索化完成。

排序

  • 排序的稳定性
  • 排序算法
    • 选择法: 依次取一个数,与剩余的未比较的数进行比较。2层循环。
    • 插入排序: 依次取一个数,与之前的数比较,比它大向后移动。
    • 冒泡法:相邻元素比较,符合条件交换。
      • 冒泡法优化: 设置标记,若未发生交换,则已排好序,之后不用再进行比较。
    • 希尔排序
    • 快速排序
    • 归并排序
原创粉丝点击