【咸鱼期末】数据结构知识点(1)

本文深入探讨数据结构核心,包括线性表、数组、链表、桶排序、基数排序、树、二叉查找树、平衡二叉树、B树、散列、堆、排序算法等,解析各数据结构的特点及应用场景。

线性表

  1. 数据结构的物理形式
    数组: 选择和搜索较快O(1), 插入和删除较慢O(n)
    链表: 插入和删除较快O(1), 选择和搜索较慢O(n)
  2. 桶排序: O(n+m) 其中m为桶的个数。每个桶是一个链表实现的线性表,链表尾部插入
  3. 基数排序:O((n+b)logb m) 其中b是基数,m为桶的个数。
    • 基数排序在每一位上而不是整个数做桶式排序,位排序的顺序是低位优先,基于前面一趟的桶排序进行桶排序,最后顺序扫描各个桶得到有序输出
    • 基数排序和桶式排序不同的地方:同一个桶中的数可以不同
  4. 堆栈的应用:符号平衡、后缀表达式求值、递归调用的实现
  5. 队列:FIFO队尾rear进或插入,队头front出或删除。为了防止队头队尾同时挤压到最后,最好使用循环链表。

  1. 节点的深度:根到节点的唯一路径的长(一般从1开始)
    节点的高度:节点到它的树叶最长路径的长(一般从1开始)
    树的高度和树的深度(类似于节点)
    内部节点:非叶子节点

  2. 先序遍历应用:层次缩进打印目录内容
    后序遍历应用:计算目录内容大小

    • 先序:根左右 中序:左根右 后序:左右根
    • 后缀表达式->表达式树的方法:借助栈,扫描后缀表达式,若遇到数字,构造单节点树,入栈;若遇到操作符,从栈中弹出两棵树,以操作符为根,合并弹出的两棵树,将得到的树入栈。
  3. 二叉查找树BST

    • BST的Find操作 ---- 递归法,迭代法

    • BST的Insert操作---- 递归法,迭代法(迭代比较,直到对应分支为NULL,插入)

    • BST的删除操作
      ----没有左右子节点,可以直接删除
      ----存在左节点或者右节点,删除后将子节点挂到对应父节点上
      ----同时存在左右子节点,通过和后继节点交换后转换为前两种情况

      后继节点:当某个节点存在右节点时,后继结点就是右节点中的最小值,因此后继节点一定没有左节点。所以,后继结点有可能存在右节点,也有可能没有任何节点。由于后继结点最多只有一个子节点,因此通过交换后继节点再删除后,就变成了 3 种情况中的前两种

      推荐:二叉查找树 - 删除节点 详解(Java实现)
      代码实现:
      在平衡性良好的情况下,查找插入删除O(log n),最坏代价达到O(n)

  4. 平衡二叉树AVL
    平衡二叉树AVL是一种任意节点的左右子树深度或高度相差最多为1的BST,AVL树的高度最大为O(logN)

    • 4种不平衡情况
      左儿子的左子树插入(左左)
      右儿子的右子树插入(右右)
      左儿子的右子树插入(左右)
      右儿子的左子树插入(右左)
      策略:1和2单旋转调整;3和4双旋转调整

    • 示例:

      在左左情境下,K2是搜寻出问题的节点。对于K2来说,先让他左膀的右臂成为他的左膀,再成为他左膀的右臂(2步操作)。

      双旋转示例(右左):
      代码(右左):在这里插入图片描述
      对于左右,先对右节点 右右处理,转化成左左;再进行左左处理
      对于右左,先对左节点 左左处理,转化成右右;再进行右右处理

  5. B树

    • M阶B树满足树根有2到M儿子;非根内部节点最少有ceil(M /2)个儿子,最多有M个儿子。这里ceil代表向上取整 。有k个儿子的内部节点,有k-1个关键字,升序排列。B树的高度: O(logM N)。 插入和删除可以接近常数时间,从而减少磁盘I/O次数。
    • 查找代价:O(logM N)
    • 插入代价(一般):O(M) 最坏代价:O(MlogM N)
      操作:由根向树叶查找,确定插入的树叶位置,插入。若树叶满,M+1关键字的节点分裂成floor((M+1)/2)和ceil((M+1)/2)个关键字的两个节点,父节点儿子树相应增加,可能一直分裂到根,从而增加B树的深度。//(我也不是很懂)
    • 删除最坏代价 :O(MlogM N)

散列

  1. 基本概念
    哈希函数: 将元素转换成可以查找的整数关键字,将理论上无限的元素映射到有限的哈希表地址空间
    哈希表:一种可以平均常数时间查找、插入和删除的数据结构
    冲突:多个元素会映射到表中同一个位置
  2. 哈希性质
    均匀哈希:每个关键字的探查序列等可能的是0-m-1中排列的任一种。
    简单均匀哈希:任何一个给定元素等可能的哈希到m个单元中任意一个,且与其他元素哈希到什么位置无关。
    哈希表大小的选择:最接近目标需求的素数
    平均情况下,哈希表的查找、插入和删除性能是常数时间
  3. 冲突处理
    • 分离链接法:将哈希到同一个单元的元素用链表连接起来
      装填因子λ=元素个数n/哈希表的大小=链表的平均长度
      平均情况分析(λ=O(1)):
      不成功的查找: λ 次比较(1+λ)
      成功查找平均情况:1+λ/2(1+λ)
      插入:1+λ
      删除:1+λ/2(1+λ)

      最坏情况分析(λ=O(N),所有元素哈希到同一链表,O(N))
      不成功的查找O(N)
      成功查找O(N)
      插入O(N)
      删除 O(N)

    • 开放寻址法

      • 线性探测
        • hi(k) = h(k) + i,只要表未满,线性探测保证元素可插入。
          哈希单元三种状态:空、删除和有效占用位置。删除标记的单元可以插入元素,可以继续往下查找。
        • 插入和不成功查找的期望探测次数为:(1+1/(1-λ)²)/2。
          成功查找的期望探测次数为:(1+1/(1-λ))/2,比不成功次数少。
        • 随机探测,不成功查找的期望探测次数(找到一个空单元)为:1/(1-λ)。
      • 二次探测(平方探测)
        hi(k) = h(k) + i², h(k) = k % 10。即使表未满,二次探测下的插入也可能失败。
        • 二次探测定理:表至少有一半是空的时候,总能插入一个新的元素
  4. 再散列
    当负载因子达到一定程度(或插入失败时),例如0.5,创建一个2倍大小的新哈希表,使用一个新的相关哈希函数将旧哈希表中的元素放到新哈希表中。代价为O(n)。

1. 优先队列

优先队列的出队基于元素的优先级,优先级最小或最大的元素先出队。按照类别可分为:最小优先队列(优先级最小的先出队,默认),最大优先队列(优先级最大的先出队):堆排序

2. 二叉堆

二叉堆是实现优先队列的一种重要方法。
分类:最小堆(最小优先队列)和最大堆(最大优先队列)
基本性质:
结构性:完全二叉树;
堆序性:任意节点小于(或大于)它的后裔;每颗子树都是一个堆。
1. 堆的初始化

其中,堆结构体里定义了堆的数组,大小和容量。在建堆时,堆的数组大小要比最大容量大一,因为要在数组的0位置设置一个哨兵元素,方便插入操作。
2. 插入(入队)
将新插入的元素放到堆尾。若新插入的元素比父亲大,父亲下沉,新插入元素上滤,直到比父亲大为止 。最坏情况下的代价:O(logN)
3. 删除(出队)
删除树根,两棵子树是二叉堆,但结构性破坏。将二叉堆最后的元素放入树根,结构性质、满足,堆序破坏。将新的树根和两个儿子比较,若有儿子大,大的儿子上升,新根下滤,直到比所有儿子小为止。 最坏情况下的代价:O(logN)
4. 构建堆
自顶而下的建堆方式:从根结点开始,把结点一个个插入堆中。当把一个新的结点插入堆中时,需要对结点进行调整,以保证堆依然是大根堆。时间复杂度为O(logN)。
自下而上的建堆方式:从最后一个内部节点直到根进行堆序性质维护。操作N次,时间复杂度为O(N)。

3. 堆排序

思想:将N个元素构建一个最大堆
重复N-1次以下过程:
1 将堆顶元素(最大元素)和堆中最后的元素交换
2 堆的大小减1
3 维护堆使得满足最大堆性质
在这里插入图片描述
其中,PercDown指的是下滤,即维护堆。上面的初始化堆代价为O(N),下面的删除后维护代价为O(logN),总的时间复杂度为O(NlogN)。

排序

稳定性:任意两个相等的数据,在排序前后相对位置不改变。

冒泡排序
  1. 最好代价O(N),最坏代价O(N²),平均代价O(N²)。
  2. 具有稳定性。
插入排序
  1. 维护一个有序区。
  2. 最好代价O(N),最坏代价O(N²),平均代价O(N²)。
  3. 具有稳定性。

基于相邻元素两两交换的排序每次只能消除一对逆序对,平均代价和最坏代价均为O(N)。

希尔排序
  1. 减小增量序列,对每个增量序列进行插入排序。
  2. 平均时间代价小于O(N²),约等于O(logN)。最坏代价达到O(N²)。
  3. 使用Hibbard增量的希尔排序每次增量为2^k-1,保证增量序列之间互质,最坏时间代价约为O(N ^ 2/3)。
  4. 希尔排序是不稳定的。
堆排序

见上一节

归并排序
  1. 合并子程:设置三个指针,进行判断和移动。需要额外空间。时间复杂度O(N)。
  2. 递归的算法:先划分成两个子问题,再进行合并。需要额外空间,总时间复杂度O(NlogN)。
  3. 归并排序是稳定的。
快速排序
  1. 时间复杂度O(logN),最坏情况为O(N^2)
  2. 操作流程:首先选取一个pivot,作为分割的参考值。比它小的放到左边,比它大的放到右边。选取pivot的方法常用三数取中值,最后将中值放到right -1的位置。
    在某一次特定的递归时,用两个指针进行比较,左边大于右边小于时交换。当右指针小于左指针时停止,并将pivot和左指针交换。
  3. 快排缺点:需要大量堆栈空间。因此,在数据量小时,常采用插入排序。
  4. 快速排序是不稳定的。

图的表示
  1. 图的邻接矩阵表示:简单直观,方便统计出度和入度。但在稀疏图情况下浪费时间和空间。
  2. 邻接链表表示:在稀疏图情况下节省时间空间,但在稠密图情况下浪费时间空间。此外,邻接链表不方便统计节点的入度,需要额外构建逆邻接链表。
图的遍历
  1. DFS深度优先搜索
    类似于树的先序遍历。沿着一条路走下去,然后逐层检测逐层退出。空间存储类似于堆栈。
void DFS(Vertex V){
	visited[V] = true;
	  for(V的每个邻接点W){
	    if(!visited[W]){
	      DFS(W);
	    }
	  }
}

时间复杂度:邻接表 O(N+E)
邻接矩阵O(N^2)。

  1. BFS广度优先搜索
    类似于树的层序遍历。遍历每一层的节点压入优先队列,弹出时再压入其邻接节点。如此循环完成每一层的遍历。
void BFS(Vertex V){
  visited[V] = true;
  Enqueue(V,Q);
  while(!isEmpty(Q)){
    V = Dequeue(Q);
    for(V的每个邻接点W){
      if(!visited[W]){
        visited[W] = true;
        Enqueue(W,Q);
      }
    }
  }
}

时间复杂度:邻接表 O(N+E)
邻接矩阵O(N^2)。

拓扑排序
  1. 拓扑序:如果从V到W有一条有向序列,那么在排序里V一定在W之前。合理的拓扑序必定是一个有向无环图。
  2. 操作过程:每次找出入度为0的节点,将其输出,并把所有与之相邻的节点入度减一(相当于删去这个点)。如果循环还未结束已经找不到p入度为0的节点,则说明这个图里有环。
void TopSort(){
  int cnt = 0;
  for(图中每个顶点V){
    if(Indegree[V] == 0)
      Enqueue(V,Q);
  }
  while(!isEmpty(Q)){
    V = Dequeue(Q);
    输出V
    cnt++;
    for(V的每个邻接点W){
	  if(--Indegree[W] == 0)
	    Enqueue(W,Q);
    }
  }
  if(cnt != |V|) 
    Error("图中有回路");
}

时间复杂度: O(N+E)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值