DS博客作业05--查找
0.PTA得分截图
1.本周学习总结
1.1 总结查找内容
- 查找的性能指标ASL
1.顺序查找
- 即从左往右顺着查找关键字,成功返回元素序号+1,失败返回0.
int SeqSearch(SeqList R,int n,KeyType k)
{ int i=0;
while (i<n && R[i].key!=k) //从左往右找
i++;
if (i>=n) //未找到返回0
return 0;
else
return i+1;//找到返回逻辑序号i+1
}
2.二分查找
- 二分查找就是每次查找优先跳到正中间的元素进行查找,也称折半查找,但要求所有节点必须已经按递增或递减顺序排列好。
- 在查找成功时,会找到图中某个圆形节点,则成功时的平均查找长度:
- 不成功时,会找到图中某个方形节点,将会是一个序号范围。
Search(SeqList R,int n,KeyType k)
{ int low=0,high=n-1,mid;
while (low<=high) //当前区间存在元素时循环
{ mid=(low+high)/2;
if (R[mid].key==k)//查找成功
return mid+1;
if (k<R[mid].key)
high=mid-1;
else
low=mid+1; }
return 0;
}
动态查找:二叉搜索树
1.如何创建二叉搜索树
- 即遍历数组,调用插入函数
代码
BSTNode *CreatBST(KeyType A[],int n) //返回树根指针
{ BSTNode *bt=NULL; //初始时bt为空树
int i=0;
while (i<n)
{
InsertBST(bt,A[i]); //将A[i]插入二叉排序树T中
i++;
}
return bt; //返回建立的二叉排序树的根指针
}
2.插入
代码如下
void InsertBST(BSTree& T, int e)//向树中插入元素
{
if (!T)//初始树为空时
{
T = new BSTNode;
T->Key = e;
T->Left = NULL;
T->Right = NULL;
}
else if (e > T->Key)//键值大的插入左子树
InsertBST(T->Left, e);
else if (e < T->Key)//键值小的插入右子树
InsertBST(T->Right, e);
}
3.删除
- 1.删除的节点是叶子节点:直接删去该节点。
- 2.删除的节点只有左子树或者只有右子树:用其左子树或者右子树代替它。
- 3.删除的节点既有左子树,也有右子树:
- 以其前驱替代之,然后再删除该前驱节点。前驱是左子树中最大的节点。也可以用其后继替代之,然后再删除该后继节点。后继是右子树中最小的节点。
代码
void Delete(BSTreee &p) //从二叉排序树中删除*p节点
{ BSTNode *q;
if (p->rchild==NULL) //*p节点没有右子树的情况
{
q=p;p=p->lchild;
delete q;
}
else if (p->lchild==NULL) //*p节点没有左子树
{
q=p;p=p->rchild;
delete q;
}
else Delete1(p,p->lchild);//*p节点既有左子树又有右子树的情况
}
AVL树的定义及4种调整做法
AVL树的定义
- AVL树又叫平衡二叉树即满足两个条件。
- 1.左、右子树是平衡二叉树
- 2.所有结点的左、右子树深度之差的绝对值≤ 1
- 平衡因子:该结点左子树与右子树的高度差
AVL树的4种调整
- LL平衡旋转
- RR平衡旋转
- LR平衡旋转
- RL平衡旋转
B-树和B+树定义
- B-树和B+树:一个节点可放多个关键字,降低树的高度。可放外存,适合大数据量查找。如数据库中数据。B-树又称为多路平衡查找树.
B-树的插入
- 关键字插入的位置必定在叶子结点层,有下列几种情况:
- 1.该结点的关键字个数n<m-1,不修改指针;
- 2.该结点的关键字个数 n=m-1,则需进行“结点分裂”。
- 1.如果没有双亲结点,新建一个双亲结点,树的高度增加一层。
- 2.如果有双亲结点,将ki插入到双亲结点中。
B-树的删除
- 1.结点中关键字的个数>[m/2]-1,直接删除
- 2.结点中关键字的个数=[m/2]-1则需要分情况讨论:
- 要从其左(或右)兄弟结点“借调”关键字。
- 若其左和右兄弟结点均无关键字可借(结点中只有最少量的关键字),则必须进行结点的“合并”。
假如b结点的关键字个数大于Min,说明删去该关键字后该结点仍满足B树的定义,则可直接删去该关键字。
假如b结点的关键字个数等于Min,说明删去关键字后该结点将不满足B树的定义。若可以从兄弟结点借。
- 1.兄弟结点最小关键字上移双亲结点
- 2.双亲结点大于删除关键字的关键字下移删除结点
b结点的关键字个数等Min,兄弟节点关键字个数也等于Min
- 1.删除关键字
- 2.兄弟节点及删除关键字节点、双亲结点中分割二者关键字合并一个新叶子结点
- 3.若双亲结点关键字个数<=Min,重复2
m阶的B+树和m阶的B-树的差异
(1)非根结点关键字个数n不同、n取值范围不同
- B+树中:一个节点n个孩子则对应n个关键字
取值范围:m/2≤n≤m,根节点是1≤n≤m;
- B-树中:一个节点n个孩子则对应n-1个关键字
取值范围: m/2-1≤n≤m-1 ,根节点 1≤n≤m-1
根节点孩子至少为2.
(2)叶子结点不一样
- B+树所有叶子节点包含了全部关键字
- B-树叶子节点包含的关键字与其他节点包含的关键字是不重复的。
(3)B+树中所有非叶子节点仅起到索引的作用,而在B-树中,每个关键字对应一个记录的存储地址
(4)通常在B+树上有两个头指针,一个指向根节点,另一个指向关键字最小的叶子节点,所有叶子节点链接成一个不定长的线性链表。
散列查找
- 哈希表(Hash Table)又称散列表,是除顺序表存储结构、链接表存储结构和索引表存储结构之外的又一种存储线性表的存储结构。
哈希表的建立
int InsertHT(HashTable ha,int p,int k,int &n){
int adr,i;
adr=k % p;
if(adr==NULLKEY || adr==DELKEY) //地址为空,可插入数据
{ ha[adr].key=k;ha[adr].count=1;}
else
{ i=1;
while(ha[adr].key!=NULLKEY && ha[adr].key!=DELKEY)
{
adr=(adr+1) % m;
i++;}//查找插入位置
ha[adr].key=k;ha[adr].count=i; //找到插入位置
}
n++;
}
哈希链的建立
void CreateHash(HashTable ht[],int n)
{
建n条带头结点的哈希链;
for i=1 to k:
输入数据data
InsertHash(ht,data)
}
ASL的计算
- 这里除了以往的基础算法外,我们还新学习了一种算法,“拉链法”。
- 拉链法是把所有的同义词用单链表链接起来的方法。
1.2.谈谈你对查找的认识及学习体会。
- 查找的知识全是全新的知识,但由于我的进度有些慢,查找方面的知识都是看课件学来的,所以,方法都和老师的差不多,希望在我熟悉知识以后可以利用创新思维找到一些较为简便的方法。
2.PTA题目介绍
2.1 7-5(哈希链) 航空公司VIP客户查询
2.1.1 该题的设计思路及伪代码
- 第一思路就是用map将客户的id与里程形成映射,然后用id查找添加里程或输出里程。但是怕超时。所以就想用哈希做,但是用哈希又不知道每个客户的里程要怎么保存,后来问了同学,发现可以用结构体来保存。
2.1.2 该题的伪代码
声明一个map容器且对应类型都是long long的名称为vip
定义整型变量IDNum存省份证号
定义字符型变量tail存身份证最后一位
定义distance存里程数
输入n,k//n代表n组数,k代表最低里程
while n-- do
输入IDNum,tail,distance
若tail为x则将IDNum置为负数
iter为IDNum在map中的位置
if 此IDNum不在vip中 do
若distance小于最低里程k,则置为k
将IDNum和distance的对应存入vip中
else do //IDNum存在
若distance小于最低里程k,则置为k
在该用户拥有的里程数基础上加上distance
end if
end while
输入m
while m-- do//查找身份证所包含的里程数
输入IDNum和tail
若tail为x则将IDNum置为负数
找IDNum在vip中的位置
找到输出里程数,找不到输出No Info
end while
2.1.3 PTA提交列表
- 一开始用了string类型定义身份证号,导致了超时,因为数据太多,后来将其改成long long才缩短运行时间。
2.1.4 本题设计的知识点
- 本题我觉得最关键的是一定要会用map函数,真的特别方便,然后要注意string类型定义时的超时问题,因为数据太多就很容易超时,再一个就是要熟悉哈希的基本知识。
2.2 是否完全二叉搜索树
2.2.1 该题的设计思路
- 刚开始想的是没有左叶子只有右叶子的节点就可以,但后来画图发现不对的,如果左、右子树都完全,且深度相差超过2就符合了条件,但却就不是完全二叉树了。后来想的是标号,但这种方法最底层节点的NULL和缺的点的NULL是一样的,无法区分。最后查了一下解题思路,还是用广搜的思路,用队列把所有节点都层序入队,所有的NULL也入队,如果遇到第一个NULL就退出,开始遍历队列里剩下的指针,如果全为NULL,就是完全二叉树。
2.2.2 该题的伪代码
2.2.3 PTA提交列表
- 这题因为在中国大学mooc上听了主要的解题思路,所以一次就过了。
2.2.4 本题设计的知识点
- 此题主要考察判断完全二叉树思路:对树进行广度优先遍历,当遇到空洞时所有结点都遍历完了,则该树为完全二叉树,否则为非完全二叉树。
- 这题主要考察的还有建树,但我觉得想清楚了用数组会更简单。