看数据结构写代码(53) 静态查找表(线性查找,二分查找,斐波那契查找,插值查找)

查找定义:根据给定的某个值,在查找表中确定一个其关键字等于给定值的数据元素(或记录)。

查找表分类:静态查找表和动态查找表。

静态查找表:只查找,而不进行插入,删除。

动态查找表:在查找过程中同时插入查找表中不存在的数据元素,或者从查找表中删除已经存在的某个数据元素。

静态表的 查找 大致 四种 算法: 线性查找,二分查找,斐波那契查找和插值查找。 其中 在线性查找之前,对表 无要求。对于 其余三种 需要 在查找之前 排序。插值查找 除了 需要 排序,还需要 均匀分布。

下面 给出代码:

线性查找:平均查找长度ASL = (n+1)/2

//无序表,顺序查找
int line_Search(int * array,int len,int key){
	for (int i = 0; i < len; i++){
		if (array[i] == key){
			return i;
		}
	}
	return -1;
}

线性 查找 ,可以 在 第一个位置 ,或者 最后一个位置 放一个 “哨兵”,其值等于 key。这样 可以防止 查找出界,并且效率提高很多。

int Sequential_Search2(int *a, int n, int key)
{
     int i;
     a[0] = key;
     for(i = n; a[i] != a[0]; i--);
     return i;
}

二分查找:又叫折半查找,算法简单,效率高,,ASL = (log以2为底的n+1 ) -1

//二分查找,查找之前需要排序
int search_Bin(int * array,int len,int key){
	int low = 0;
	int high = len -1;
	while (low <= high){
		int mid = (low + high) /2 ;
		if (array[mid] == key){
			return mid;
		}
		else if( array[mid] > key){
			high = mid - 1;
		}
		else{
			low = mid + 1;
		}
	}
	return -1;
}

斐波那契查找:效率高,空间复杂度高,易出错(数组越界)

//斐波那契查找
#define MAX_SIZE 30
void fibonacci(int * f){
	f[0] = 0;
	f[1] = 1;
	for (int i = 2; i < MAX_SIZE; i++){
		f[i] = f[i-1] + f[i-2];
	}
}

int fibonacci_Search(int * array,int len,int key){
	int low = 0;
	int high = len -1;
	int f[MAX_SIZE];
	fibonacci(f);//创建斐波那契数列
	int k;
	for (k = 0; k < MAX_SIZE; k++){//寻找最小匹配的斐波那契数
		if (f[k] >= len){
			break;
		}
	}
	for (int i = len; i < f[k]; i++){//补齐不足的位置
		array[i] = array[high];
	}
	while (low <= high){
		int mid = low + f[k-1] -1;//斐波那契查找效率高,因为在计算机里 加法 比 除法简单.
		if (key > array[mid]){
			low = mid + 1;
			k = k-2;
		}
		else if(key < array[mid]){
			high = mid - 1;
			k = k - 1;
		}
		else{
			if (mid <= high){//这一步 不懂
				return mid;
			}
			else{
				return -1;
			}
		}
	}
	return -1;
}

斐波那契查找 不是 每次 查找 表长的 一半,而是 用 某个 斐波那契数 分割 表。斐波那契比 二分查找 效率高 是因为 二分 查找  在计算 mid时用的是除法:int mid = (low + high) /2 ;而 斐波那契 用 的是 加法:int mid
= low + f[k-1] -1; 但是 斐波那契算法 需要 表长 必须为 某个斐波那契数 -1,所以 需要 表要 留有一些空间。

插值 查找:当我们 在 字典里 查找 “apple”时,我们 并不会从 打开字典的一半进行查找,而是从 前面开始查找。当我们 查找“zero”时,我们从 尾部开始查找。 这就是 插值查找 和 二分查找的区别。他会 根据 key值 在 表里 的 比例 来 安排 查找的 位置;

插值 查找,与二分查找,代码 仅 一句 不同:

int mid = low + (high-low)*((key-array[low])/(array[high]-array[low]));//根二分查找仅此区别

//插值查找
//顺序表+ 均匀分布
int insertValue_Search(int * array,int len,int key){
	int low = 0;
	int high = len -1;
	while (low <= high){
		//根据比例查找
		int mid = low + (high-low)*((key-array[low])/(array[high]-array[low]));//根二分查找仅此区别
		int midValue = array[mid];
		if (midValue > key){
			high = mid -1;
		}
		else if(midValue < key){
			low = mid +1;
		}
		else{
			return mid;
		}
	}
	return -1;
}

总结:

折半查找进行加法与除法运算(mid = (low + high) / 2),插值查找进行复杂的四则运算( mid = low + (key - a[low] / (a[high] - a[low]) * (high - low)) ),斐波那契查找只是运用简单家减法运算 (mid  = low + f[k-1] -1) ,在海量的数据查找过程中,这种席位的差别会影响最终的查找效率。三种有序表的查找本质上是分割点的选择不同,各有优劣,实际开发可根据数据的特点综合考虑再做决定。

完整源代码网盘地址:点击打开链接

时间: 2024-08-19 18:34:43

看数据结构写代码(53) 静态查找表(线性查找,二分查找,斐波那契查找,插值查找)的相关文章

二分查找算法(加法方式:斐波那契查找)

二分查找算法比较熟悉的是折半查找,但是折半查找中计算mid时,有加法和除法.下面介绍仅有加法方式的二分查找算法 核心思想利用斐波那契表达式来实现加法方式的折半查找 技巧点:1)将数组中数的个数(f(n)-1)分成 f(n-1) -1和 f(n-2)2):f(n) - 1 = (f(n-1)-1 )+ (f(n-2)-1) 在数组中因为要用到一个mid数(中间位置的数) 故总共要减掉1,故上述表达式成立. 1 #-*-coding: UTF-8 -*- 2 3 4 # 5 # 时间复杂度O(log

看数据结构写代码(54)次优查找树

查找顺序表时,若 每个元素的概率 都相等 用 二分查找 效率 最高.但是 如果 概率 不相等时,(SOST)静态最优查找表 效率 要高于 二分查找.静态最优查找表 是 使得 从 根 到 每个节点的路径 长度 和 权值 乘积 之和 最小. 书上说的 静态最优 查找树的创建 时间 复杂度 较高,所以 用 次优 查找树(NOST) 代替. 下面 上代码: // Nost.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <cstd

看数据结构写代码(57) AVL树的删除

上一节 已经说了 AVL树的插入 操作,可是 只有 插入,没有删除,怎么能叫 动态 查找表呢. 呵呵,博主 赶紧 去 研究了一番.下面 是成果: AVL树的删除 大致 分为 两大块: 1. 查找节点 并 删除 2. 保持 删除 后 平衡因子的 影响 1. 首先 找到 这个 节点,如果 节点 不存在,直接 退出 函数 if (*tree == NULL){//没找到 return false; } 2.如果 存在,分为 四种情况:(根 二叉 排序树的 删除 类似) 1.节点 为 叶子 节点,直接

看数据结构写代码(32) 赫夫曼树编码以及译码

杂谈:最近有点慵懒,不好不好.好几天都没写代码,原本准备上星期完结 树 这一章节的.现在 又耽误了.哎.要抓紧时间啊. 下面直接上代码: 可以到我的网盘下载源代码,或者 直接拷贝下面的源代码 运行 网盘地址:点击打开链接 // HuffmanTree.cpp : 定义控制台应用程序的入口点. //哈弗曼编码,译码 #include "stdafx.h" #include <stdlib.h> #include <cstring> enum E_State { E

看数据结构写代码(5)静态链表

静态链表用于 不能使用 指针的 编程语言中. 下面奉上代码: // StaticLinkList.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <stdlib.h> //静态链表的 实现 typedef int Element; #define INIT_SIZE 10 //为了测试,故意将值设置的很小 enum E_STATE { E_STATE_ERROR = 0, E_STATE_OK = 1, }; str

看数据结构写代码(36) 图的邻接表表示与实现

图的邻接表表示法,是为每一个顶点建立一个链表,链表里存放着相同弧尾的 弧的信息,这些链表顺序存放在数组中.下面是无向图g2的邻接表 邻接表 比 邻接矩阵 节省空间,同时 也带来一些操作上的 不便,例如 看 两个顶点是否 相邻,需要 遍历 链表,在 求 无向图顶点的度时,只需 遍历 顶点的链表,而 求 有向图 顶点的度 需要 遍历 整个图 查找 弧头 为这个顶点的 个数. 如果 不想这样做,可以 建立 逆邻接表,即 链表里 存放着 相同 弧头的 弧 的信息. 下一节 要说的 十字链表 类似于这种结

看数据结构写代码(61) 哈希表

前面说的 各种查找都是 基于 "比较" 的基础 来进行 查找的.查找的 效率 要 看 比较的 次数.那么 有没有 不需要 比较,就可以 找到 想要的数据的 方法呢? 哈希表 就是 这样的 一种方法,它用  数组 作为 保存 关键字的 数据原型,通过 一个 哈希 函数f(k),来找到 关键字 存储的位置,从而 找到想要的信息. 例如 我们 想要解决 这样的一个问题: 假设这有一个各种字母组成的字符串,假设这还有另外一个字符串,而且这个字符串里的字母数相对少一些.什么方法能最快的查出所有小

看数据结构写代码(38) 图的邻接多重表表示法与实现

图的邻接多重表 是 无向图的 另一种表示法.其与 邻接表 的差别 仅仅 在于 ,邻接表 用 两个 顶点 来表示 一条边,而 邻接多重表 用一个 顶点来表示一条边.这样使得 邻接多重表 在 某些操作 要 来的 方便.例如 将 搜索过的边 做记号 或者 删除 一条边. 下面是邻接多重表的结构: 下面的 6条边 用 6个弧 节点表示,用12个指针指向,每个弧节点被 指向2次.这样使得我们 在 释放内存的时候 需要格外小心. 下面上代码: 源码工程文件网盘地址:点击打开链接 // AMLGraph.cp

看数据结构写代码(52) 广义表的扩展线性链表存储表示

广义表 的另一种 存储结构是 扩展线性链表存储表示,这种 存储结构的 根 节点 必 存在,并且 根节点的 表尾 为空,将 根节点的 表尾 放 在 表头 的 表尾 指针上. 这样 从 表头 一直 就可以 遍历 所有 同级 节点. 具体j结构 如下: 例如 下面的 广义表 ,用 扩展线性链表 表示为: 而 头尾 存储表示,是 把 表头 和 表尾 都放在 根节点 的 指针上.其存储结构如下: 所以 其 实现 代码略有 不同,要 小心 处理 下面 上代码: // GList2.cpp : 定义控制台应用