数据结构 查找

目录

  • 一、查找
  • 7.1 查找的基本概念
    • 1. 概念
    • 2. 静态动态查找表
  • 7.2 顺序查找和折半查找
    • 7.2.1 顺序查找
    • 1. 顺序查找的算法思想
    • 2. 顺序查找的实现
    • 3. 哨兵思想实现
    • 4. 查找效率分析
    • 5. 顺序查找的优化(对有序表)
    • 6. 用查找判定树分析ASL
    • 7. 顺序查找的优化(被查概率不相等)
    • 7.2.2 折半查找
    • 1. 折半查找的算法思想
    • 2. 折半查找的实现
    • 3. 查找效率分析
    • 4. 折半查找判定树的构造
    • 5. 折半查找的效率
    • 6. 拓展思考
    • 7.2.3 分块查找
    • 1. 分块查找的算法思想
    • 2. 用折半查找查索引
    • 3. 查找效率分析(ASL)
    • 4. 拓展思考
  • 7.3 二叉排序树(BST)
    • 1. 二叉排序树的定义
    • 2. 二叉排序树的查找
      • 非递归实现
      • 递归实现
    • 3. 二叉排序树的插入
    • 4. 二叉排序树的构造
    • 5. 二叉排序树的删除
    • 6. 查找效率分析
  • 7.4 平衡二叉树
    • 1. 平衡二叉树的定义
    • 2. 平衡二叉树的插入
      • LL
      • RR
      • LR
      • RL
      • 总结
    • 3. 查找效率分析
    • 4. 平衡二叉树的删除
      • 例子1
      • 例子2
      • 总结
  • 7.5 红黑树 RBT
    • 1. 红黑树的定义和性质
      • 为什么要发明红黑树?
    • 2. 红黑树的定义
    • 3. 结点的 ”黑高“
    • 4. 红黑树的性质
      • 根节点黑高为h的红黑树的性质
      • 总结
    • 5. 红黑树的查找
    • 6. 红黑树的插入
    • 7. 红黑树的删除
  • 7.6 B树
    • 1. 定义
    • 2. 核心特征
    • 3. B树的插入
      • 1. 重点1
      • 2. 重点2
      • 3. 重点3
      • 4. 重点4
      • 5. 总结
    • 4. B树的删除
      • 1. 重点1
      • 2. 重点2
      • 3. 重点3
  • 7.7 B+树
    • 1. 定义
    • 2. B+树的查找
    • 3. B+树 vs B树
    • 4. B+和B树的总结
  • 7.8 散列查找
    • 7.8.1 散列表(Hash Table)
      • 1. 定义
      • 2. 拉链法
      • 3. 开放定址法
    • 7.8.2 散列函数的设计
      • 除留余数法
      • 直接定址法
      • 数字分析法
      • 平方取中法
    • 7.8.3 处理冲突的拉链法
      • 插入
      • 查找
      • 删除
      • 总结
      • 小优化
    • 7.8.4 处理冲突的开放定址法
      • 定义
    • 插入操作
      • 线性探测法
      • 平方探测法
      • 双散列法
      • 伪随机序列法
    • 删除操作
    • 总结
    • 探测覆盖率
      • 总结
    • 散列因子

一、查找

7.1 查找的基本概念

1. 概念

数据结构 查找_第1张图片

  • 查找:在数据集合中寻找满足某种条件的数据元素的过程称为查找
  • 查找表(查找结构):用于查找的(只要是用于存放我们需要查找的数据的任何数据结构都是查找表)。数据集合称为查找表,它由同一类型的数据元素 (或记录)组成。
  • 关键字:数据元素中唯一标识该元素的某个数据项的值,使用基于关键字的查找,查找结果应该是唯一的(结果是唯一的而不是查找到的元素的个数只有一个)
  • 查找长度:在查找运算中,需要对比关键字的次数称为查找长度
  • 平均查找长度(ASL,Average Search Length): 所有查找过程中进行关键字的比较次数的平均值

2. 静态动态查找表

数据结构 查找_第2张图片

7.2 顺序查找和折半查找

7.2.1 顺序查找

数据结构 查找_第3张图片

1. 顺序查找的算法思想

  • 顺序查找,又叫“线性查找”,通常用于线性表

  • 算法思想:从最开始或者最后一个数据元素位置查找,直到找到或者找不到

2. 顺序查找的实现

以顺序表为例,查找表是单链表或者双链表当然也行,循环和结束的写法有些逻辑差别

typedef struct{       //查找表的数据结构(顺序表)
    ElemType *elem;   //动态数组基址
    int TableLen;     //表的长度
}SSTable;

//顺序查找
int Search_Seq(SSTable ST,ElemType key){
    int i;
    for(i=0;i<ST.TableLen&&ST.elem[i]!=key;++i);
    //查找成功,则返回元素下标;查找失败,则返回-1
    return i==ST.TableLen?-1:i;
}

3. 哨兵思想实现

增加了一个”哨兵“,把0号位置空出来,实际的数据从1这个位置开始存放。当我们要查找某一个关键字的时候,会把关键字放在0号位置,这就是所谓的哨兵。

查找时,是从最后开始查找,直到遇到哨兵停止

差别在于无需同从最开始开始查找一样一直判断是否越界
这种写法在每一轮for循环的时候,只需要判断当前指向的元素与要查找的关键字是否相等而无需判断是否越界,效率更高

typedef struct{       //查找表的数据结构(顺序表)
    ElemType *elem;   //动态数组基址
    int TableLen;     //表的长度
}SSTable;

//顺序查找
int Search_Seq(SSTable ST,ElemType key){
    ST.elem[0]=key;                               //哨兵
    int i;
    for(i=ST.TableLen,ST.elem[i]!=key;--i);       //从后往前找
    return i;                  //查找成功,则返回元素下标;查找失败,则返回0
}

4. 查找效率分析

数据结构 查找_第4张图片

5. 顺序查找的优化(对有序表)

  • 若表中元素是递增的,则每一次对比时若等于则查找成功结束,若小于则查找失败,若大于则继续对比下一个。

  • 从下图中可以看到有n + 1个失败结点,即n + 1种失败情况,如21落在( 19 , 29 ) 区间内,是第4个失败结点,就要对比4次发现查找失败。

数据结构 查找_第5张图片
分子上最后面加了两个n,因为最下面的两种失败情况都需要把最前面的n个元素全部对比一遍,这两种情况都要对比n次关键字

6. 用查找判定树分析ASL

数据结构 查找_第6张图片

7. 顺序查找的优化(被查概率不相等)

数据结构 查找_第7张图片

7.2.2 折半查找

数据结构 查找_第8张图片

1. 折半查找的算法思想

折半查找,又称“二分查找”,仅适用于有序的顺序表

顺序表其实就是说些元素得是用数组存放起来的,只有顺序表才拥有随机访问的特性(就是只有这样才方便折半),链表没有这个特性

左右指针,low high; 中间指针mid,不断比较mid指针与查找元素的大小,左右修改指针,再次折半,直到找到

2. 折半查找的实现

注:以下代码基于升序排列的线性表,如果是降序排列会略有不同

typedef struct{       //查找表的数据结构(顺序表)
    ElemType *elem;   //动态数组基址
    int TableLen;     //表的长度
}SSTable;

//折半查找
int Binary_Search(SSTable L,ElemType key){
    int low=0,high=L.TableLen-1,mid;
    while(low<=high){
        mid=(low+high)/2;             //取中间位置
        if(L.elem[mid]==key){
            return mid;               //查找成功则返回所在位置
        }else if(L.elem[mid]>key){
            high=mid-1;               //从前半部分继续查找
        }else{
            low=mid+1;                //从后半部分继续查找
        }
    }
    return -1;                        //查找失败。返回-1
}

3. 查找效率分析

数据结构 查找_第9张图片

数据结构 查找_第10张图片

4. 折半查找判定树的构造

数据结构 查找_第11张图片
数据结构 查找_第12张图片

  • 平衡二叉树
  • ==树的形状是可用确定的,只要有序元素的个数确定并且mid的算法确定,向上或者向下取整,也会导致左树和右树的高度差固定-1 0 +1 ==

5. 折半查找的效率

在这里插入图片描述
折半查找大部分情况比顺序快(要查找的数据就在第一个就会顺序查找最快)

6. 拓展思考

数据结构 查找_第13张图片

7.2.3 分块查找

数据结构 查找_第14张图片

1. 分块查找的算法思想

给定一组元素,进行分块查找前将元素分块并保证块内无序,块间有序。对其建立索引表

数据结构 查找_第15张图片

//索引表
typedef struct{
    ElemType maxValue;
    int low,high;
}Index;

//顺序表存储实际元素
ElemType List[100];

2. 用折半查找查索引

数据结构 查找_第16张图片
数据结构 查找_第17张图片

数据结构 查找_第18张图片
注意上边应该是high左移一位,因为mid=high,所以high=mid-1,导致high左移。
所以low>high查找失败的时候,low一定大于key

3. 查找效率分析(ASL)

数据结构 查找_第19张图片
数据结构 查找_第20张图片

4. 拓展思考

数据结构 查找_第21张图片

7.3 二叉排序树(BST)

数据结构 查找_第22张图片

1. 二叉排序树的定义

二叉排序树,又称二叉查找树(BST)。一棵二叉排序树或者是空二叉树,或者是具有如下性质的二叉树:

  • 左子树上所有结点的关键字均小于根结点的关键字

  • 右子树上所有结点的关键字均大于根结点的关键字
    数据结构 查找_第23张图片

  • 对二叉排序树进行中序遍历,可以得到一个递增的有序序列。

  • 二叉排序树可用于元素的有效组织,搜索

2. 二叉排序树的查找

利用二叉排序树左子树结点值<根结点值<右子树结点值的特点实现查找操作

  • 若树非空,目标值与根结点的值相比较:

  • 若相等,则查找成功;

  • 若小于根结点,则在左子树上查找,否则在右子树上查找。

  • 查找成功,返回结点指针;查找失败返回NULL

非递归实现

//二叉排序树结点
typedef struct BSTNode{
    int key;
    struct BSTNode *lchild,*rchild;
}BSTNode,*BSTree
    
//在二叉排序树中查找值为key的结点
BSTNode* BST_Search(BSTree T,int key){
    while(T!=NULL&&key!=T->key){             //若树空或等于根结点值,则结束循环
        if(key<T->key){                     
            T=T->lchild;                     //小于,则在左子树上查找
        }else{
            T=T->rchild                      //大于,则在右子树上查找
        }
    }
    return T;
}

最坏空间复杂度为O ( 1 ) ,也有如下递归实现的版本,最坏空间复杂度为O ( h ),不如这个好

递归实现

//在二叉排序树中查找值为key的结点(递归实现)
BSTNode *BSTSearch(BSTree T,int key){
    if(T==NULL){
        return NULL;  //查找失败
    }
    if(key==T->key){
        return T;     //查找成功
    }else if(key<T->key){
        return BSTSearch(T->lchild,key); //在左子树中找
    }else{
        return BSTSearch(T->rchild,key); //在右子树中找
    }
}

3. 二叉排序树的插入

新插入的一定是叶子结点

  • 若原二叉排序树为空,则直接插入结点
  • 否则,若关键字k小于根结点值,则插入到左子树
  • 若关键字k大于根结点值,则插入到右子树
  • 注意传参是引用类型
//在二叉排序树中插入关键字为k的新结点(递归实现)
int BST_Insert(BSTree &T,int k){
    if(T==NULL){                   //原树为空,新插入的结点为根结点
        T=(BSTree)malloc(sizeof(BSTNode));
        T->key=k;
        T->lchild=T->rchild=NULL;
        return 1;                    //返回1,插入成功
    }else if(k==T->key){             //树中存在相同关键字的结点,插入失败
        return 0;
    }else if(k<T->key){              //插入到T的左子树
        return BST_Insert(T->lchild,k);
    }else{                           //插入到T的右子树
        return BST_Insert(T->rchild,k);
    }
}

时间复杂度为O ( h ),也有空间复杂度更低的用循环实现的非递归的实现方式

二叉树中不允许两个结点的值相等,如果我们要插入的关键字本来已经存在就不应该被插入

4. 二叉排序树的构造

插入新结点的过程

//按照str[]中的关键字序列建立二叉排序树
void Creat_BST(BSTree &T,int str[],int n){
    T=NULL;                       //初始时T为空树
    int i=0;
    while(i<n){
        BST_Insert(T,str[i]);     //依次将每个关键字插入到二叉排序树中
        i++;
    }
}

序列不同的关键字序列可能得到同款二叉排序树,也可能得到不同款二叉排序树

5. 二叉排序树的删除

先搜索找到目标结点

  • 若被删除结点z是叶结点,则直接删除,不会破坏二叉排序树的性质

  • 若结点z只有一棵左子树或右子树,则让z的子树成为z父结点的子树,替代z的位置

  • 若结点z有左右两棵子树,则令z的中序遍历的直接后继(或直接前驱)替代z,然后从二叉排序树中删去这个直接后继(或直接前驱),这样就转换成了第一种或第二种情况
    数据结构 查找_第24张图片

数据结构 查找_第25张图片

数据结构 查找_第26张图片

6. 查找效率分析

查找长度直接反映了查找操作时间复杂度,时间复杂度的数量级应该和查找长度的数量级相同
平均查找长度的数量级就是查找操作的时间复杂度,我们要追求查找操作的时间复杂度尽可能地低,因为插入和删除都需要建立在查找基础上
数据结构 查找_第27张图片
数据结构 查找_第28张图片

数据结构 查找_第29张图片

7.4 平衡二叉树

1. 平衡二叉树的定义

平衡二叉树(Balanced Binary Tree)简称平衡树(AVL树)
树上任一结点的左子树和右子树高度之差不超过1

结点的平衡因子=左子树高-右子树高

  • 显然平衡二叉树结点的平衡因子的值只可能是-1,0,1
  • 只要有任一结点的平衡因子绝对值大于1,就不是平衡二叉树
//平衡二叉树结点
typedef struct AVLNode{
    int key;                  //数据域
    int balance;              //平衡因子
    struct AVLNode *lchild,*rchild;
}AVLNode,*AVLTree;

2. 平衡二叉树的插入

上一小节的末尾提过让一棵二叉排序树保持平衡就能保证其查找效率达到O ( l o g 2 n ) 数量级,因此我们着重关心在二叉排序树中插入新结点后如何保持平衡

每次调整的对象都是“最小不平衡子树”。在插入操作中,只要将最小不平衡子树调整平衡,则其他祖先结点都会恢复平衡

数据结构 查找_第30张图片
这里讨论的是先假定插入后A是最小不平衡子树的根结点且插入新结点之后刚好导致不平衡的情况,所以要假定所有子树的高度都是H,尽管让AR高度为H+1和BR高度为H-1都能满足初始状态的树是平衡的

LL

数据结构 查找_第31张图片

1.f->lchild=p->rchild //B的右子树变为A的左子树
2.p->rchild=f;        //A结点变为B的右孩子
3.gf->lchild/rchild=p //让A的父结点原本指向A的孩子指针指向B

RR

数据结构 查找_第32张图片

1.f->rchild=p->lchild   //B结点左孩子变为A的右孩子
2.p->lchild=f;          //A结点变为B的左孩子
3.gf->lchild/rchild=p;  //让A的父结点原本指向A的孩子指针指向B

数据结构 查找_第33张图片

LR

数据结构 查找_第34张图片
数据结构 查找_第35张图片

数据结构 查找_第36张图片

RL

数据结构 查找_第37张图片
数据结构 查找_第38张图片
数据结构 查找_第39张图片

总结

对于LR RL 只要把旋转的结点直接放在不平衡的根节点,再考虑把旋转结点的左子树和右子树分别放在不平衡根节点的左边或者右边(左边就放在左边的右边,右边就放在右边的左边)

数据结构 查找_第40张图片
总结出只有左孩子才能右上旋,只有右孩子才能左上旋,每一次旋转都能导致它和它的父结点父子关系互换的规律。在LR中处理A的左孩子的右孩子,它首先是一个右孩子,所以第一步只能让它左上旋替代A以前的左孩子,然后作为左孩子它只能右旋,RL也遵循相同的规律

插入操作导致“最小不平衡子树”高度+1,经过调整后高度恢复,所以在插入操作中只要将最小不平衡树调整平衡则其他祖先结点的平衡因子都会恢复

3. 查找效率分析

数据结构 查找_第41张图片

数据结构 查找_第42张图片

4. 平衡二叉树的删除

例子1

数据结构 查找_第43张图片
数据结构 查找_第44张图片
数据结构 查找_第45张图片
数据结构 查找_第46张图片

例子2

数据结构 查找_第47张图片
数据结构 查找_第48张图片
数据结构 查找_第49张图片
数据结构 查找_第50张图片

总结

数据结构 查找_第51张图片

7.5 红黑树 RBT

1. 红黑树的定义和性质

为什么要发明红黑树?

平衡二叉树 AVL:插入/删除 很容易破坏 ”平衡“ 特性,需要频繁调整树的形态。如:插入操作导致不平衡,则需要先计算平衡因子,找到不平衡子树(时间开销大),再进行 LL/RR/LR/RL 调整

红黑树 RBT:插入/删除 很多时候不会破坏 ”红黑“ 特性,无需频繁调整树的形态。即便需要调整,一般都可以在常数级时间内完成

  • 平衡二叉树:适用于以查为主、很少插入/删除的场景

  • 红黑树:适用于频繁插入、删除的场景,实用性更强
    数据结构 查找_第52张图片

2. 红黑树的定义

数据结构 查找_第53张图片
“左根右,根叶黑,不红红,黑路同”

红黑树是二叉排序树 左子树结点值 ≤ 根结点值 ≤ 右子树结点值

与普通BST相比,有什么要求

  • 1.每个结点或是红色,或是黑色的

  • 2.根节点是黑色的(根叶黑) ”根叶黑“

  • 3.叶结点(外部结点、NULL结点、失败结点)均是黑色的

红黑树中的叶子结点并不是包含关键字的实际结点而是查找失败的结点,对应的是一个空指针

  • 4.不存在两个相邻的红结点(即红结点的父节点和孩子结点均是黑色) ”不红红“

  • 5.对每个结点,从该节点到任一叶结点的简单路径上,所含黑结点的数目相同 ”黑叶同“

struct RBnode {      // 红黑树的结点定义
    int key;         // 关键字的值
    RBnode* parent;  // 父节点指针
    RBnode* lchild;  // 左孩子指针
    RBnode* rchild;  // 右孩子指针
    int color;       // 结点颜色,如:可用 0/1 表示 黑/红,也可使用枚举型enum表示颜色
};

3. 结点的 ”黑高“

结点的黑高 bh —— 从某结点出发(不含该结点)到达任一空叶结点的路径上黑结点总数

数据结构 查找_第54张图片

4. 红黑树的性质

数据结构 查找_第55张图片
左右子树高度只差不超过2倍,所以比avl树要求更低,就更不容易修改,删除插入效率更高

根节点黑高为h的红黑树的性质

根节点黑高为h的红黑树,内部结点数(关键字)至少有多少个?至多有多少个?
内部结点数最少的情况 —— 总共h层黑结点的满树形态

如果不是满二叉树就会违反黑路同的特性
数据结构 查找_第56张图片
数据结构 查找_第57张图片

内部结点数最多的情况 —— h层黑结点,每一层黑结点下面都铺满一层红结点。
共2h层的满树形态
数据结构 查找_第58张图片

总结

在这里插入图片描述

5. 红黑树的查找

与BST、AVL相同,从根出发,左小右大,若查找到一个空叶节点,则查找失败

6. 红黑树的插入

数据结构 查找_第59张图片
数据结构 查找_第60张图片
数据结构 查找_第61张图片

7. 红黑树的删除

数据结构 查找_第62张图片

7.6 B树

数据结构 查找_第63张图片

1. 定义

B树,⼜称多路平衡查找树,B树中所有结点的孩⼦个数的最⼤值称为B树的阶,通常⽤m表示。⼀棵m阶B树或为空树,或为满⾜如下特性的m叉树:

数据结构 查找_第64张图片
数据结构 查找_第65张图片
绝对平衡所以根节点至少有两棵子树

数据结构 查找_第66张图片

2. 核心特征

数据结构 查找_第67张图片
数据结构 查找_第68张图片

数据结构 查找_第69张图片
数据结构 查找_第70张图片
数据结构 查找_第71张图片

3. B树的插入

数据结构 查找_第72张图片

1. 重点1

数据结构 查找_第73张图片
数据结构 查找_第74张图片

2. 重点2

数据结构 查找_第75张图片
数据结构 查找_第76张图片

3. 重点3

数据结构 查找_第77张图片
数据结构 查找_第78张图片

4. 重点4

数据结构 查找_第79张图片

数据结构 查找_第80张图片

数据结构 查找_第81张图片

5. 总结

数据结构 查找_第82张图片

4. B树的删除

1. 重点1

数据结构 查找_第83张图片
对非终端结点关键字的删除,必然可以转化为对终端结点的删除操作。若被删除关键字在非终端节点,则用直接前驱或直接后继来替代被删除的关键字

  • 直接前驱:当前关键字左侧指针所指子树中”最右下“的元素

  • 直接后继:当前关键字右侧指针所指子树中”最左下“的元素
    在这里插入图片描述

2. 重点2

数据结构 查找_第84张图片
删除38
数据结构 查找_第85张图片


删除90
数据结构 查找_第86张图片
数据结构 查找_第87张图片
在这里插入图片描述

3. 重点3

数据结构 查找_第88张图片
数据结构 查找_第89张图片
数据结构 查找_第90张图片
数据结构 查找_第91张图片

7.7 B+树

1. 定义

数据结构 查找_第92张图片

数据结构 查找_第93张图片

可以理解为:要追求 “绝对平衡”,即所有子树高度要相同

结点的子树个数与关键字个数相等
这也是与B树最大的不同

2. B+树的查找

B+树同时支持多路查找和顺序查找

B树查找成功,可能停在任何一层。B+树中,如果只是在分支结点找到要查找的关键字,查找并没有结束,只有找到最下层的叶子结点后才可以找到某一个关键字实际对应的记录的存放位置。无论查找成功与否,最终一定都要走到最下面一层结点
数据结构 查找_第94张图片
数据结构 查找_第95张图片

3. B+树 vs B树

数据结构 查找_第96张图片
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

数据结构 查找_第97张图片
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
数据结构 查找_第98张图片

  • 操作系统对于磁盘的读写一般以磁盘块为单位,一般B+树和B树的不同结点就存放在不同磁盘块当中,对于B+树的查找就是反复将各个结点对应的磁盘块读入内存处理,最终得到要查找的关键字对应记录的存放位置

  • 一层就是一次读磁盘

  • 在B+树中,非叶结点不含有该关键字对应记录的存储地址。可以使一个磁盘块可以包含更多个关键字,使得B+树的阶更大,树高更矮,读磁盘次数更少,查找更快。典型应用:关系数据库的“索引”(如MySQL)

4. B+和B树的总结

数据结构 查找_第99张图片

7.8 散列查找

7.8.1 散列表(Hash Table)

数据结构 查找_第100张图片

1. 定义

哈希表。是一种数据结构,特点是:数据元素的关键字与其存储地址直接相关
数据结构 查找_第101张图片
数据结构 查找_第102张图片

2. 拉链法

数据结构 查找_第103张图片

3. 开放定址法

在这里插入图片描述

7.8.2 散列函数的设计

数据结构 查找_第104张图片

除留余数法

数据结构 查找_第105张图片

数据结构 查找_第106张图片

直接定址法

数据结构 查找_第107张图片

数字分析法

数据结构 查找_第108张图片

平方取中法

数据结构 查找_第109张图片

7.8.3 处理冲突的拉链法

插入

数据结构 查找_第110张图片
数据结构 查找_第111张图片

查找

数据结构 查找_第112张图片
数据结构 查找_第113张图片

删除

数据结构 查找_第114张图片

总结

数据结构 查找_第115张图片

小优化

数据结构 查找_第116张图片

7.8.4 处理冲突的开放定址法

数据结构 查找_第117张图片

定义

数据结构 查找_第118张图片
数据结构 查找_第119张图片
数据结构 查找_第120张图片

插入操作

线性探测法

在这里插入图片描述

平方探测法

数据结构 查找_第121张图片

双散列法

数据结构 查找_第122张图片

伪随机序列法

数据结构 查找_第123张图片

删除操作

注意都只能逻辑删除
在这里插入图片描述

数据结构 查找_第124张图片
数据结构 查找_第125张图片
数据结构 查找_第126张图片

总结

数据结构 查找_第127张图片

探测覆盖率

数据结构 查找_第128张图片

数据结构 查找_第129张图片
数据结构 查找_第130张图片
数据结构 查找_第131张图片

总结

数据结构 查找_第132张图片

散列因子

数据结构 查找_第133张图片

你可能感兴趣的:(#,数据结构,考研,数据结构,学习,笔记,算法,考研)