当前位置:   article > 正文

算法练习-常用查找算法复现_本关任务:实现折半查找。相关知识为了完成本关任务,你需要掌握:1.静态查找表

本关任务:实现折半查找。相关知识为了完成本关任务,你需要掌握:1.静态查找表

一个不知名大学生,江湖人称菜狗
original author: jacky Li
Email : 3435673055@qq.com

Time of completion:2023.1.1
Last edited: 2023.1.1

 

目录

算法练习-常用查找算法复现(PS:1 -- 3自己写的,4、5懒得写了,直接拿的同学的)

第1关:顺序查找(算法7.1和7.2)

任务描述

相关知识

编程要求

测试说明

参考代码

第2关:折半查找(算法7.3)

任务描述

相关知识

编程要求

测试说明

参考代码

第3关:二叉排序树和查找(算法7.4-7.7)

任务描述

相关知识

编程要求

测试说明

参考代码

 第4关:B- 树的查找和插入(算法7.8和7.9)

任务描述

相关知识

编程要求

测试说明

 参考代码

第5关:散列表查找(算法7.10)

任务描述

相关知识

编程要求

测试说明

参考代码

作者有言


算法练习-常用查找算法复现(PS:1 -- 3自己写的,4、5懒得写了,直接拿的同学的)

第1关:顺序查找(算法7.1和7.2)

任务描述

本关任务:用顺序查找法找出关键字在列表中的位置,下标由1开始计数。

相关知识

顺序查找(Sequential Search)的查找过程为:从表的一端开始,依次将记录的关键字和给定值进行比较,若某个记录的关键字和给定值相等,则查找成功;反之,若扫描整个表后,仍未找到关键字和给定值相等的记录,则查找失败。

数据元素类型定义

 
  1. typedef struct{
  2. KeyType key; //关键字域
  3. InfoType otherinfo; //其他域
  4. }ElemType;

顺序表的定义

 
  1. typedef struct{
  2. ElemType *R; //存储空间基地址
  3. int length; //当前长度
  4. }SSTable;

算法7.1 顺序查找

算法描述

示例如下:

  1. int Search_Seq(SSTable ST,KeyType key)
  2. {//在顺序表ST中顺序查找其关键字等于key的数据元素。
  3. //若找到,则函数值为该元素在表中的位置,否则为0
  4. for(i=ST.length;i>=1;--i)
  5. if(ST.R[i].key==key) return i; //从后往前找
  6. return 0;
  7. }

算法7.1在查找过程中每步都要检测整个表是否查找完毕,即每步都要有循环变量是否满足条件i>=1的检测。改进这个程序,可以免去这个检测过程。改进方法是查找之前先对ST.R[0]的关键字赋值key,在此,ST.R[0]起到了监视哨的作用,如算法7.2所示。

算法7.2 设置监视哨的顺序查找

算法描述

  1. int Search_Seq(SSTable ST,KeyType key)
  2. {//在顺序表ST中顺序查找其关键字等于key的数据元素。若找到,则函数值为该元素在表中的位置,否则为0
  3. ST.R[0].key=key;//“哨兵”
  4. for(i=ST.length;ST.R[i].key!=key;--i);//从后往前找
  5. return i;
  6. }

算法分析

算法7.2仅是一个程序设计技巧上的改进,即通过设置监视哨,免去查找过程中每一步都要检测整个表是否查找完毕。然而实践证明,这个改进能使顺序查找在ST.length≥1000时,进行一次查找所需的平均时间几乎减少一半。当然,监视哨也可设在高下标处。算法7.2和算法7.1的时间复杂度一样,在第2章已经做过分析,即 ASL=n1​∑i=1n​i=2n+1​ 算法7.2的时间复杂度为O(n)。

编程要求

根据提示,在右侧编辑器Begin和End间补充代码,完成顺序查找之任务。

测试说明

平台会对你编写的代码进行测试:

测试输入(共两行,第一行为待查找的关键字,第二行为以空格分隔的顺序表各元素:

4

0 1 2 3 4 5

预期输出:

找到4位置为5

测试输入:

7

0 1 2 3 4 5

预期输出:

未找到7

参考代码

  1. #include <iostream>
  2. #include <algorithm>
  3. #include <vector>
  4. #include <set>
  5. #include <map>
  6. #include <cmath>
  7. #include <cstring>
  8. #include <unordered_map>
  9. #include <unordered_set>
  10. #define IOS std::ios::sync_with_stdio(false)
  11. #define inf 0x3f3f3f3f
  12. #define YES cout << "YES" << endl;
  13. #define yes cout << "yes" << endl;
  14. #define no cout << "no" << endl;
  15. #define NO cout << "NO" << endl;
  16. //#define int long long
  17. #define x first
  18. #define y second
  19. //#define cmp [&](PII a, PII b){ return a.y < b.y; }
  20. const int N = 5e5+10, mod = 1e9+7, M = 1e7+5, K = 1e5+10, Z = 2e5+7;
  21. using namespace std;
  22. typedef long long LL;
  23. typedef pair<int, int> PII;
  24. #define MAXSIZE 10000
  25. #define OK 1;
  26. typedef struct{
  27. int key;//关键字域
  28. }ElemType;
  29. typedef struct{
  30. ElemType *R;
  31. int length;
  32. }SSTable;
  33. int InitList_SSTable(SSTable &L)
  34. {
  35. L.R=new ElemType[MAXSIZE];
  36. if (!L.R)
  37. {
  38. cout<<"初始化错误";
  39. return 0;
  40. }
  41. L.length=0;
  42. return OK;
  43. }
  44. int Insert_SSTable(SSTable &L) //将所有关键字输入顺序表备查
  45. {
  46. /***********************Begin**********************/
  47. int n;
  48. while(scanf("%d", &n) != EOF)
  49. L.length ++, L.R[L.length].key = n;
  50. return OK;
  51. /*********************** End **********************/
  52. }
  53. int Search_Seq(SSTable ST, int key){
  54. //在顺序表ST中顺序查找其关键字等于key的数据元素。若找到,则函数值为
  55. //该元素在表中的位置,否则为0
  56. /***********************Begin**********************/
  57. for(int i = 1; i <= ST.length; i ++)
  58. if(ST.R[i].key == key)
  59. return i;
  60. return 0;
  61. /*********************** End **********************/
  62. }// Search_Seq
  63. void Show_End(int result,int testkey)
  64. {
  65. if(result==0)
  66. cout<<"未找到"<<testkey<<endl;
  67. else
  68. cout<<"找到"<<testkey<<"位置为"<<result<<endl;
  69. return;
  70. }
  71. int main()
  72. {
  73. int testkey1;
  74. scanf("%d",&testkey1);
  75. SSTable ST;
  76. InitList_SSTable(ST);
  77. Insert_SSTable(ST);
  78. int result;
  79. result=Search_Seq(ST, testkey1);
  80. Show_End(result,testkey1);
  81. }

第2关:折半查找(算法7.3)

任务描述

本关任务:用折半查找算法查找关键字在有序列表中的位置,下标从1开始计数。

相关知识

折半查找(Binary Search)也称二分查找,它是一种效率较高的查找方法。但是,折半查找要求线性表必须采用顺序存储结构,而且表中元素按关键字有序排列。在下面及后续的讨论中,均假设有序表是递增有序的。 折半查找的查找过程为:从表的中间记录开始,如果给定值和中间记录的关键字相等,则查找成功;如果给定值大于或者小于中间记录的关键字,则在表中大于或小于中间记录的那一半中查找,这样重复操作,直到查找成功,或者在某一步中查找区间为空,则代表查找失败。 折半查找每一次查找比较都使查找范围缩小一半,与顺序查找相比,很显然会提高查找效率。为了标记查找过程中每一次的查找区间,下面分别用low和high来表示当前查找区间的下界和上界,mid为区间的中间位置。

算法7.3 折半查找

【算法步骤】

① 置查找区间初值,low为1,high为表长。 ② 当low小于等于high时,循环执行以下操作: ·mid取值为low和high的中间值; ·将给定值key与中间位置记录的关键字进行比较,若相等则查找成功,返回中间位置mid; ·若不相等则利用中间位置记录将表对分成前、后两个子表。如果key比中间位置记录的关键字小,则high取为mid-1,否则low取为mid+1。 ③ 循环结束,说明查找区间为空,则查找失败,返回0。 。

【算法描述】

  1. int Search_Bin(SSTable ST,KeyType key)
  2. {//在有序表ST中折半查找其关键字等于key的数据元素。若找到,则函数值为该元素在表中的位置,否则为0
  3. low=1;high=ST.length; //置查找区间初值
  4. while(low<=high)
  5. {
  6. mid=(low+high)/2;
  7. if(key==ST.R[mid].key) return mid; //找到待查元素
  8. else if(key<ST.R[mid].key) high=mid-1; //继续在前一子表进行查找
  9. else low=mid+1; //继续在后一子表进行查找
  10. } //while
  11. return 0; //表中不存在待查元素
  12. }

本算法很容易理解,唯一需要注意的是,循环执行的条件是low<=high,而不是low<high,因为low=high时,查找区间还有最后一个结点,还要进一步比较。 折半查找算法7.3的平均查找长度为 ASL=nn+1​log2​(n+1)−1 当n较大时,时间复杂度有近似结果O(log2​n)

编程要求

根据提示,在右侧编辑器Begin和End间补充代码,完成折半查找之任务。

测试说明

平台会对你编写的代码进行测试:

测试输入(共两行,第一行为待查找的关键字,第二行为以空格分隔的顺序表各元素:

4

0 1 2 3 4 5

预期输出:

找到4位置为5

测试输入:

7

0 1 2 3 4 5

预期输出:

未找到7

参考代码

  1. #include <iostream>
  2. #include <algorithm>
  3. #include <vector>
  4. #include <set>
  5. #include <map>
  6. #include <cmath>
  7. #include <cstring>
  8. #include <unordered_map>
  9. #include <unordered_set>
  10. #define IOS std::ios::sync_with_stdio(false)
  11. #define inf 0x3f3f3f3f
  12. #define YES cout << "YES" << endl;
  13. #define yes cout << "yes" << endl;
  14. #define no cout << "no" << endl;
  15. #define NO cout << "NO" << endl;
  16. //#define int long long
  17. #define x first
  18. #define y second
  19. //#define cmp [&](PII a, PII b){ return a.y < b.y; }
  20. const int N = 5e5+10, mod = 1e9+7, M = 1e7+5, K = 1e5+10, Z = 2e5+7;
  21. using namespace std;
  22. typedef long long LL;
  23. typedef pair<int, int> PII;
  24. #define ERROR 0
  25. #define MAXSIZE 2000
  26. #define OK 1;
  27. typedef struct{
  28. int key;//关键字域
  29. }ElemType;
  30. typedef struct{
  31. ElemType *R;
  32. int length;
  33. }SSTable;
  34. int InitList_SSTable(SSTable &L)
  35. {
  36. L.R=new ElemType[MAXSIZE];
  37. if (!L.R)
  38. {
  39. cout<<"初始化错误";
  40. return 0;
  41. }
  42. L.length=0;
  43. return OK;
  44. }
  45. int Insert_SSTable(SSTable &L) //将所有关键字输入顺序表备查
  46. {
  47. /***********************Begin**********************/
  48. int n;
  49. while(scanf("%d", &n) != EOF)
  50. L.length ++, L.R[L.length].key = n;
  51. return OK;
  52. /*********************** End **********************/
  53. }
  54. int Search_Bin(SSTable ST,int key) {
  55. // 在有序表ST中折半查找其关键字等于key的数据元素。若找到,则函数值为
  56. // 该元素在表中的位置,否则为0
  57. /***********************Begin**********************/
  58. int low = 1, high = ST.length;
  59. while(low <= high)
  60. {
  61. int mid = (low + high) >> 1;
  62. if(ST.R[mid].key == key) return mid;
  63. else if(key > ST.R[mid].key) low = mid + 1;
  64. else high = mid - 1;
  65. }
  66. return ERROR;
  67. /*********************** End **********************/
  68. }// Search_Bin
  69. void Show_End(int result,int testkey)
  70. {
  71. if(result==0)
  72. cout<<"未找到"<<testkey<<endl;
  73. else
  74. cout<<"找到"<<testkey<<"位置为"<<result<<endl;
  75. return;
  76. }
  77. int main()
  78. {
  79. int testkey1;
  80. scanf("%d",&testkey1);
  81. SSTable ST;
  82. InitList_SSTable(ST);
  83. Insert_SSTable(ST);
  84. int result;
  85. result=Search_Bin(ST, testkey1);
  86. Show_End(result,testkey1);
  87. }

第3关:二叉排序树和查找(算法7.4-7.7)

任务描述

本关任务:实现二叉排序树的创建、插入、查找、删除操作。

相关知识

二叉排序树(Binary Sort Tree)又称二叉查找树,它是一种对排序和查找都很有用的特殊二叉树。 1.二叉排序树的定义二叉排序树或者是一棵空树,或者是具有下列性质的二叉树: (1)若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; (2)若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; (3)它的左、右子树也分别为二叉排序树。 二叉排序树是递归定义的。由定义可以得出二叉排序树的一个重要性质:中序遍历一棵二叉树时可以得到一个结点值递增的有序序列。

编程要求

根据提示,在右侧编辑器Begin和End间补充代码,完成折半查找之任务。

测试说明

平台会对你编写的代码进行测试,以下为两个测试用例的例子。

测试输入(共3行:第1行,为构造二叉排序树的字符序列,行末为结束符号#;第2行为单个字符,是需要在二叉排序树中查找的字符;第3行为单个字符,是需要从二叉排序树中删除的字符):

参考代码

  1. #include <iostream>
  2. #include <algorithm>
  3. #include <vector>
  4. #include <set>
  5. #include <map>
  6. #include <cmath>
  7. #include <cstring>
  8. #include <unordered_map>
  9. #include <unordered_set>
  10. #define IOS std::ios::sync_with_stdio(false)
  11. #define inf 0x3f3f3f3f
  12. #define YES cout << "YES" << endl;
  13. #define yes cout << "yes" << endl;
  14. #define no cout << "no" << endl;
  15. #define NO cout << "NO" << endl;
  16. //#define int long long
  17. #define x first
  18. #define y second
  19. //#define cmp [&](PII a, PII b){ return a.y < b.y; }
  20. const int N = 5e5+10, mod = 1e9+7, M = 1e7+5, K = 1e5+10, Z = 2e5+7;
  21. using namespace std;
  22. typedef long long LL;
  23. typedef pair<int, int> PII;
  24. #define ENDFLAG '#'
  25. typedef struct ElemType{
  26. char key;
  27. }ElemType;
  28. typedef struct BSTNode{
  29. ElemType data; //结点数据域
  30. BSTNode *lchild,*rchild; //左右孩子指针
  31. }BSTNode,*BSTree;
  32. //算法7.4 二叉排序树的递归查找
  33. BSTree SearchBST(BSTree T,char key) {
  34. //在根指针T所指二叉排序树中递归地查找某关键字等于key的数据元素
  35. //若查找成功,则返回指向该数据元素结点的指针,否则返回空指针
  36. /***********************Begin**********************/
  37. if(!T || key == T -> data.key) return T;
  38. else if (key < T -> data.key) return SearchBST(T -> lchild, key);
  39. else return SearchBST(T -> rchild, key);
  40. /*********************** End **********************/
  41. } // SearchBST
  42. //算法7.5 二叉排序树的插入
  43. void InsertBST(BSTree &T,ElemType e ) {
  44. //当二叉排序树T中不存在关键字等于e.key的数据元素时,则插入该元素
  45. /***********************Begin**********************/
  46. if(!T)
  47. {
  48. BSTree S = new BSTNode;
  49. S -> data = e, S -> lchild = S -> rchild = NULL, T = S;
  50. }
  51. else if (e.key < T -> data.key) InsertBST(T -> lchild, e);
  52. else if (e.key > T -> data.key) InsertBST(T -> rchild, e);
  53. /*********************** End **********************/
  54. }// InsertBST
  55. //算法7.6 二叉排序树的创建
  56. void CreateBST(BSTree &T ) {
  57. //依次读入一个关键字为key的结点,将此结点插入二叉排序树T中
  58. /***********************Begin**********************/
  59. T=NULL; ElemType e; cin >> e.key;
  60. while(e.key != ENDFLAG)
  61. {
  62. InsertBST(T, e);
  63. cin >> e.key;
  64. }
  65. /*********************** End **********************/
  66. }//CreatBST
  67. void DeleteBST(BSTree &T,char key) {
  68. //从二叉排序树T中删除关键字等于key的结点
  69. /***********************Begin**********************/
  70. BSTree p = T, f = NULL, q, s; //初始化
  71. /*------------下面的while循环从根开始查找关键字等于key的结点*p-------------*/
  72. while(p)
  73. {
  74. if(p -> data.key == key) break; //找到关键字等于key的结点*p,结束循环
  75. f = p; //*f为*p的双亲结点
  76. if(p -> data.key > key) p = p -> lchild; //在*p的左子树中继续查找
  77. else p = p -> rchild; //在*p的右子树中继续查找
  78. }
  79. if(!p) return; //找不到被删结点则返回
  80. /*―考虑三种情况实现p所指子树内部的处理:*p左右子树均不空、无右子树、无左子树―*/
  81. if((p -> lchild) && (p -> rchild))
  82. { //被删结点*p左右子树均不空
  83. q = p, s = p->lchild;
  84. while(s -> rchild) //在*p的左子树中继续查找其前驱结点,即最右下结点
  85. q = s, s = s -> rchild; //向右到尽头
  86. p -> data = s -> data; //s指向被删结点的“前驱”
  87. if(q != p)
  88. q -> rchild = s->lchild; //重接*q的右子树
  89. else q -> lchild = s -> lchild; //重接*q的左子树
  90. delete s;
  91. }//if
  92. else
  93. {
  94. if(!p->rchild) //被删结点*p无右子树,只需重接其左子树
  95. q = p, p = p -> lchild;
  96. else if(! p -> lchild) //被删结点*p无左子树,只需重接其右子树
  97. q = p, p = p -> rchild;
  98. /*――――――――――将p所指的子树挂接到其双亲结点*f相应的位置――――――――*/
  99. if(!f) T = p; //被删结点为根结点
  100. else if (q == f -> lchild) f -> lchild = p; //挂接到*f的左子树位置
  101. else f -> rchild = p; //挂接到*f的右子树位置
  102. delete q;
  103. }
  104. /*********************** End **********************/
  105. }//DeleteBST
  106. //算法 7.7 二叉排序树的删除
  107. //中序遍历
  108. void InOrderTraverse(BSTree &T)
  109. {
  110. /***********************Begin**********************/
  111. if(T)
  112. {
  113. InOrderTraverse(T -> lchild);
  114. cout << T->data.key;
  115. InOrderTraverse(T -> rchild);
  116. }
  117. /*********************** End **********************/
  118. }
  119. int main()
  120. {
  121. BSTree T;
  122. CreateBST(T);
  123. cout<<"当前有序二叉树中序遍历结果为";
  124. InOrderTraverse(T);
  125. cout<<endl;
  126. char key;//待查找或待删除内容
  127. cin>>key;
  128. BSTree result=SearchBST(T,key);
  129. if(result)
  130. {cout<<"找到字符"<<key<<endl;}
  131. else
  132. {cout<<"未找到"<<key<<endl;}
  133. cin>>key;
  134. DeleteBST(T,key);
  135. cout<<"当前有序二叉树中序遍历结果为";
  136. InOrderTraverse(T);
  137. }

 第4关:B- 树的查找和插入(算法7.8和7.9)

任务描述

本关任务:根据给定数据完成B-树的构建、查找和插入。

相关知识

前面介绍的查找方法均适用于存储在计算机内存中较小的文件,统称为内查找法。若文件很大且存放于外存进行查找时,这些查找方法就不适用了。内查找法都以结点为单位进行查找,这样需要反复地进行内、外存的交换,是很费时的。1970年,R.Bayer和E.Mccreight提出了一种适用于外查找的平衡多叉树——B-树,磁盘管理系统中的目录管理,以及数据库系统中的索引组织多数都采用B-树这种数据结构。

B-树的查找

在B-树上进行查找的过程和二叉排序树的查找类似。例如,在下图所示的B-树上

查找关键字47的过程如下:首先从根开始,根据根结点指针t找到*a结点,因*a结点中只有一个关键字,且47>35,若查找的记录存在,则必在指针P1所指的子树内,顺指针找到*c结点,该结点有两个关键字(43和78),而43<47<78,若查找的记录存在,则必在指针P1所指的子树中。同样,顺指针找到*g结点,在该结点中顺序查找,找到关键字47,由此,查找成功。 查找不成功的过程也类似,例如,在同一棵树中查找23。从根开始,因为23<35,则顺该结点中指针P0找到*b结点,又因为*b 结点中只有一个关键字18,且23>18,所以顺结点中第二个指针P1找到*e结点。同理,因为23<27,则顺指针往下找,此时因指针所指为叶子结点,说明此棵B-树中不存在关键字23,查找因失败而告终。 由此可见,在B-树上进行查找的过程是一个顺指针查找结点,和在结点的关键字中查找交叉进行的过程。 由于B-树主要用做文件的索引,因此它的查找涉及外存的存取,在此略去外存的读写,只做示意性的描述。假设结点类型定义如下:

  1. #define m 3 // B-树的阶,暂设为3
  2. typedef struct BTNode
  3. {
  4. int keynum;//结点中关键字的个数,即结点的大小
  5. struct BTNode *parent; //指向双亲结点
  6. KeyType K[m+1];//关键字向量,0号单元未用
  7. struct BTNode *ptr[m+1];//子树指针向量
  8. Record *recptr[m+1]; //记录指针向量,0号单元未用
  9. }BTNode,*BTree; //B-树结点和B-树的类型
  10. typedef struct
  11. {
  12. BTNode *pt; //指向找到的结点
  13. int i; //1..m,在结点中的关键字序号
  14. int tag; //1:查找成功,0:查找失败
  15. }Result; //B-树的查找结果类型

算法7.8 B-树的查找

【算法步骤】 将给定值key与根结点的各个关键字K1, K2, …, Kj(1≤j≤m−1)进行比较,由于该关键字序列是有序的,所以查找时可采用顺序查找,也可采用折半查找。查找时: ① 若key=Ki(1≤i≤j),则查找成功; ② 若key<K1,则顺着指针P0所指向的子树继续向下查找; ③ 若Ki<key<Ki+1(1≤i≤j−1),则顺着指针Pi所指向的子树继续向下查找; ④ 若key>Kj,则顺着指针Pj所指向的子树继续向下查找。 如果在自上而下的查找过程中,找到了值为key的关键字,则查找成功;如果直到叶子结点也未找到,则查找失败。

B-树的插入

B-树是动态查找树,因此其生成过程是从空树起,在查找的过程中通过逐个插入关键字而得到。但由于B-树中除根之外的所有非终端结点中的关键字个数必须大于等于⌈m/2⌉,因此,每次插入一个关键字不是在树中添加一个叶子结点,而是首先在最低层的某个非终端结点中添加一个关键字,若该结点的关键字个数不超过m−1,则插入完成,否则表明结点已满,要产生结点的“分裂”,将此结点在同一层分成两个结点。一般情况下,结点分裂方法是:以中间关键字为界把结点一分为二,成为两个结点,并把中间关键字向上插入到双亲结点上,若双亲结点已满,则采用同样的方法继续分解。最坏的情况下,一直分解到树根结点,这时B-树高度增加1。

算法7.9 B-树的插入

【算法步骤】 ① 在B-树中查找给定关键字的记录,若查找成功,则插入操作失败;否则将新记录作为空指针ap插入到查找失败的叶子结点的上一层结点(由q指向)中。 ② 若插入新记录和空指针后,q指向的结点的关键字个数未超过m−1,则插入操作成功,否则转入步骤③。 ③ 以该结点的第⌈m/2⌉个关键字K⌈m/2⌉​为拆分点,将该结点分成3个部分:K⌈m/2⌉​左边部分、K⌈m/2⌉​、K⌈m/2⌉​右边部分。K⌈m/2⌉​左边部分仍然保留在原结点中;K⌈m/2⌉​右边部分存放在一个新创建的结点(由ap指向)中;关键字值为K⌈m/2⌉​的记录和指针ap插入到q的双亲结点中。因q的双亲结点增加一个新的记录,所以必须对q的双亲结点重复②和③的操作,依次类推,直至由q指向的结点是根结点,转入步骤④。 ④ 由于根结点无双亲,则由其分裂产生的两个结点的指针ap和q,以及关键字为[插图]的记录构成一个新的根结点。此时,B-的高度增加1。

编程要求

根据提示,在右侧编辑器补全B-树的插入函数InsertBTree和查找函数SearchBTree的代码,完成对主函数给定数组的B-树构建。

测试说明

平台会对你编写的代码进行测试:

测试输入:无;

预期输出:

OK

 参考代码

  1. //算法7.8 B-树的查找
  2. //算法7.9 B-树的插入
  3. #include<iostream>
  4. using namespace std;
  5. #define FALSE 0
  6. #define TRUE 1
  7. #define OK 1
  8. #define m 3 //B-树的阶,暂设为3
  9. typedef struct BTNode{
  10. int keynum; //结点中关键字的个数,即结点的大小
  11. BTNode *parent; //指向双亲结点
  12. int key[m+1]; //关键字矢量,0号单元未用
  13. BTNode *ptr[m+1]; //子树指针矢量
  14. }BTNode,*BTree;
  15. //- - - - - B-树的查找结果类型定义- - - - -
  16. struct Result{
  17. BTNode *pt; //指向找到的结点
  18. int i; //1..m,在结点中的关键字序号
  19. int tag; //1:查找成功,0:查找失败
  20. };
  21. int Search(BTree T,int key)
  22. {
  23. BTree p=T;
  24. int endnum;
  25. if(p) //树不为空时
  26. {
  27. endnum=p->keynum; //获得首节点包含的记录个数
  28. }
  29. else
  30. {
  31. return 0; //返回没找到
  32. }
  33. int i=0;
  34. if(endnum==0)
  35. {
  36. return i; //树存在,但仅有一个为空根节点
  37. }
  38. else if(key>=p->key[endnum])//节点不为空,但当前值比最大的key还大
  39. {
  40. i=endnum;
  41. return i;
  42. }
  43. else if(key<=p->key[1]) //节点不为空,但当前值比最小的key还小
  44. {
  45. return i;}
  46. else
  47. {
  48. for(i=1;i<endnum;i++) //有合适的位置,即处于当前结点的最大和最小值之间,或找到了
  49. {
  50. if(p->key[i]<=key && key<p->key[i+1])
  51. return i;
  52. }
  53. }
  54. }
  55. void Insert(BTree &q,int i,int x,BTree &ap)
  56. {//将x插入q结点的i+1位置中
  57. int j;
  58. for(j=m-1;j>i;j--)
  59. {
  60. //将插入位置之后的key全部后移一位
  61. q->key[j+1]=q->key[j];
  62. }
  63. for(j=m;j>i;j--)
  64. {
  65. //相应地也移动其后ptr的位置
  66. q->ptr[j]=q->ptr[j-1];
  67. }
  68. q->key[i+1]=x;//插入x到该位置
  69. q->ptr[i+1]=ap;
  70. q->keynum++;
  71. }
  72. void split(BTree &q,int s,BTree &ap)
  73. { //将q->key[s+1,..,m], q->ptr[s+1,..,m]移入新结点*ap作为右结点
  74. //原结点作为新的左侧结点
  75. //中间值被保存在ap[0]->key中,等待找到跳转回InsertBTree()寻找到到合适的插入位置插入
  76. int i;
  77. ap=new BTNode;
  78. for(i=s+1;i<=m;i++)
  79. { //将q->key[s+1,..,m]保存到ap->key[0,..,m-s+1]中
  80. //将q->ptr[s+1,..,m]保存到ap->ptr[0,..,m-s+1]中
  81. ap->key[i-s-1]=q->key[i];
  82. ap->ptr[i-s-1]=q->ptr[i];
  83. }
  84. if(ap->ptr[0])
  85. {
  86. //当ap有子树的时候
  87. for(i=0;i<=1;i++)
  88. {
  89. //将ap的子树的父亲改为ap自己
  90. ap->ptr[i]->parent=ap;
  91. }
  92. }
  93. ap->keynum=(m-s)-1;
  94. ap->parent=q->parent;//将ap的父亲改为q的父亲
  95. q->keynum=q->keynum-(m-s);//修改q的记录个数
  96. }
  97. void NewRoot(BTree &T,BTree q,int x,BTree &ap)//生成含信息(T, x, ap)的新的根结点*T,原T和ap为子树指针
  98. {
  99. BTree newT=new BTNode;//新建一个结点作为新的根
  100. newT->key[1]=x;//写入新根的key[1]
  101. newT->ptr[0]=T;//将原来的树根作为新根的左子树
  102. newT->ptr[1]=ap;//ap作为新根的右子树
  103. newT->keynum=1;
  104. newT->parent=NULL;//新根的父亲为空
  105. ap->parent=newT;//ap的父亲为新根
  106. T->parent=newT;//T的父亲为新根
  107. T=newT;//树改成新根引导的
  108. }
  109. //算法7.9 B-树的插入
  110. int InsertBTree(BTree &T,int K,BTree q,int i){
  111. /***********************Begin**********************/
  112. /*********************** End **********************/
  113. } //InsertBTree
  114. //算法7.8 B-树的查找
  115. Result SearchBTree(BTree &T, int key){
  116. /***********************Begin**********************/
  117. /*********************** End **********************/
  118. }//SearchBTree
  119. void InitialBTree(BTree &T)
  120. {
  121. //初始化一个空的根
  122. T->keynum=0;
  123. T->parent=NULL;
  124. for(int i=0;i<m+1;i++)
  125. {
  126. T->ptr[i]=NULL;
  127. }
  128. }
  129. int main()
  130. {
  131. BTree T=new BTNode;
  132. InitialBTree(T);
  133. //先用SearchBTree()找到要插入的位置,得到一个Result结构体
  134. //再用InsertBTree()插入数据
  135. Result result;
  136. int a[11]={45,24,53,90,3,12,50,61,70,100};
  137. for(int i=0;i<10;i++)
  138. {
  139. result=SearchBTree(T,a[i]);
  140. if(result.tag==0)
  141. {
  142. InsertBTree(T,a[i],result.pt,result.i);
  143. }
  144. }
  145. cout<<"OK";
  146. }

第5关:散列表查找(算法7.10)

任务描述

本关任务:用散列表查找法找出关键字在列表中的位置,下标由1开始计数。

相关知识

前面讨论的基于线性表、树表结构的查找方法,这些查找方法都是以关键字的比较为基础的。在查找过程中只考虑各元素关键字之间的相对大小,记录在存储结构中的位置和其关键字无直接关系,其查找时间与表的长度有关,特别是当结点个数很多时,查找时要大量地与无效结点的关键字进行比较,致使查找速度很慢。如果能在元素的存储位置和其关键字之间建立某种直接关系,那么在进行查找时,就无需做比较或做很少次的比较,按照这种关系直接由关键字找到相应的记录。这就是散列查找法(Hash Search)的思想,它通过对元素的关键字值进行某种运算,直接求出元素的地址,即使用关键字到地址的直接转换方法,而不需要反复比较。因此,散列查找法又叫杂凑法或散列法。 常用的几个术语: (1)散列函数和散列地址:在记录的存储位置p和其关键字key之间建立一个确定的对应关系H,使p=H(key),称这个对应关系H为散列函数,p为散列地址。 (2)散列表:一个有限连续的地址空间,用以存储按散列函数计算得到相应散列地址的数据记录。通常散列表的存储空间是一个一维数组,散列地址是数组的下标。 (3)冲突和同义词:对不同的关键字可能得到同一散列地址,即key1≠key2,而H(key1)=H(key2),这种现象称为冲突。具有相同函数值的关键字对该散列函数来说称作同义词,key1与key2互称为同义词。 构造散列函数的常用方法包括数字分析法平方取中法折叠法除留余数法。 处理冲突的手段主要有开放地址法链地址法两类。 下面以开放地址法为例,给出散列表的存储表示。

  1. //- - - - -开放地址法散列表的存储表示- - - - -
  2. #define m 20 //散列表的表长
  3. typedef struct{
  4. KeyType key; //关键字项
  5. InfoType otherinfo; //其他数据项
  6. }HashTable[m];

算法步骤

在散列表上进行查找的过程和创建散列表的过程基本一致。算法7.10描述了开放地址法(线性探测法)处理冲突的散列表的查找过程。

算法7.10 散列表的查找

① 给定待查找的关键字key,根据造表时设定的散列函数计算H0=H(key)。 ② 若单元H0为空,则所查元素不存在。 ③ 若单元H0中元素的关键字为key,则查找成功。 ④ 否则重复下述解决冲突的过程: ·按处理冲突的方法,计算下一个散列地址Hi; ·若单元Hi为空,则所查元素不存在; ·若单元Hi中元素的关键字为key,则查找成功。

算法描述

示例如下:

  1. #define NULLKEY 0 //单元为空的标记
  2. int SearchHash(HashTable HT,KeyType key)
  3. {//在散列表HT中查找关键字为key的元素,若查找成功,返回散列表的单元标号,否则返回-1
  4. H0=H(key);
  5. //根据散列函数H(key)计算散列地址
  6. if(HT[H0].key==NULLKEY) return -1; //若单元H0为空,则所查元素不存在
  7. else if(HT[H0].key==key) return H0; //若单元H0中元素的关键字为key,则查找成功
  8. else
  9. {
  10. for(i=1;i<m;++i){
  11. Hi=(H0+i)%m; //按照线性探测法计算下一个散列地址Hi
  12. if(HT[Hi].key==NULLKEY) return -1; //若单元Hi为空,则所查元素不存在
  13. else if(HT[Hi].key==key) return Hi; //若单元Hi中元素的关键字为key,则查找成功
  14. } //for
  15. return -1;
  16. } //else
  17. }

算法分析

从散列表的查找过程可见: (1)虽然散列表在关键字与记录的存储位置之间建立了直接映像,但由于“冲突”的产生,使得散列表的查找过程仍然是一个给定值和关键字进行比较的过程。因此,仍需以平均查找长度作为衡量散列表查找效率的量度。 (2)查找过程中需和给定值进行比较的关键字的个数取决于三个因素:散列函数、处理冲突的方法和散列表的装填因子。散列表的装填因子α定义为即 α=散列表的长度表中填入的记录数​ α标志散列表的装满程度。直观地看,α越小,发生冲突的可能性就越小;反之,α越大,表中已填入的记录越多,再填记录时,发生冲突的可能性就越大,则查找时,给定值需与之进行比较的关键字的个数也就越多。 (3)散列函数的“好坏”首先影响出现冲突的频繁程度。但一般情况下认为:凡是“均匀的”散列函数,对同一组随机的关键字,产生冲突的可能性相同,假如所设定的散列函数是“均匀”的,则影响平均查找长度的因素只有两个——处理冲突的方法和装填因子α。 4)从表7.3可以看出,散列表的平均查找长度是α的函数,而不是记录个数n的函数。由此,在设计散列表时,不管n多大,总可以选择合适的α以便将平均查找长度限定在一个范围内。

编程要求

根据提示,在右侧编辑器Begin和End间补充代码,完成散列表查找之任务。

测试说明

平台会对你编写的代码进行测试:

测试输入(共两行,第一行为待查找的关键字,第二行为以空格分隔的散列表中各元素:

55

-1 14 1 68 27 55 19 20 84 79 23 11 10 -1 -1 -1

预期输出:

在第5位置找到

测试输入:

47

-1 14 1 68 27 55 19 20 84 79 23 11 10 -1 -1 -1

预期输出:

未找到

参考代码

  1. #include<iostream>
  2. #include<stdio.h>
  3. using namespace std;
  4. //算法7.10 哈希表的查找
  5. //- - - - -开放地址法哈希表的存储表示- - - - -
  6. #define m 16 //哈希表的表长
  7. #define NULLKEY 0 //单元为空的标记
  8. struct HashTable{
  9. int key; //关键字项
  10. // InfoType otherinfo; //其他数据项
  11. };
  12. // 算法7.10为哈希表查找的算法,采用线性探测法处理冲突。
  13. // 【算法实现】
  14. int H(int key)
  15. {
  16. int result;
  17. result=key%13;
  18. return result;
  19. }
  20. int SearchHash(HashTable HT[],int key){
  21. //在哈希表HT中查找关键字为key的元素,若查找成功,返回哈希表的单元标号,否则返回-1
  22. /***********************Begin**********************/
  23. for(int i=1;i<=m;i++)
  24. {
  25. if(HT[i].key==key)
  26. return i;
  27. }
  28. return -1;
  29. /*********************** End **********************/
  30. }//SearchHash
  31. int main()
  32. {
  33. int result,i=0;
  34. int a[m];
  35. char end=' ';
  36. int lookfor; //lookfor为待查找的元素
  37. scanf("%d",&lookfor);
  38. for(;i<m;i++)
  39. {
  40. scanf("%d",&a[i]);
  41. }
  42. HashTable HT[m];
  43. for(i=0;i<16;i++)
  44. {
  45. HT[i].key=a[i];
  46. }
  47. result=SearchHash(HT,lookfor);
  48. if(result!=-1)
  49. {
  50. cout<<"在第"<<result<<"位置找到"<<endl;
  51. }
  52. else
  53. {
  54. cout<<"未找到"<<endl;
  55. }
  56. }

作者有言

如果感觉博主讲的对您有用,请点个关注支持一下吧,将会对此类问题持续更新……

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/593372
推荐阅读
相关标签
  

闽ICP备14008679号