0
点赞
收藏
分享

微信扫一扫

算法之查找


数据结构之查找

  • ​​静态查找表​​
  • ​​顺序查找​​
  • ​​折半查找​​
  • ​​次优查找树​​
  • ​​分块查找​​
  • ​​动态查找表​​
  • ​​二叉排序树​​
  • ​​平衡二叉树​​
  • ​​B树​​
  • ​​B+树​​
  • ​​键树​​
  • ​​哈希表​​

查找:在数据集合中寻找满足某种条件的数据元素的过程。

查找表:用于查找的数据集合,由同一类型(或记录)的组成,可以是一个数组或链表等数据类型。

关键字:数据元素中唯一标识该元素的某个数据项的值,使用基于关键字的查找,查找结果应该是唯一的。

平均查找长度:查找时,关键字比较次数的平均值。

静态查找表

查询某个特定的数据元素是否在查找表中,检索满足条件的某个特定的数据元素的各种属性。

以下来分别介绍各种方法:

顺序查找

顺序查找:顺序表的查找,又称线性查找,主要用于在线性表中进行查找

对无序线性表进行查找,查找失败则要遍历整个线性表

#include <iostream>
#include <cstdio>
int main() {
int arr[6] = { 1, 2, 4, 5, 6, 7 };
int keyword = 4;
for(auto a : arr) {
if (a == keyword) {
printf("successful!");
break;
}
}

}

折半查找

折半查找:有序表的查找,又称二分查找,仅适用于有序的线性表,元素中各元素等概率

算法思想:

  • 首先将给定key值同表中中间位置元素的关键字比较
  • 若相等,则返回该元素的位置
  • 若不等,则在前半部分或者后半部分进行查找
  • 查找序列升序时,若key小于中间元素,则查找前半部分,若key大于中间元素,则查找后半部分

int BinaryFind(int arr[], int length, int keyword) {
int low = 0, high = length - 1, mid;
while(low <= high) { // At this point they have traversed the entire table
mid = low + (high - low)/2;
if (keyword == arr[mid]) {
return mid;
} else if(keyword > arr[mid]) {
low = mid + 1;
} else {
high = mid - 1;
}
}
return -1;
}

我直接来写一个折半查找的例子

#include <iostream>
#include <cstdio>

int BinaryFind(int*, int, int);
void InsertSort(int* , int);

int main() {
/* 对arr数组中的元素进行折半查找
*/
int arr[6] = {3, 4, 6, 5, 2, 1}; // at this point they are out of order
int length = sizeof(arr) / sizeof(arr[0]);
// 先用希尔排序进行排序
InsertSort(arr, length);
int keyword;
printf("please input a number for you: \n");
std::cin >> keyword;
// 用折半查找进行查找
int position = BinaryFind(arr, length, keyword);
std::cout << position << std::endl;
// std::cout << "Hello World!" << std::endl;
return 0;
}
// the algorithm for shellsort
void InsertSort(int arr[], int n) {
for (int gap = n/2; gap > 0; gap /= 2) {
int i, j;
for (i = gap; i < n; i++) {
int key = arr[i];
for (j = i; j > 0 && key < arr[j - gap] && j-gap >= 0; j-=gap) {
arr[j] = arr[j - gap];
}
arr[j] = key;
}
}
}
// the algorithm for binaryfind
int BinaryFind(int arr[], int length, int keyword) {
int low = 0, high = length - 1, mid;
while(low <= high) { // At this point they have traversed the entire table
mid = low + (high - low)/2;
if (keyword == arr[mid]) {
return mid;
} else if(keyword > arr[mid]) {
low = mid + 1;
} else {
high = mid - 1;
}
}
return -1;
}

次优查找树

次优查找树:静态树表的查找,在对于序列中元素的权值不同而确定

分块查找

分块查找:索引顺序表,又称索引顺序查找,他吸取了顺序查找和折半查找各自的优点,既有动态结构,又适用于快速查找

如何分块?

  • 将查找表分为若干个子块,块内的元素可以无序,但块间是有序的,即对于所有块有第i块的最小关键字小于i+1块的所有记录的关键字
  • 建立索引表,索引表中的每个元素含有各块的最大关键字和各块中的第一个元素的地址,索引表按关键字有序排列

如何查找?

  • 在索引表中确定待查记录所在的块,可以顺序查找或折半查找索引表
  • 在块内进行顺序查找

/* 分块查找是不太推荐的
* 因为在划分层次的很难划分
* 当然,如果你们有好方法,可以告诉我,我可以进行修正
*/
#include <stdio.h>
#define MAXL 100 //数据表的最大长度
#define MAXI 20 //索引表的最大长度
typedef int KeyType;
typedef char InfoType[10];
typedef struct
{
KeyType key; //KeyType为关键字的数据类型
InfoType data; //其他数据
} NodeType;
typedef NodeType SeqList[MAXL]; //顺序表类型

typedef struct
{
KeyType key; //KeyType为关键字的类型
int link; //指向对应块的起始下标
} IdxType;
typedef IdxType IDX[MAXI]; //索引表类型

int IdxSearch(IDX I,int m,SeqList R,int n,KeyType k)
{
int low=0,high=m-1,mid,i;
int b=n/m; //b为每块的记录个数
while (low<=high) //在索引表中进行二分查找,找到的位置存放在low中
{
mid=(low+high)/2;
if (I[mid].key>=k)
high=mid-1;
else
low=mid+1;
}
//应在索引表的high+1块中,再在线性表中进行顺序查找
i=I[high+1].link;
while (i<=I[high+1].link+b-1 && R[i].key!=k) i++;
if (i<=I[high+1].link+b-1)
return i+1;
else
return 0;
}

int main()
{
int i,n=25,m=5,j;
SeqList R;
IDX I= {{14,0},{34,5},{66,10},{85,15},{100,20}};
KeyType a[]= {8,14,6,9,10,22,34,18,19,31,40,38,54,66,46,71,78,68,80,85,100,94,88,96,87};
KeyType x=85;
for (i=0; i<n; i++)
R[i].key=a[i];
j=IdxSearch(I,m,R,n,x);
if (j!=0)
printf("%d是第%d个数据\n",x,j);
else
printf("未找到%d\n",x);
return 0;
}

动态查找表

查询某个特定的数据元素是否在查找表中,检索满足条件的某个特定的数据元素的各种属性,在查找表中插入一个数据元素,在查找表中删除一个数据元素

二叉排序树

二叉排序树:BST,二叉查找树,或为空,若非空时:

  • 若左子树非空,则左子树上所有结点关键字值均小于根节点的关键字;
  • 若右子树非空,则右子树上所有结点关键字值均大于根节点的关键字;
  • 左右子树本身也是一棵二叉排序树
  • 即左子树结点值 < 根结点 < 右子树结点值(中序遍历的方向是:左子树—根节点—右子树)

因此!二叉排序树的中序遍历是一个递增的有序序列!

二叉排序树的构建

  • 读入一个元素并建立节点,若二叉排序树为空将其作为根节点;
  • 若二叉树非空,当值小于根节点时,插入左子树;
  • 当值大于根节点,插入右子树;
  • 当值等于根节点不进行插入

// str[]:保存要插入值的数组,n:要插入的数量
void Create_BST(BiTree &T, KeyType str[], int n) {
T = NULL;
int i = 0;
while (i < n) {
// 从根结点进行查找
BST_Insert(T, str[i]);
i++;
}
}
int BST_Insert(BiTree &T, KeyType k) {
if (T == NULL) {
T = new BSTNode();
T->key = k;
T->lchild = T->rchild = NULL;
return 1;
}
// 查看是否有这个结点的值
else if (k == T->key)
return 0;
// 跟根结点比较
else if (k < T->key)
return BST_Insert(T->lchild, k);
else
return BST_Insert(T->rchild, k);
}

查找逻辑:
二叉树非空时,查找根节点,若相等则查找成功;
若不等,当小于根节点查找左子树,当大于根节点查找右子树,当查找到叶节点仍没查找相应的值,则查找失败。

// T:二叉树,key:要查找的元素
BSTNode *BST_Search(BiTree T, ElemType key) {
while(T!=nullptr && key != T->data) {
if(key < T->data) {
T = T->lchild;
} else {
T = T->rchild;
}
}
return T;
}

删除逻辑:
若被删除结点是叶节点,直接删除
若被删除结点z只有一棵子树,则让z的子树成为z父结点的子树,代替z结点
若被删除结点z只有两棵子树,则让z的中序序列直接后继代替z,并后继直接后继结点

平衡二叉树

平衡二叉树:AVL树,任意结点的平衡因子的绝对值不超过1,(左子树高度-右子树高度)

平衡二叉树的判断:(后序遍历)

  • 判断左子树是一棵平衡二叉树
  • 判断右子树是一棵平衡二叉树
  • 判断以该结点为根的二叉树为平衡二叉树

void Judge_AVL(BiTree bt, int &balance, int &h) {
// bl/hl表示左子树是否平衡和高度,同理
int bl = 0, br = 0, hl = 0, hr = 0;
// 判断是不是根结点
if(bt == NULL) {
// h:总体高度
h = 0;
balance = 1;
}
// 判断是不是叶结点
else if (bt->lchild==NULL && bt->rchild==NULL) {
h = 1;
balance = 1;
}
else {
Judge_AVL(bt->lchild, bl, hl);
Judge_AVL(bt->rchild, br, hr);
// 左右没有太大区别
if (hl > hr)
h = hl + 1;
else
h = hr + 1;

if (abs(hl - hr) < 2 && b1 == 1 && br = 1)
balance = 1;
else
balance = 0;
}
}

插入:先插入(在二叉排序中中进行插入),再调整(每次调整最小不平衡子树):

LL平衡旋转(右单旋转)

原因:在结点A的左孩子的左子树上插入了新结点

调整方法:右旋操作:将A的左孩子B代替A,将A结点称为B的右子树根节点,而B的原右子树作为A 的左子树(我的理解就是将根结点进行了一个右折,如果右边有结点,就往下推)

算法之查找_子树


RR平衡旋转(左单旋转)

原因:在结点A 的右孩子的右子树上插入了新结点

调整方法:左旋操作:将A 的右孩子代替A,将A 结点称为B的左子树根节点,而B的原左子树则作为A的右子树

算法之查找_数据_02


LR平衡旋转(先左后右双旋转)

原因:在结点A的左孩子的右子树上插入了新结点

B树

B树:又称多路平衡查找树,B树中所有结点的孩子结点数的最大值称为B树的阶。
一棵m阶B树或为空树,或为满足如下特性的m叉树:

  • 树中每个结点至多有m棵子树(即至多含有m-1个关键字)
  • 若根结点不是终端结点,则至少有两颗子树
  • 除根结点外的所有非叶结点至少有[m/2]棵子树(即[m/2]-1个关键字)
  • 非叶结点的结构,Ki为结点的关键字,Pi为子树根结点的指针,Pi-1所指子树的关键字均小于Ki,Pi所指子树的关键字均大于Ki,
  • 所有叶结点都出现在同一层次上,并不带任何信息

算法之查找_算法_03


插入:

  • 若插入后,不会破坏m阶B树的定义,即插入后结点关键字个数在属于[[m/2]-1, m-1],则直接插入
  • 若插入后,关键字数量大于m-1,则对插入后的结点进行分裂操作

如图:

算法之查找_算法_04


进行分裂(增加1个阶,其父节点的关键字最大值也自然增加了一位)

算法之查找_算法_05


删除:

终端结点:最底层的非叶子结点

非终端结点:其他结点

算法之查找_算法_06


对于终端结点:

  • 直接删除:若被删除关键字所在结点关键字个数>[m/2]-1,表面删除后仍满足B树定义,直接删除

非终端结点

算法之查找_子树_07

B+树

B+树

键树

键树

哈希表


举报

相关推荐

0 条评论