0
点赞
收藏
分享

微信扫一扫

网络安全:通过445端口暴力破解植入木马。

m逆光生长 2023-05-01 阅读 80

一、线性表的查找

1. 顺序查找(线性查找)

1.1 基本概念

1.2 代码

#include <iostream>
using namespace std;

// 全局变量 查找表
#define LEN	(10)
int searchTable[LEN + 1] = { 0, 1, 16, 24, 35, 47, 59, 62, 73, 88, 99 };	// 查找表元素从下标1开始存储

/**
* @brief:		常规顺序查找
* @param a:		顺序表指针
* @param n:		顺序表长度
* @param key:	要查找的关键字
* @return:		若查找成功,则返回key所在下标,否则返回0
*/
int sequentialSearch(int* a, int n, int key)
{
	for (size_t i = 1; i <= n; i++)
	{
		if (a[i] == key)
		{
			return i;	// 查找成功
		}
	}
	return 0;	// 查找失败
}

int main(int argc, char* argv[])
{
	int key = 199;
	int pos = sequentialSearch(searchTable, LEN, key);
	if (pos)
	{
		cout << "the index of " << key << " is " << pos << endl;
	}
	else
	{
		cout << "not found" << endl;
	}
}

从上面代码可以看出,除了要进行 a [ i ] a[i] a[i]与关键字 k e y key key是否相等比较外,每次循环还需要对 i i i是否越界进行检查。为了避免每次循环的越界检查,可以设置一个哨兵,即设置 a [ 0 ] = k e y a[0]=key a[0]=key,优化后的代码如下。

#include <iostream>
using namespace std;

// 全局变量 查找表
#define LEN	(10)
int searchTable[LEN + 1] = { 0, 1, 16, 24, 35, 47, 59, 62, 73, 88, 99 };	// 查找表元素从下标1开始存储

/**
* @brief:		带哨兵优化的顺序查找
* @param a:		顺序表指针
* @param n:		顺序表长度
* @param key:	要查找的关键字
* @return:		若查找成功,则返回key所在下标,否则返回0
*/
int sequentialSearchOpt(int* a, int n, int key)
{
	a[0] = key;	// 哨兵
	int i = n;	// 循环从尾部开始	
	while (a[i] != key)
	{
		i--;	
	}
	// 由于设置了a[0]=key,所以即使在a[1]~a[n]处不等于key,那么while循环也会在i=0时结束,此时查找失败
	return i;
}

int main(int argc, char* argv[])
{
	int key = 199;
	int pos = sequentialSearchOpt(searchTable, LEN, key);
	if (pos)
	{
		cout << "the index of " << key << " is " << pos << endl;
	}
	else
	{
		cout << "not found" << endl;
	}
}

2. 折半查找(二分或对分查找)

2.1 基本概念

2.2 代码

#include <iostream>
using namespace std;

// 全局变量 查找表
#define LEN	(10)
int searchTable[LEN + 1] = { 0, 1, 16, 24, 35, 47, 59, 62, 73, 88, 99 };	// 查找表元素从下标为1处开始

/**
* @brief:		二分查找迭代形式
* @param a:		查找表指针
* @param n:		查找表长度
* @param key:	要查找的关键字
* @return:		若查找成功,则返回key所在下标,否则返回0
*/
int binarySearch(int* a, int n, int key)
{
	int low = 1;	// 记录最低下标
	int high = n;	// 记录最高下标
	int mid;		// 记录中值下标

	while (low <= high)
	{
		mid = (low + high) / 2;		
		if (key > a[mid])			// 若查找值大于中值
		{
			low = mid + 1;
		}
		else if (key < a[mid])		// 若查找值小于中值
		{
			high = mid - 1;
		}
		else						// 若查找值等于中值
		{
			return mid;
		}
	}
	return 0;
}

int main(int argc, char* argv[])
{
	int key = 59;
	int pos = binarySearch(searchTable, LEN, key);
	if (pos)
	{
		cout << "the index of " << key << " is " << pos << endl;
	}
	else
	{
		cout << "not found" << endl;
	}
}
#include <iostream>
using namespace std;

// 全局变量 查找表
#define LEN	(10)
int searchTable[LEN + 1] = { 0, 1, 16, 24, 35, 47, 59, 62, 73, 88, 99 };	// 查找表元素从下标为1处开始

/**
* @brief:		二分查找递归形式
* @param a:		查找表指针
* @param n:		查找表长度
* @param key:	要查找的关键字
* @return:		若查找成功,则返回key所在下标,否则返回0
*/
int binarySearchRec(int* a, int low, int high, int key)
{
	if (low > high)
	{
		return 0;
	}
	
	int mid = (low + high) / 2;
	if (key > a[mid])		// 若查找值大于中值
	{
		return binarySearchRec(a, mid + 1, high, key);
	}
	else if (key < a[mid])	// 若查找值小于中值
	{
		return binarySearchRec(a, low, mid - 1, key);
	}
	else					// 若查找值等于中值
	{
		return mid;
	}
}

int main(int argc, char* argv[])
{
	int key = 59;
	int pos = binarySearchRec(searchTable, 1, LEN, key);
	if (pos)
	{
		cout << "the index of " << key << " is " << pos << endl;
	}
	else
	{
		cout << "not found" << endl;
	}
}

3. 插值查找

3.1 基本概念

3.2 代码

#include <iostream>
using namespace std;

// 全局变量 查找表
#define LEN	(10)
int searchTable[LEN + 1] = { 0, 1, 16, 24, 35, 47, 59, 62, 73, 88, 99 };	// 查找表元素从下标为1处开始

/**
* @brief:		插值查找
* @param a:		查找表指针
* @param n:		查找表长度
* @param key:	要查找的关键字
* @return:		若查找成功,则返回key所在下标,否则返回0
*/
int insertSearch(int* a, int n, int key)
{
	int low = 1;
	int high = n;
	int mid;

	while (low <= high)
	{
		mid = low + (key - a[low]) / (a[high] - a[low]) * (high - low);		// 插值
		if (key > a[mid])
		{
			low = mid + 1;
		}
		else if (key < a[mid])
		{
			high = mid - 1;
		}
		else
		{
			return mid;
		}
	}
	return 0;
}

int main(int argc, char* argv[])
{
	int key = 59;
	int pos = insertSearch(searchTable, LEN, key);
	if (pos)
	{
		cout << "the index of " << key << " is " << pos << endl;
	}
	else
	{
		cout << "not found" << endl;
	}
}

4. 斐波拉契查找

4.1 基本概念

斐波拉契查找的前提要求有序查找表记录数n为某个斐波拉契数减1,即 n = F ( k ) − 1 n=F(k)-1 n=F(k)1,这样mid可将查找表分成左右长度分别为 F ( k − 1 ) − 1 F(k-1)-1 F(k1)1 F ( k − 2 ) − 1 F(k-2)-1 F(k2)1的两个有序表。至于为什么要求 n = F ( k ) − 1 n=F(k)-1 n=F(k)1,可移步至下面的参考链接内容,对照图示理解。

4.2 代码

#include <iostream>
#include <vector>
using namespace std;

// 全局变量,斐波拉契数列
vector<int> FibArray = { 1, 1 };	// 初始第一、二项
vector<int> searchTable = { 0, 1, 16, 24, 35, 47, 59, 62, 73, 88, 99 };	// 查找表元素从下标为1处开始

/**
* @brief:		斐波拉契查找
* @param a:		查找表 vector
* @param key:	要查找的关键字
* @return:		若查找成功,则返回key所在下标,否则返回0
*/
int fibonacciSearch(vector<int>& a, int key)
{
	int n = a.size() - 1;	// 查找表长度,有效元素从下标1开始
	int low = 1;			// 记录最低下标
	int high = n;			// 记录最高下标
	int mid;				// 记录中值下标

	int k = 0;
	while (n > FibArray[k] - 1)		// 计算n位于斐波拉契数列的位置
	{
		k++;
		if (k >= FibArray.size())
		{
			FibArray.push_back(FibArray[k - 1] + FibArray[k - 2]);
		}
	}

	for (size_t i = n; i < FibArray[k] - 1; i++)	// 查找表起初长度可能不足 FibArray[k] - 1,将其补足
	{
		a.push_back(a[n]);
	}

	while (low <= high)
	{
		mid = low + FibArray[k - 1] - 1;	// 分隔点mid位置
		if (key < a[mid])		// 若查找值大于中值
		{
			high = mid - 1;
			k -= 1;
		}
		else if (key > a[mid])	// 若查找值小于中值
		{
			low = mid + 1;
			k -= 2;
		}
		else
		{
			if (mid <= n)
			{
				return mid;
			}
			else				// 若mid>n说明是补全的数值,直接返回n
			{
				return n;
			}
		}
	}

	return 0;
}

int main(int argc, char* argv[])
{
	int key = 59;
	int pos = fibonacciSearch(searchTable, key);
	if (pos)
	{
		cout << "the index of " << key << " is " << pos << endl;
	}
	else
	{
		cout << "not found" << endl;
	}		
}

二、树表的查找

1. 基本概念

1.1 二叉排序树操作——查找

1.2 二叉排序树操作——插入

1.3 二叉排序树操作——生成

1.4 二叉排序树操作——删除

2. 平衡二叉树

三、散列表的查找

1. 基本概念

2. 散列函数的构造方法

2.1 直接定址法

H a s h ( k e y ) = a ⋅ k e y + b   ( a 、 b 为常数 ) Hash(key) = a·key + b \ (a、b为常数) Hash(key)=akey+b (ab为常数)

优点:以关键码 k e y key key的某个线性函数值为散列地址,不会产生冲突;
缺点:要占用连续地址空间,空间效率低。

2.2 除留余数法

H a s h ( k e y ) = k e y   m o d   p   ( p 是一个整数 ) Hash(key) = key \ mod \ p \ (p是一个整数) Hash(key)=key mod p (p是一个整数)
关键:如何选取合适的 p p p
技巧:设表长为 m m m,取 p ⩽ m p \leqslant m pm 且为质数

3. 冲突处理方法

3.1 开放地址法(开地址法)

3.2 链地址法(拉链法)

4. 查找性能分析

平均查找长度ASL取决于

  • 散列函数
  • 处理冲突的方法
  • 散列表的装填因子 α α α
    α = 表中填入的记录数 哈希表的长度 α = \frac{表中填入的记录数}{哈希表的长度} α=哈希表的长度表中填入的记录数
    α α α越大,表中记录数越多,说明表装得越满,发生冲突的可能性就越大,查找时比较次数就越多。

A S L ASL ASL与装填因子 α α α有关!既不是严格的 O ( 1 ) O(1) O(1),也不是 O ( n ) O(n) O(n)

A S L ≈ 1 + α 2   ( 拉链法 ) ASL ≈ 1 + \frac{α}{2}\ (拉链法) ASL1+2α (拉链法)
A S L ≈ 1 2 ( 1 + 1 1 − α )   ( 线性探测法 ) ASL ≈ \frac{1}{2}(1 + \frac{1}{1 - α})\ (线性探测法) ASL21(1+1α1) (线性探测法)
A S L ≈ − 1 α l n ( 1 − α ) ( 随机探测法 ) ASL ≈ -\frac{1}{α}ln(1 - α)(随机探测法) ASLα1ln(1α)(随机探测法)

5. 代码

#include <iostream>
#include <vector>
using namespace std;

#define HASHSIZE (5)	// 散列表表长,自定义

struct HashNode {
	int			key;		// 关键字
	HashNode*	next;		// 指向下一个结点

	HashNode() {
		key = -1;
		next = nullptr;
	};
	HashNode(int key) : key(key), next(nullptr) {};
};

/* 哈希表结构 */
struct HashTable {
	HashNode** elem;		// 哈希表数组,每个元素都是一个指向HashNode结构的指针(每个链表的首地址)		
};


/**
* @brief:		散列表初始化
* @param H:		散列表指针
* @return:		
*/
void InitHashTable(HashTable *H)
{
	H->elem = new HashNode*[HASHSIZE];	// 分配哈希表数组空间
	for (size_t i = 0; i < HASHSIZE; i++)
	{
		H->elem[i] = nullptr;
	}
}

/**
* @brief:		散列表销毁,回收空间
* @param H:		散列表指针
* @return:
*/
void DestroyHashTable(HashTable* H)
{
	// 对每个链表的每个结点元素进行回收
	for (size_t i = 0; i < HASHSIZE; i++)
	{
		HashNode* root;
		HashNode* tmp;
		root = H->elem[i];
		while (root)
		{
			tmp = root->next;
			delete root;
			root = tmp;
		}
	}

	delete[] H->elem;
}


/**
* @brief:		散列函数:除留余数法
* @param key:	关键字
* @return:		散列值
*/
int Hash(int key)
{
	return key % HASHSIZE;
}


/**
* @brief:		将关键字插入散列表
* @param H:		散列表指针
* @param key:	关键字
* @return:		
*/
void InsertHash(HashTable *H, int key)
{	
	int addr = Hash(key);			// 散列值,应该放到散列表的哪个链表上

	HashNode* node = new HashNode(key);
	// 头插法
	node->next = H->elem[addr];
	H->elem[addr] = node;
}


/**
* @brief:		散列表查找
* @param H:		散列表指针
* @param key:	关键字
* @return:		true/false
*/
bool SearchHash(HashTable* H, int key)
{
	int addr = Hash(key);			// 散列值,应该在散列表的哪个链表上查找
	HashNode* root = H->elem[addr];	// 链表头结点
	while (root)
	{
		if (key == root->key)		// 找到记录
		{
			return true;
		}

		root = root->next;
	}

	return false;
}


int main()
{
	HashTable* H = new HashTable;
	// 初始化哈希表
	InitHashTable(H);

	// 往哈希表中插值
	vector<int> searchTable = { 1, 16, 24, 35, 47, 59, 62, 73, 88, 99 };	// 查找表元素
	for (size_t i = 0; i < searchTable.size(); i++)
	{
		InsertHash(H, searchTable[i]);
	}

	// 哈希表查找
	int key = 98;
	if (SearchHash(H, key))
	{
		cout << "found" << endl;
	}
	else
	{
		cout << "not found" << endl;
	}

	// 销毁散列表
	DestroyHashTable(H);
	delete H;

	return 0;
}

参考链接

青岛大学数据结构-王卓
七大查找算法
查找算法—(图解)斐波那契查找
斐波拉契查找法的原理及实现

举报

相关推荐

0 条评论