软件设计师2023记录(软考)-数据结构

三、数据结构

1.时间复杂度

在这里插入图片描述

重点计算时间复杂度,就是算出每条语句的执行次数,每条语句按上图规则求出时间复杂度,然后复杂度相加,再取最高的。

嵌套循环,复杂度相乘。

2.渐进符号

O符号:渐进上界,大于等于算法运算次数

Ω符号:渐进下界,小于等于算法运算次数

渐进紧致界:渐进上界 与 渐进下界相等

在这里插入图片描述

3.递归的时间复杂度、空间复杂度

每次递归时间复杂度和空间复杂度不变的情况下:

  1. 递归的时间复杂度:递归的次数×每次递归的时间复杂度
  2. 递归的空间复杂度:递归的次数×每次递归的空间复杂度

递归时间复杂度本质也是求语句的运行次数,如果出现每次递归中循环语句循环次数会发生变化,就自己算,等比数列或者等差数列去算

4.递归式主方法

第三种不会考

  1. 先写出a,b,f(n)
  2. 然后利用公式求出 衣服新浪或k
  3. 然后带入公式

在这里插入图片描述

在这里插入图片描述

5.线性表

除了线性表的顺序存储查找的时间复杂度O(1)为1,其他都是最好时间复杂度O(1),最坏时间复杂度O(n) ,平均时间复杂度O(n)

  1. 线性表的顺序存储

    1. 插入 表长为n,可能移动n个元素,移动元素个数的期望:n/2
      • 最好时间复杂度O(1),最坏时间复杂度O(n) ,平均时间复杂度O(n)
    2. 删除 表长为n,可能移动n-1个元素,移动元素个数的期望:(n-1)/2
      • 最好时间复杂度O(1),最坏时间复杂度O(n) ,平均时间复杂度O(n)
    3. 查找:时间复杂度O(1)
  2. 线性表的链式存储

    1. 单链表分为带头结点的和不带头结点的插入

      1. 带头结点的插入:插入的新节点为Node,i是第几个节点,P是指向第i个节点,要在k位置插入,则i要为k-1。步骤 我们找到插入位置的前一个节点P, Node->next = P->next;P->next = Node;

        • 带头结点的插入时间复杂度:
        • 最好时间复杂度O(1),最坏时间复杂度O(n) ,平均时间复杂度O(n)
      2. 不带头结点的插入:插入的新节点为Node,要判断k==1,因为当插入位置1的时候,P是第一个结点指针,只会插入到k的后面去,所以按照Node->next = p; p = Node;其他是Node->next = P->next;P->next = Node;

        • 不带头结点的插入时间复杂度:
        • 最好时间复杂度O(1),最坏时间复杂度O(n) ,平均时间复杂度O(n)
    2. 单链表分为带头结点的和不带头结点的删除

      1. 带头结点的删除:我们找到删除结点的前一个结点P,删除的结点为S ,S = P->next; P >next = S->next;
      2. 不带头结点的删除:我们找到删除结点的前一个结点P,删除的结点为S ,S = P->next; P >next = S->next;删除第一个结点比较特殊,直接将 第一个结点的指针赋给第二个结点
      3. 删除的时间复杂度:最好:O(1),最坏:O(n),平均:O(n)
    3. 单链表查找的时间复杂度 最好:O(1),最坏:O(n),平均:O(n)

  3. 循环单链表

    • 循环单链表,查找,删除,插入的时间复杂度 最好:O(1),最坏:O(n),平均:O(n)
  4. 双链表

    结点有前驱结点和后驱结点两个结点

看清题目,是尾指针,末尾加入尾结点

在这里插入图片描述

6.栈

先进后出

有顺序存储栈和单链表栈

在这里插入图片描述

7.队列

先进先出

  1. 循环队列
    1. 循环队列的题目,注意看头指针和尾指针的位置
    2. 有些尾指针指示队尾元素之后的位置

在这里插入图片描述

  1. 队列的链式存储
    1. 队尾(尾指针)入队,队头(头指针)出队。
    2. 入队不需要遍历整个链表,直接移动尾指针。
    3. 出队不需要遍历整个链表,直接移动头指针。

8.串

  1. 空串:长度为零的串称为空串,空串不包含任何字符。

  2. 子串:由串中任意长度的连续字符构成的序列称为子串。

  3. 空串是任意串的子串。

9.串的模式匹配与朴素模式匹配

k 为主串的第几个,i为开始比较后,i在主串的位置 ,每次比较开始 将 i = k,j为子串开始比较的位置 j=1

朴素模式匹配中:最原始的匹配方法,用子串和主串去一一比对,若不匹配,则从主串的下一个字符再去一一匹配,直到匹配结束。

  1. 最好时间复杂度:O(m)或者O(1),比较次数m次
  2. 最坏时间复杂度:O(n×m),匹配成功的最坏的比较次数 (n-m+1)次
  3. 平均时间复杂度:O(n+m),平均比较次数(n+m)/2次

10. KMP 匹配

  1. 手算next数组值

    1. 先遣知识:

      • 串的前缀:包含第一个字符并且不包含最后一个字符的子串。
      • 串的后缀:包含最后一个字符并且不包含第一个字符的子串
    2. 第i个字符的next值为,从1 ~ i - 1串中最长相等的前后缀长度+1。特殊情况下next[1] = 0; next[2] = 1;

    3. 注意:这个next数组是从1开始的,即第i个字符,数组中表示也是next[i],不是next[i-1]

  2. 算出next数组值之后,用子串和主串去一一比对,若不匹配,则从主串的下一个字符再去一一匹配,KMP算法做了个优化,模式串(子串)的第几个匹配不成功,则通过之前求出的next数组,获得相应的next数组值,然后模式串的j就为这个next数组值。

  3. 时间复杂度:O(n+m)

11.数组

  1. 一维数组

    1. L:元素大小 ,LOC:数组首地址
    2. 下标0开始,下标为i的元素的首地址 = LOC + i*L;
    3. 下标1开始,下标为i的元素的首地址 = LOC+ (i-1)*L;
  2. 二维数组

    1. 总共N行,i:行下标。总共M列,j:列下标。LOC:数组首地址,L:元素大小

    2. 下标从0开始

      1. 按行优先 位于第几个 :位置 = i×M+j

      2. 按列优先 位于第几个:位置 = j×N+i

    3. 下标从1开始

      1. 按行优先 位于第几个 :位置 = (i-1)×M+(j-1)
      2. 按列优先 位于第几个:位置 =(j-1)×N+ (i-1)
    4. i == j的时候,按行存储和按列存储一样

在这里插入图片描述

12.矩阵

  1. 对称矩阵

    • 对称矩阵中A[i,j] = A[j,i]
    • 所以我们存值只用存对角线的一个三角区

    在这里插入图片描述

  2. 在这里插入图片描述

  3. 三对角矩阵

    1. 二维数组下标0开始,位置 = 2i + j + 1
    2. 二维数组下标1开始 ,位置 = 2i + j -2
  4. 稀疏矩阵

    1. 用三元组表存储
    2. 第一个元素是i (行)
    3. 第二个元素是j (列)
    4. 第三个元素是数值
    5. 还可以用十字链表存储

13.树

1.定义

树结构是一种非线性结构,1对多(多是零个或多个)

结点为0则是空树

  1. 结点的度:拥有的孩子结点的数量
  2. 树的度:结点的最大度
  3. 叶子节点:没有子结点的结点
  4. 分支结点(内部结点):有分支的结点/不是根结点也不是叶子结点
  5. 兄弟结点:属于同一个父结点
  6. 层次:第几层,根结点就是第一层,根的孩子为第二层
  7. 树的高度:有几层

2.性质

  1. **树中的结点总数等于树中所有结点的度数之和加1,**加1是加上根结点。

  2. 性质2:度为m的树中第i层上至多有m^(i-1)个结点(i≥1) 。

  3. 性质3:高度为h的m次树至多有(m^h-1)/(m-1)个结点

  4. 性质4:具有n个结点、度为m的树的最小高度为[logm(n(m -1)+1)]

  5. 上取整:整数+1 ,下取整:直接取整数 下取整大致长这样,L I

14.二叉树

在这里插入图片描述

  1. 满二叉树和完全二叉树
    • 向下取整大致长这样,L I
    • 二叉树性质4针对完全二叉树,可以由二 叉树性质2推导出来,算最小高度,直接结点数+1,然后log2 ,然后上取整
  2. 在这里插入图片描述
    • 5个结点:42

15.二叉树存储

  1. 顺序存储

在这里插入图片描述

  1. 链式存储

在这里插入图片描述

二叉链表:n个结点有n-1个分支,每个结点有2个指针,总共2n个指针,n-1个有效指针,n+1个空指针域

三叉链表:n+2个空指针域

在这里插入图片描述

16.二叉树遍历和构造

  1. 二叉树先序遍历:根左右
  2. 二叉树中序遍历:左根右
  3. 二叉树后序遍历:左右根
  4. 根据遍历序列构造二叉树:通过先序、后序和层次获得根结点,然后再根据根结点在中序遍历中划分左右子树

17.平衡二叉树和二叉排序树

  1. 平衡二叉树:二叉树中的任意一个结点的左右子树高度之差的绝对值不超过
    • 完全二叉树肯定是平衡二叉树
  2. 二叉排序树(二叉查找树):左子树所有结点的关键字小于根结点的关键字,右子树所有结点的关键字大于根结点的关键字,左右子树也是一颗二叉排序树(左小右大)
    • 中序遍历得到的序列是有序序列,就是从小到大的排序序列

18.最优二叉树

  1. WPL = (叶子结点的权值)×(叶子结点到根的路径长度) 之和
  2. 如何构造最优二叉树

在这里插入图片描述

最优二叉树不唯一,其WPL值是唯一确定的

  1. 最优二叉树的性质

    1. 没有度为1的结点,全是0或2
    2. 树总结点个数 = 2n - 1 ;n为权值个数
  2. 软考中构造最优二叉树,这个很重要

    在这里插入图片描述

  3. 哈夫曼编码:在最优二叉树上,左边是标0,右边是标1

  4. 哈夫曼编码压缩比:

    1. 等长编码的长度n,怎么求?:2^n >= 字符的种数
    2. 比如有a,b,c,d,e五种字符,则等长编码的长度为3,如果有七种字符,长度也是3

19.建立线索二叉树

就是一种数据结构,不是一种数的类型,任何树都可以是线索二叉树

20.图

  1. 有向图:每条边有方向
  2. 无向图:每条边没有方向,就是一条直线
  3. 完全图
    1. 无向完全图:n个顶点,而每一个顶点与其他n-1个顶点之间都有边。边的数目为n(n-1)/2
    2. 有向完全图:任意两个不同顶点之间都有方向相反的两条弧存在。弧的数目为n(n-1)
  4. 顶点的度:顶点v的度是指关联于该顶点的边的数目
    1. 出度 A----->B ,这就A的出度,从顶点A指出去的边
    2. 入度 A<-----B ,这就A的入度,从其他顶点指向A的边
    3. 度 = 出度 + 入度
    4. 总度数 = 边的数量(e)×2
      • 总度数 = 2e
  5. 连通图和强连通图:
    1. 连通图:是无向图,任意两个顶点都是连通的
      • 最少n-1条边,最多n(n-1)/2条边
    2. 强连通图:是有向图,任意两个顶点,从顶点v到顶点u和从顶点u到顶点v都存在路径
      • 最少n条边,最多n(n-1)条边

20.图的存储结构

  1. 邻接矩阵

    在这里插入图片描述

    有向图是e个非零元素,无向图是2e个非零元素

  2. 邻接表

    在这里插入图片描述

  3. 稠密图与稀疏图

    • 稠密图:边多,用邻接矩阵存最好,不会浪费太多空间
    • 稀疏图:边少,用邻接表最好
  4. 网:边或弧带权值的图称为网

21.图的遍历

  1. 定义:图的遍历是指从某个顶点出发,沿着某条搜索路径对图中的所有顶点进行访问且只访问一次的过程。

  2. 深度优先遍历(DFS)

    1. 这是递归的思想,只要能访问,就一直访问到不能访问为止。不能访问的话就回退到上一个顶点。
    2. 时间复杂度:深度优先,采用邻接矩阵的时间复杂度O(n^2)===========采用邻接表的时间复杂度O(n+e)
    3. 在这里插入图片描述
  3. 广度优先搜索(BFS)

    1. 访问当前顶点v1的所有邻接点后,再往下访问这些邻接点中的某一个顶点v2的所有邻接点,v2的所有邻接点访问完后,在访问v1所有邻接点中某一个邻接点v3的所有邻接点,以此类推
    2. 用队列理解就是,搜索到的顶点入队,当该顶点所有的邻接点都在队列中(即都被访问过),则该顶点出队,出队序列就是广度优先搜索的序列
    3. 在这里插入图片描述
    4. 时间复杂度:广度优先,采用邻接矩阵的时间复杂度O(n^2)===========采用邻接表的时间复杂度O(n+e)
  4. 拓扑排序:AOV网:有向无环图

    1. 对 AOV网进行拓扑排序的方法如下

      • (1)在AOV网中选择一个入度为0(没有前驱)的顶点且输出它。
      • (2)从网中删除该顶点及与该顶点有关的所有弧。
      • (3)重复上述两步,直到网中不存在入度为0的顶点为止。
    2. 在有向无环图G的拓扑序列中,顶点vi在Vj之前,则可能存在弧<vi,vj>,一定不存在弧<vj,vi>。可能存在v到vj的路径,一定不存在vj到vi的路径

在这里插入图片描述

​ 拓扑排序 序列:614325

22.普里姆算法和克鲁斯卡算法(生成最小生成树)

普里姆算法:加点法,在已加入的点中找到邻接点最短的边

  • 时间复杂度O(n^2),n是顶点的数量
  • 适用于稠密图

克鲁斯卡算法:加边法,直接找到权值小的边

这两个都不能形成环

  • 时间复杂度O(mlogm),m是边的数量
  • 适用于稀疏图

四、数据结构 - 排序

1.查找分类

  1. 静态查找表有:顺序查找,折半(二分)查找,分块查找

  2. 动态查找表有:二叉排序树,平衡二叉树,B_树,哈希表

  3. 平均查找长度ASL = PC之和,P是查找概率为1,C是查找元素比较的个数

2.查找方法

  1. 顺序查找

    1. 平均查找长度:ASL = (n+1)/2
    2. 顺序结构和链式结构都适用
    3. 时间复杂度 T = O(n)
  2. 折半查找

    1. 折半查找用于顺序存储,元素必须有序
    2. 二分查找的过程:
      • 有1到12个数,从小到大排序,下标从1开始
      • mid = (1+12)/2 = 6 下取整
      • 和下标6比,比下标为6小,则high = mid -1;
      • 则比1到5
      • mid = (1+5)/2 = 3
    3. 最多比较的次数 :log2n下取整+1,平均查找长度:ASL = log2(n+1)-1
    4. 时间复杂度 T = O(log2n)
  3. 哈希表

    1. 计算地址:Hi = key%m

    2. 解决冲突:Hi = (H(key)+di )%m

      1. n个元素,长度m取接近n但不大于n的质数,可以减少冲突。哈希表的位置为0开始,0,1,2,3,……,m-2,m-1
    3. 在这里插入图片描述

    4. 在这里插入图片描述

    5. a 是哈希表的装填因子=表中已装入的记录数 / 哈希表的长度,a越小,发生冲突的可能性就越小

    6. 例题:

      在这里插入图片描述

3.小顶堆和大顶堆

优先队列使用堆结构

小顶堆:所有孩子结点都比父亲结点大

大顶堆:所有孩子结点都比父亲结点小

在这里插入图片描述

4.排序

排序:关键字满足以下递增(或递减)关系就是

若在待排序的一个序列中,Ri和Rj的关键字相同,即ki=kj,且在排序前Ri领先于Rj,那么在排序后,如果Ri和Rj的相对次序保持不变,Ri仍领先于Rj,则称此类排序方法为稳定的。若在排序后的序列中有可能出现Rj领先于Ri的情形,则称此类排序为不稳定的。

归位:确定了某个元素的位置,这个元素在之后排序结果中不会变化了


稳定:直接插入排序,冒泡排序,归并排序,基数排序(冒插归基

归位:简单选择排序,堆排序,快速排序,冒泡排序(**快选冒归 **)

在这里插入图片描述

在这里插入图片描述

  1. 直接插入排序,元素基本有序,插入排序更适宜

    • 就跟打牌中摸牌,理牌一样,从后往前比,大的放后面,小的放前面
  2. 希尔排序

    • 先取一个小于(n的整数d1作为第一个增量,把文件的全部记录分成d1个组,即将所有距离为d1倍数序号的记录放在同一个组中,在各组内进行直接插入排序;然后取第二个增量d2;(d1<d2),重复上述分组和排序工作,依此类推,直到所取的增量 d=l(di<di-1<…<d2<d1),即所有记录放在同一组进行直接插入排序为止。
  3. 简单选择排序

    • 从小到大:第一次排序就把最小的元素得到,放在第一位,然后第二次排序就在剩下的元素中继续找最小的元素,以此重复直到排序结束
  4. 堆排序

    • 大顶堆就确定了最大的元素,把最大元素移出树后,将树中最后一个结点移至根结点,然后调整为新堆

    在这里插入图片描述

  5. 冒泡排序

    • 比较数组中,两个相邻的元素,如果第一个数比第二个数大,就交换他们的位置,每轮比较都会产生出一个最大或者最小的数, 下一轮则少一次排序
    //冒泡排序
    //1. 比较数组中,两个相邻的元素,如果第一个数比第二个数大,就交换他们的位置
    //2. 每轮比较都会产生出一个最大或者最小的数
    //3. 下一轮则少一次排序
    // 2 3 5  4 1 9 8 5 -1  第一次比较2 3
    // 2 3 5  4 1 9 8 5 -1	第二次比较3 5
    // 2 3 4  5 1 9 8 5 -1  第三次比较5 4
    // 2 3 4  1 5 9 8 5 -1
    // 2 3 4  1 5 9 8 5 -1
    // 2 3 4  1 5 8 9 5 -1
    // 2 3 4  1 5 8 5 9 -1
    // 2 3 4  1 5 8 5 -1 9
    //这是一轮
    public static int[] sort(int[] array){
     for (int i = 0; i < array.length-1; i++) {
         for(int j = 0;j<array.length-1-i;j++){
             if (array[j]>array[j+1]){
                 int t = array[j];
                 array[j] = array[j+1];
                 array[j+1] = t;
             }
         }
     }
     return array;
    }
    
  6. 快速排序:元素基本有序是最坏的情况

    1. 先从右向左找第一个小于x的数,找到之后与下标i交换,然后从左向右找第一个大于x的数,与下标j

    void quick_sort(int a[], int l, int r) //l是low,低的一边,右边第一个元素的下标
    {
     if (l < r)
     {
         int i,j,x;
    
         i = l;
         j = r;
         x = a[i];
         while (i < j)
         {
             while(i < j && a[j] > x)// 先从右向左找第一个小于x的数
                 j--; 		//没找到则j--,找下一个
                 a[i] = a[j];	//从右向左找到比x小的数之后,与下标i交换
             
             while(i < j && a[i] < x)// 从左向右找第一个大于x的数
                 i++;	 //没找到则j--,找下一个
                 a[j] = a[i];//从左向右找到比x小的数之后,与下标j交换
         }
         a[i] = x;
         quick_sort(a, l, i-1); //枢轴的左边继续快速排序
         quick_sort(a, i+1, r); //枢轴的右边继续快速排序
     }
    }
    

​ 注意:快速排序如果取中间的元素为枢轴,j从后往前找第一个大于枢轴的元素。i从前往后找第一个小于枢轴的元素,然后这两个元素交换

  1. 归并排序

    • 先分,就是将所有元素分成一个个元素,再合,将所有元素按顺序合起来。怎么合?举例子:指针第一位i在4578,另一个指针j在1236。4和1比,4比1大,1放在另一个备用数组的第一位,然后j++,再4和2比,则2放在用数组的第二位,j++。以此类推

    在这里插入图片描述

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值