アルゴリズム演習 - 一般的な検索アルゴリズムの繰り返し

蒋胡の名で知られる無名の大学生
原著者: jacky Li
電子メール: [email protected]

完成時期:2023.1.1
最終編集日:2023.1.1

 

目次

アルゴリズム演習 - 一般的に使用される検索アルゴリズムの再現 (追伸: 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)

ミッション詳細

関連情報

プログラミング要件

テスト命令

参照コード

作者には言いたいことがある


アルゴリズム演習 - 一般的に使用される検索アルゴリズムの再現 (追伸: 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. }

Algorithm 7.1 検索プロセスの各ステップでは、テーブル全体が検索されたかどうか、つまり、各ステップでループ変数が条件 i>=1 を満たすかどうかをチェックする必要があります。このプログラムを改善することで、この検出プロセスを省くことができます。改善方法は、前のキーに割り当てられたキーを見つけることでありST.R[0]、ここでは、アルゴリズム 7.2ST.R[0]に示すように、監視のセンチネルの役割を果たします

Algorithm 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. }

アルゴリズムの分析

Algorithm 7.2 は、プログラミング スキルの向上にすぎません。つまり、監視ポストを設定することで、検索プロセスの各ステップでテーブル全体が検索されたかどうかを確認する必要がなくなります。ただし、この改善により、順次検索が ST.length ≥ 1000 の場合、検索に必要な平均時間をほぼ半分に短縮できることが実際に証明されています。もちろんウォッチポストも高添え字でセット可能。Algorithm 7.2 は Algorithm 7.1 と同じ時間計算量を持ちます. これは Chapter 2 で分析されています. つまり, ASL=n1 ∑i=1ni i=2n+1 であり, Algorithm 7.2 の時間計算量は O(n).

プログラミング要件

プロンプトに従って、右側のエディターの Begin と End の間にコードを追加して、順次検索のタスクを完了します。

テスト命令

プラットフォームは、記述したコードをテストします。

テスト入力 (合計 2 行、1 行目は検索するキーワード、2 行目はスペースで区切られた順序リスト要素です。

4

0 1 2 3 4 5

期待される出力:

找到4位置为5

テスト入力:

7

0 1 2 3 4 5

期待される出力:

未找到7

参考代码

#include <iostream>
#include <algorithm>
#include <vector>
#include <set>
#include <map>
#include <cmath>
#include <cstring>
#include <unordered_map>
#include <unordered_set>

#define IOS std::ios::sync_with_stdio(false)
#define inf 0x3f3f3f3f
#define YES cout << "YES" << endl;
#define yes cout << "yes" << endl;
#define no cout << "no" << endl;
#define NO cout << "NO" << endl;
//#define int long long
#define x first
#define y second
//#define cmp [&](PII a, PII b){ return a.y < b.y; }

const int N = 5e5+10, mod = 1e9+7, M = 1e7+5, K = 1e5+10, Z = 2e5+7;

using namespace std;
typedef long long LL;
typedef pair<int, int> PII;

#define MAXSIZE 10000
#define OK 1;

typedef struct{
	int key;//关键字域
}ElemType;

typedef struct{
	ElemType *R;
	int length;
}SSTable;

int InitList_SSTable(SSTable &L)
{
    L.R=new ElemType[MAXSIZE];
	if (!L.R)
	{
		cout<<"初始化错误";
		return 0;
	}
	L.length=0;
	return OK;
}

int Insert_SSTable(SSTable &L) //将所有关键字输入顺序表备查
{
	/***********************Begin**********************/
	int n;
	while(scanf("%d", &n) != EOF)
		L.length ++, L.R[L.length].key = n;
	return OK;
	/*********************** End **********************/
}

int Search_Seq(SSTable ST, int key){
    //在顺序表ST中顺序查找其关键字等于key的数据元素。若找到,则函数值为
    //该元素在表中的位置,否则为0
    /***********************Begin**********************/
	for(int i = 1; i <= ST.length; i ++)
		if(ST.R[i].key == key)
			return i;
	return 0;
	/*********************** End **********************/
}// Search_Seq

void Show_End(int result,int testkey)
{
	if(result==0)
		cout<<"未找到"<<testkey<<endl;
	else
		cout<<"找到"<<testkey<<"位置为"<<result<<endl;
	return;
}
int main()
{
	int testkey1;
    scanf("%d",&testkey1);
    SSTable ST;
	InitList_SSTable(ST);
	Insert_SSTable(ST);
	int result;
	result=Search_Seq(ST, testkey1);
	Show_End(result,testkey1);
}

レベル 2: 二分探索 (アルゴリズム 7.3)

ミッション詳細

このレベルのタスク: 二分探索アルゴリズムを使用して、順序付きリスト内のキーワードの位置を見つけます。添字は 1 から数え始めます。

関連情報

二分探索とも呼ばれる二分探索 (二分探索) は、より効率的な検索方法です。ただし、バイナリ検索では、線形テーブルが順次格納構造を採用する必要があり、テーブル内の要素はキーに従って順番に配置されます。以降の説明では、順序付きリストは昇順であると想定されています。ハーフ検索の検索処理は、テーブルの真ん中のレコードから始めて、与えられた値が真ん中のレコードのキーと等しければ検索成功、与えられた値がテーブルのキーよりも大きいか小さければ検索は成功です。中間レコードの半分を検索し、検索が成功するまで操作を繰り返すか、特定のステップで検索間隔が空の場合、検索が失敗したことを意味します。二分探索 探索比較ごとに探索範囲が半減し、逐次探索に比べて明らかに探索効率が向上します。検索プロセス中に各検索間隔をマークするために、現在の検索間隔の下限と上限をそれぞれ表すために low と high が使用され、mid は間隔の中間位置です。

アルゴリズム 7.3 二分探索

【アルゴリズムステップ】

① 検索間隔の初期値を設定し、low を 1、high をテーブル長とする。② low が high 以下の場合、次の操作をループする: mid の値は low と high の中間の値である; 与えられた value key と middle 位置に記録された key が等しい場合は比較する. 、検索が成功し、中間位置が mid に返されます; ·それらが等しくない場合、中間位置レコードを使用して、テーブル ペアを前後のサブテーブルに分割します。キーが中間位置に記録されたキーワードよりも小さい場合は、高が mid-1 と見なされ、そうでない場合は低が 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 の間にコードを追加して、半分の検索タスクを完了します。

テスト命令

プラットフォームは、記述したコードをテストします。

テスト入力 (合計 2 行、1 行目は検索するキーワード、2 行目はスペースで区切られた順序リスト要素です。

4

0 1 2 3 4 5

期待される出力:

找到4位置为5

テスト入力:

7

0 1 2 3 4 5

期待される出力:

未找到7

参照コード

#include <iostream>
#include <algorithm>
#include <vector>
#include <set>
#include <map>
#include <cmath>
#include <cstring>
#include <unordered_map>
#include <unordered_set>

#define IOS std::ios::sync_with_stdio(false)
#define inf 0x3f3f3f3f
#define YES cout << "YES" << endl;
#define yes cout << "yes" << endl;
#define no cout << "no" << endl;
#define NO cout << "NO" << endl;
//#define int long long
#define x first
#define y second
//#define cmp [&](PII a, PII b){ return a.y < b.y; }

const int N = 5e5+10, mod = 1e9+7, M = 1e7+5, K = 1e5+10, Z = 2e5+7;

using namespace std;
typedef long long LL;
typedef pair<int, int> PII;

#define ERROR 0
#define MAXSIZE 2000
#define OK 1;

typedef struct{
	int key;//关键字域
}ElemType;

typedef struct{
	ElemType *R;
	int length;
}SSTable;

int InitList_SSTable(SSTable &L)
{
	L.R=new ElemType[MAXSIZE];
	if (!L.R)
	{
		cout<<"初始化错误";
		return 0;
	}
	L.length=0;
	return OK;
}

int Insert_SSTable(SSTable &L) //将所有关键字输入顺序表备查
{
/***********************Begin**********************/
	int n;
	while(scanf("%d", &n) != EOF)
		L.length ++, L.R[L.length].key = n;
	return OK;
/*********************** End **********************/
}

int Search_Bin(SSTable ST,int key) {
   // 在有序表ST中折半查找其关键字等于key的数据元素。若找到,则函数值为
   // 该元素在表中的位置,否则为0
/***********************Begin**********************/
	int low = 1, high = ST.length;
	while(low <= high)
	{
		int mid = (low + high) >> 1;
		if(ST.R[mid].key == key) return mid;
		else if(key > ST.R[mid].key) low = mid + 1;
		else high = mid - 1;
	}  
	return ERROR;
/*********************** End **********************/
}// Search_Bin

void Show_End(int result,int testkey)
{
	if(result==0)
		cout<<"未找到"<<testkey<<endl;
	else
		cout<<"找到"<<testkey<<"位置为"<<result<<endl;
	return;
}

int main()
{
	int testkey1;
    scanf("%d",&testkey1);
    SSTable ST;
	InitList_SSTable(ST);
	Insert_SSTable(ST);
	int result;
	result=Search_Bin(ST, testkey1);
	Show_End(result,testkey1);
}

レベル 3: バイナリ ソート ツリーと検索 (アルゴリズム 7.4-7.7)

ミッション詳細

このレベルのタスク: 二分ソート ツリーの作成、挿入、検索、および削除操作を実現します。

関連情報

バイナリ ソート ツリー (バイナリ ソート ツリー) は、バイナリ検索ツリーとも呼ばれ、並べ替えと検索に役立つ特別なバイナリ ツリーです。1. バイナリ ソート ツリーの定義 バイナリ ソート ツリーは、空のツリー、または次のプロパティを持つバイナリ ツリーのいずれかです。(1) 左のサブツリーが空でない場合、左のサブツリーのすべてのノードの値はそれより小さい(2) 右のサブツリーが空でない場合、右のサブツリーのすべてのノードの値は、そのルート ノードの値よりも大きくなります; (3) その左と右のサブツリーもバイナリ ソート ツリーです。二分ソートツリーは再帰的に定義されます。バイナリ ソート ツリーの重要なプロパティは、定義から引き出すことができます。バイナリ ツリーを順番にトラバースすると、ノード値が増加する順序付けられたシーケンスを取得できます。

プログラミング要件

プロンプトに従って、右側のエディターの Begin と End の間にコードを追加して、半分の検索タスクを完了します。

テスト命令

The platform will test the code you write. 以下は、テスト ケースの 2 つの例です。

テスト入力 (合計 3 行: 1 行目は、バイナリ ソート ツリーを構築するための文字シーケンスであり、行末は終了記号です。2 行目は、検索する必要がある単一の文字です#。バイナリ ソート ツリーで、3 行目は単一の文字であり、バイナリ ソート ツリーから削除する必要がある文字です):

参照コード

#include <iostream>
#include <algorithm>
#include <vector>
#include <set>
#include <map>
#include <cmath>
#include <cstring>
#include <unordered_map>
#include <unordered_set>

#define IOS std::ios::sync_with_stdio(false)
#define inf 0x3f3f3f3f
#define YES cout << "YES" << endl;
#define yes cout << "yes" << endl;
#define no cout << "no" << endl;
#define NO cout << "NO" << endl;
//#define int long long
#define x first
#define y second
//#define cmp [&](PII a, PII b){ return a.y < b.y; }

const int N = 5e5+10, mod = 1e9+7, M = 1e7+5, K = 1e5+10, Z = 2e5+7;

using namespace std;
typedef long long LL;
typedef pair<int, int> PII;

#define ENDFLAG '#'
typedef struct ElemType{	
	char key;
}ElemType;

typedef struct BSTNode{
	ElemType data;	//结点数据域
	BSTNode *lchild,*rchild;	//左右孩子指针
}BSTNode,*BSTree;


//算法7.4 二叉排序树的递归查找
BSTree SearchBST(BSTree T,char key) {
  //在根指针T所指二叉排序树中递归地查找某关键字等于key的数据元素
  //若查找成功,则返回指向该数据元素结点的指针,否则返回空指针
/***********************Begin**********************/
	if(!T || key == T -> data.key) return T;
	else if (key < T -> data.key)  return SearchBST(T -> lchild, key);
	else return SearchBST(T -> rchild, key);  
/*********************** End **********************/
} // SearchBST



//算法7.5 二叉排序树的插入
void InsertBST(BSTree &T,ElemType e ) {
  //当二叉排序树T中不存在关键字等于e.key的数据元素时,则插入该元素
/***********************Begin**********************/
	if(!T) 
	{                				
		BSTree S = new BSTNode; 
        S -> data = e, S -> lchild = S -> rchild = NULL, T = S;
	}
	else if (e.key < T -> data.key) InsertBST(T -> lchild, e);	
	else if (e.key > T -> data.key) InsertBST(T -> rchild, e);	
/*********************** End **********************/
}// InsertBST



//算法7.6 二叉排序树的创建
void CreateBST(BSTree &T ) {
  //依次读入一个关键字为key的结点,将此结点插入二叉排序树T中
/***********************Begin**********************/
	T=NULL; ElemType e; cin >> e.key;
	while(e.key != ENDFLAG)
	{
    	InsertBST(T, e);
    	cin >> e.key;			
	}   
/*********************** End **********************/
}//CreatBST

void DeleteBST(BSTree &T,char key) {
  //从二叉排序树T中删除关键字等于key的结点
/***********************Begin**********************/
	BSTree p = T, f = NULL, q, s;      //初始化

  /*------------下面的while循环从根开始查找关键字等于key的结点*p-------------*/
  while(p)
  {                  
	if(p -> data.key == key) break;  	      	//找到关键字等于key的结点*p,结束循环
	f = p;                                			//*f为*p的双亲结点
	if(p -> data.key > key)  p = p -> lchild;     	//在*p的左子树中继续查找
	else p = p -> rchild;  	                  		//在*p的右子树中继续查找
	}
	if(!p) return;                         		//找不到被删结点则返回

/*―考虑三种情况实现p所指子树内部的处理:*p左右子树均不空、无右子树、无左子树―*/
	if((p -> lchild) && (p -> rchild)) 
	{     		//被删结点*p左右子树均不空
		q = p, s = p->lchild;
		while(s -> rchild)                			//在*p的左子树中继续查找其前驱结点,即最右下结点
			q = s, s = s -> rchild;	         		//向右到尽头
		p -> data = s -> data;               			//s指向被删结点的“前驱”
		if(q != p)
			q -> rchild = s->lchild;     	//重接*q的右子树
		else q -> lchild = s -> lchild;        		//重接*q的左子树
		delete s;
	}//if
	else
	{
		if(!p->rchild)               		//被删结点*p无右子树,只需重接其左子树
			q = p, p = p -> lchild; 
		else if(! p -> lchild)               		//被删结点*p无左子树,只需重接其右子树
			q = p, p = p -> rchild;
	/*――――――――――将p所指的子树挂接到其双亲结点*f相应的位置――――――――*/
		if(!f) T = p;                       			//被删结点为根结点
		else if (q == f -> lchild) f -> lchild = p;   	//挂接到*f的左子树位置
		else f -> rchild = p;                 		//挂接到*f的右子树位置
		delete q;
	}
/*********************** End **********************/
}//DeleteBST

//算法 7.7 二叉排序树的删除

//中序遍历
void InOrderTraverse(BSTree &T)
{
/***********************Begin**********************/
	if(T)
	{
		InOrderTraverse(T -> lchild);
		cout << T->data.key;
		InOrderTraverse(T -> rchild);
	}	
/*********************** End **********************/
}

int main()
{
	BSTree T;
	CreateBST(T);
	cout<<"当前有序二叉树中序遍历结果为";
	InOrderTraverse(T);
    cout<<endl;
	char key;//待查找或待删除内容
	cin>>key;
	BSTree result=SearchBST(T,key);
	if(result)
	{cout<<"找到字符"<<key<<endl;}
	else
	{cout<<"未找到"<<key<<endl;}
	cin>>key;
	DeleteBST(T,key);
	cout<<"当前有序二叉树中序遍历结果为";
	InOrderTraverse(T);
}

 レベル 4: B ツリーの検索と挿入 (アルゴリズム 7.8 と 7.9)

ミッション詳細

このレベルのタスク: 与えられたデータに従って、B ツリーの構築、検索、および挿入を完了します。

関連情報

上記の検索方法は、まとめて内部検索方法と呼ばれる、コンピュータのメモリに格納された小さなファイルに適用できます。ファイルが非常に大きく、検索用に外部メモリに保存されている場合、これらの検索方法は適用できません。内部検索方法はすべてノード単位で検索するため、内部と外部のメモリ交換を繰り返す必要があり、非常に時間がかかります。1970 年に、R.Bayer と E.Mccreight は、外部検索に適したバランスの取れたマルチフォーク ツリーを提案しました。B ツリー、ディスク管理システムのディレクトリ管理、およびデータベース システムのほとんどのインデックス編成は B ツリーを使用します。このデータ構造。

B ツリー検索

B ツリーでの検索プロセスは、バイナリ ソート ツリーのプロセスと似ています。たとえば、下の図に示す B ツリーでは、

キーワード 47 を検索するプロセスは次のとおりです。まず、ルートから開始し、ルート ノード ポインター t に従って*aノードを検索します。*aこれは、ノードにキーワードが 1 つしかないためであり、検索されたレコードが存在する場合は 47>35 です。 、それはポインター P1 によって指されている必要があります のサブツリーで、*cポインターに沿ってノードを見つけます。ノードには 2 つのキーワード (43 と 78) があり、43<47<78、検索されたレコードが存在する場合、それはポインタ P1 が指すサブツリー。同様に、ポインタ42に沿ってノードが見つかり*g、順次ノードが検索され、キーワード47が見つかったので、検索は成功する。ルックアップが失敗した場合のプロセスも同様です。たとえば、同じツリーで 23 をルックアップします。ルートから開始すると、23<35 であるため、ノード のポインター P0 に沿って*bノードを見つけます。ノードにはキーワード 18 が 1 つしかなく、23>18 であるため、*bノード の 2 番目のポインター P1 に沿って*eノードを見つけます。同様に、23<27なのでポインタを下方向に検索すると、このときポインタは葉ノードを指しているため、キーワード23はこのB-treeには存在しないことを意味し、検索は失敗して終了します。B-tree を検索するプロセスは、ポインタに沿ってノードを検索し、ノードのキーを検索するプロセスであることがわかります。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 ツリー検索

【アルゴリズムの手順】 与えられた値キーとルートノードの各キー 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 が指す部分木を引き続き下方向に探索する. 値がキーであるキーワードがトップダウン検索プロセスで見つかった場合、検索は成功し、リーフ ノードまで見つからない場合、検索は失敗します。

B ツリーの挿入

B-tree は動的な検索木であるため、その生成プロセスは空の木から始まり、検索プロセス中にキーワードを 1 つずつ挿入することによって得られます。ただし、ルートを除く B ツリーのすべての非終端ノードのキーワードの数は ⌈m/2⌉ 以上でなければならないため、毎回キーワードを挿入してもツリーにリーフ ノードが追加されるわけではありませんが、最初に、最下層の非終端ノードにキーワードを追加します. ノード内のキーワードの数が m-1 を超えない場合、挿入は完了します. それ以外の場合は、ノードがいっぱいであることを示し、「分割」 "のノードが生成されます。"、このノードを同じレイヤーの2つのノードに分割します。一般的なノード分割方法は、中間キーワードを境にノードを2つに分割し、中間キーワードを親ノードに上向きに挿入し、親ノードが満杯の場合は同様に分解を続けます。最悪の場合、ツリーのルート ノードまで分解されており、この時点で B-tree の高さは 1 増加します。

アルゴリズム 7.9 B ツリーの挿入

【アルゴリズムの手順】 ① 与えられたキーワードのレコードを B-tree から検索し、検索に成功した場合は挿入失敗、失敗した場合は新しいレコードを空ポインタ 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 の指すノードがルート ノードになるまで手順 に進みます。 ④。④ ルートノードは親を持たないため、分割により生成された 2 つのノードのポインタ ap と q と [図] をキーワードとするレコードが新たなルートノードとなります。この時点で、B- の高さが 1 増加します。

プログラミング要件

プロンプトに従って、右側のエディターで B ツリーの挿入関数 InsertBTree と検索関数 SearchBTree のコードを完成させ、メイン関数の指定された配列に対する B ツリーの構築を完了します。

テスト命令

プラットフォームは、記述したコードをテストします。

テスト入力: なし;

期待される出力:

OK

 参照コード

//算法7.8 B-树的查找
//算法7.9 B-树的插入
#include<iostream>
using namespace std;
#define FALSE 0
#define TRUE 1
#define OK 1
#define m 3						//B-树的阶,暂设为3
typedef struct BTNode{
	int keynum;					//结点中关键字的个数,即结点的大小
	BTNode *parent;				//指向双亲结点
	int key[m+1];				//关键字矢量,0号单元未用
	BTNode *ptr[m+1];			//子树指针矢量
}BTNode,*BTree;

//- - - - - B-树的查找结果类型定义- - - - -
struct Result{
  BTNode *pt;     							//指向找到的结点
  int i;           							//1..m,在结点中的关键字序号
  int tag;         							//1:查找成功,0:查找失败
}; 	                           


int Search(BTree T,int key)
{
	BTree p=T;	
	int endnum;
	if(p)						//树不为空时
	{
		endnum=p->keynum;		//获得首节点包含的记录个数
	}
	else
	{
		return 0;				//返回没找到
	}
	int i=0;
	if(endnum==0)
	{
		return i;				//树存在,但仅有一个为空根节点
	}
	else if(key>=p->key[endnum])//节点不为空,但当前值比最大的key还大
	{
		i=endnum;
		return i;
	}
	else if(key<=p->key[1])		//节点不为空,但当前值比最小的key还小
	{
		return i;}
	else
	{
		for(i=1;i<endnum;i++)	//有合适的位置,即处于当前结点的最大和最小值之间,或找到了
		{
			if(p->key[i]<=key && key<p->key[i+1])
				return i;
		}
	}
}

void Insert(BTree &q,int i,int x,BTree &ap)
{//将x插入q结点的i+1位置中
	int j;
	for(j=m-1;j>i;j--)			
	{
		//将插入位置之后的key全部后移一位
		q->key[j+1]=q->key[j];
	}
	for(j=m;j>i;j--)
	{
		//相应地也移动其后ptr的位置
		q->ptr[j]=q->ptr[j-1];
	}
	q->key[i+1]=x;//插入x到该位置
	q->ptr[i+1]=ap;
	q->keynum++;
}

void split(BTree &q,int s,BTree &ap)
{	//将q->key[s+1,..,m], q->ptr[s+1,..,m]移入新结点*ap作为右结点
	//原结点作为新的左侧结点
	//中间值被保存在ap[0]->key中,等待找到跳转回InsertBTree()寻找到到合适的插入位置插入	
	int i;
	ap=new BTNode;
	for(i=s+1;i<=m;i++)
	{	//将q->key[s+1,..,m]保存到ap->key[0,..,m-s+1]中
		//将q->ptr[s+1,..,m]保存到ap->ptr[0,..,m-s+1]中
		ap->key[i-s-1]=q->key[i];	
		ap->ptr[i-s-1]=q->ptr[i];
	}
	if(ap->ptr[0])
	{
		//当ap有子树的时候
		for(i=0;i<=1;i++)
		{
			//将ap的子树的父亲改为ap自己
			ap->ptr[i]->parent=ap;
		}
	}
	ap->keynum=(m-s)-1;
	ap->parent=q->parent;//将ap的父亲改为q的父亲
	q->keynum=q->keynum-(m-s);//修改q的记录个数
}

void NewRoot(BTree &T,BTree q,int x,BTree &ap)//生成含信息(T, x, ap)的新的根结点*T,原T和ap为子树指针
{
	BTree newT=new BTNode;//新建一个结点作为新的根
	
	newT->key[1]=x;//写入新根的key[1]
	newT->ptr[0]=T;//将原来的树根作为新根的左子树
	newT->ptr[1]=ap;//ap作为新根的右子树
	newT->keynum=1;
	newT->parent=NULL;//新根的父亲为空

	ap->parent=newT;//ap的父亲为新根
	T->parent=newT;//T的父亲为新根

	T=newT;//树改成新根引导的
}

//算法7.9 B-树的插入
int InsertBTree(BTree &T,int K,BTree q,int i){
/***********************Begin**********************/



/*********************** End **********************/
}							//InsertBTree

//算法7.8 B-树的查找
Result SearchBTree(BTree &T, int key){
/***********************Begin**********************/



/*********************** End **********************/
}//SearchBTree

void InitialBTree(BTree &T)
{
	//初始化一个空的根
	T->keynum=0;		
	T->parent=NULL;	
	for(int i=0;i<m+1;i++)
	{
		T->ptr[i]=NULL;
	}
}

int main()
{
	BTree T=new BTNode;
	InitialBTree(T);
	//先用SearchBTree()找到要插入的位置,得到一个Result结构体
	//再用InsertBTree()插入数据
	Result result;
	int a[11]={45,24,53,90,3,12,50,61,70,100};
	for(int i=0;i<10;i++)
	{
		result=SearchBTree(T,a[i]);
		if(result.tag==0)
		{
			InsertBTree(T,a[i],result.pt,result.i);
		}
	}
	cout<<"OK";
}

レベル 5: ハッシュ テーブル ルックアップ (アルゴリズム 7.10)

ミッション詳細

このレベルのタスク: ハッシュ テーブル検索メソッドを使用して、リスト内のキーワードの位置を見つけます。添字は 1 から数え始めます。

関連情報

上記の線形テーブルとツリー テーブル構造に基づく検索方法は、すべてキーワードの比較に基づいています。検索プロセスでは、各要素のキー間の相対的なサイズのみが考慮されます. ストレージ構造内のレコードの位置は、そのキーに直接関係していません. 検索時間は、特にテーブルの長さに関連しています.検索の際、無効なノードの多数のキーワードを比較する必要があり、検索速度が非常に遅くなります。要素の格納場所とそのキーワードの間に直接の関係が確立できる場合、検索時に比較を行う必要がないか、比較をほとんど行わずに、この関係に従ってキーワードによって対応するレコードを直接見つけることができます。これが、要素のキーワード値に対して何らかの演算を行うことで要素のアドレスを直接計算する、つまりキーワードから比較を繰り返さずにアドレスを指定します。そのため、ハッシュルックアップ方式は、ハッシュ方式またはハッシュ方式とも呼ばれます。いくつかの一般的に使用される用語: (1)ハッシュ関数とハッシュ アドレス: レコードの格納場所 p とそのキーワード キーの間に明確な対応 H を確立し、p=H(キー) とします。この対応は H と呼ばれ、ハッシュ関数です。 、p はハッシュ アドレスです。(2)ハッシュテーブル: 対応するハッシュアドレスがハッシュ関数に従って計算されるデータレコードを格納するために使用される限定された連続したアドレス空間。通常、ハッシュ テーブルの格納領域は 1 次元配列であり、ハッシュ アドレスは配列の添字です。(3)競合と同義語: key1≠key2 であり、H(key1)=H(key2) という異なるキーワードに対して同じハッシュ アドレスが取得される場合があります。この現象を競合と呼びます。同じ関数値を持つキーワードは、ハッシュ関数の同義語と呼ばれ、key1 と key2 は互いに同義語と呼ばれます。ハッシュ関数を構築するための一般的な方法には、数値分析平方センタリング折りたたみ、および除算と剰余があります。コンフリクトへの対処手段としては、主にオープンアドレス方式チェーン アドレス方式には2 種類あります。オープン アドレス方式を例にとると、ハッシュ テーブルの格納表現は次のようになります。

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

アルゴリズムの手順

ハッシュ テーブルを検索するプロセスは、基本的にハッシュ テーブルを作成するプロセスと同じです。Algorithm 7.10 では、ハッシュ テーブルの衝突に対処するオープン アドレス方式 (線形検出方式) のルックアップ処理について説明します。

アルゴリズム 7.10 ハッシュ テーブルでのルックアップ

①検索するキーワードkeyを与え、テーブル作成時に設定したハッシュ関数よりH0=H(key)を計算する。②ユニットH0が空の場合、チェックした要素は存在しません。③ ユニット H0 内の要素のキーワードがキーであれば検索成功。④ それ以外の場合は、次の競合解決プロセスを繰り返す: · 競合解決の方法に従って、次のハッシュ アドレス Hi を計算する; · ユニット Hi が空の場合、チェック対象の要素が存在しない; , 検索は成功.

アルゴリズムの説明

例は次のとおりです。

  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 つの要因に依存します。ハッシュ テーブルの充填係数 α は、次のように定義されます。α = ハッシュ テーブルの長さ。テーブルに充填されたレコードの数 α は、ハッシュ テーブルの完全性を示します。直感的には、α が小さいほど競合が発生する可能性は低く、逆に α が大きいほどテーブルに多くのレコードが埋められており、レコードを補充するときに競合する可能性が高くなります。特定の値を比較する必要があるキーワード。(3) ハッシュ関数の「良いか悪いか」は、最初に競合の頻度に影響します。ただし、一般に、任意の「均一な」ハッシュ関数は、ランダムなキーワードの同じグループに対して競合する可能性が同じであると考えられています. 設定されたハッシュ関数が「均一な」場合、平均検索長に影響します. 要因は2つだけです. - コンフリクトの処理方法とフィル ファクター α。4) 表 7.3 から、ハッシュ テーブルの平均検索長は、レコード数 n の関数ではなく、α の関数であることがわかります。したがって、ハッシュテーブルを設計する場合、n がいくら大きくても、常に適切な α を選択して、平均検索長を範囲内に収めることができます。

プログラミング要件

プロンプトに従って、右側のエディタの Begin と End の間にコードを追加して、ハッシュ テーブル ルックアップのタスクを完了します。

テスト命令

プラットフォームは、記述したコードをテストします。

テスト入力 (合計 2 行、1 行目は検索するキーワード、2 行目はスペースで区切られたハッシュ テーブル内の要素です。

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

期待される出力:

未找到

参考代码

#include<iostream>
#include<stdio.h>
using namespace std;

//算法7.10 哈希表的查找
//- - - - -开放地址法哈希表的存储表示- - - - -
#define m 16                        			//哈希表的表长
#define NULLKEY 0                 			//单元为空的标记

struct HashTable{
   int  key;		         	 			//关键字项
// InfoType  otherinfo;					//其他数据项
};

//	算法7.10为哈希表查找的算法,采用线性探测法处理冲突。
//	【算法实现】

int H(int key)
{
	int result;
	result=key%13;
	return result;
} 

int SearchHash(HashTable HT[],int key){
  //在哈希表HT中查找关键字为key的元素,若查找成功,返回哈希表的单元标号,否则返回-1 
  /***********************Begin**********************/
  for(int i=1;i<=m;i++)
  {
  	if(HT[i].key==key)
  	  return i;
  }

return -1;
  
  /*********************** End **********************/
}//SearchHash

int main()
{	
	int result,i=0;
	int a[m];
	char end=' ';
	int lookfor;		//lookfor为待查找的元素 
	scanf("%d",&lookfor);
	for(;i<m;i++)
	{
		scanf("%d",&a[i]);
	}
	HashTable HT[m];
	for(i=0;i<16;i++)
	{
		HT[i].key=a[i];
	}
	result=SearchHash(HT,lookfor);
	if(result!=-1)
	{
		cout<<"在第"<<result<<"位置找到"<<endl;
	}
	else
	{
		cout<<"未找到"<<endl;
	}
}

作者には言いたいことがある

ブロガーの発言が役に立ったと思われる場合は、クリックしてサポートしてください。そのような問題は引き続き更新されます...

おすすめ

転載: blog.csdn.net/weixin_62075168/article/details/128513547