现在我们的新问题是,为什么一定要折半,而不是折四分之一或者折更多呢?
打个比方,在英文词典里查"apple",你下意识里翻开词典是翻前面的书页还是后面的书页呢?如果再让你查"zoo",你又怎么查?很显然,这里你绝对不会是从中间开始查起,而是有一定目的的往前或往后翻。
同样的,比如要在取值范围0 - 10000之间100个元素从小到大均匀分布的数组中查找5,我们自然会考虑从数组下标较小的开始查找。
看来,我们的折半查找,还是有改进空间的。折半计算mid的公式,我们略微等式变换后得到:
mid = (low+high)/2 = low + (high-low)/2
有改进方案用 for 替代 foreach ,这样仍会报下标越界错误。mode,更改手柄模式,一般是切换类比摇杆是否作为类比摇杆使用,关闭类比摇杆以后,左类比摇杆的4个方向等于方向键的四个方向,右类比的四个方向等于 1,2,3,4按键的功能。2、类比线段的大小、和与差、中点,学习角的比较、角的和与差、角平分线,体会类比思想。
mid = low + ((key - a[low])/(a[high] - a[low]))(high - low)
这样就可以大大提高查找的效率。
插值查找(Interpolation Search)是根据要查找的关键字 key 与查找表中最大最小记录的关键字比较后的查找方法,其核心就在于插值的计算公式(key - a[low])/(a[high] - a[low])。应该说,从时间复杂度来看,它也是O(logn),但对于表长较大,而关键字分布又比较均匀的查找表来说,插值查找算法的平均性能比折半查找要好得多 。反之, 数组中如果分布类似{0,1,2,2000,2001,.......,999998, 999999}这种极端不均匀的数据,用插值查找未必是很合适的选择。
/**
* 插值查找
*
* @param a
* 数组
* @param key
* 待查找关键字
* @return 返回折半下标, -1表示不存在该关键字
*/
public static int interpolationSearch(int[] a, int key) {
int low, mid, high;
low = 0;// 最小下标
high = a.length - 1;// 最大小标
while (low < high) {
mid = low + (high - low) * (key - a[low]) / (a[high] - a[low]);
// mid = (high + low) / 2;// 折半下标
if (key > a[mid]) {
low = mid + 1; // 关键字比 折半值 大,则最小下标 调成 折半下标的下一位
} else if (key < a[mid]) {
high = mid - 1;// 关键字比 折半值 小,则最大下标 调成 折半下标的前一位
} else {
return mid; // 当 key == a[mid] 返回 折半下标
}
}
return -1;
}
斐波那契查找(Fibonacci Search)时,它是利用了黄金分割原理来实现的。
下面我们根据代码来看程序是如何运行的。
/** 斐波那契数列 */
static int[] f = { 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55 };
/**
* 斐波那契查找(黄金分割原理)
*
* @param a
* 待查询数组
* @param key
* 待查找关键字
* @return 返回关键字在a数组中的下标,返回-1表示数组中不存在此关键字
*/
public static int fibonaciSearch(int[] a, int key) {
int low, mid, high, k;
low = 0;
high = a.length - 1;
// 斐波那契数列下标
k = 0;
// 获取斐波那契分割值下标
while (high > f[k] - 1)
k++;
// 利用Java工具类Arrays构造长度为f[k]的新数组并指向引用a
a = Arrays.copyOf(a, f[k]);
// 对新数组后面多余的元素赋值最大的元素
for (int i = high + 1; i < f[k]; i++) {
a[i] = a[high];//当key是是最大值时候,防止角标越界异常
}
while (low <= high) {
// 前半部分有f[k-1]个元素,由于下标从0开始
// 减去 1 获取 分割位置元素的下标
mid = low + f[k - 1] - 1;
if (key < a[mid]) {// 关键字小于分割位置元素,则继续查找前半部分,高位指针移动
high = mid - 1;
// (全部元素) = (前半部分)+(后半部分)
// f[k] = f[k-1] + f[k-2]
// 因为前半部分有f[k-1]个元素, 则继续拆分f[k-1] = f[k-2] + f[k-3]成立
// 即在f[k-1]个元素的前半部分f[k-2]中继续查找,所以k = k - 1,
// 则下次循环mid = low + f[k - 1 - 1] - 1;
k = k - 1;
} else if (key > a[mid]) {// 关键字大于分割位置元素,则查找后半部分,低位指针移动
low = mid + 1;
// (全部元素) = (前半部分)+(后半部分)
// f[k] = f[k-1] + f[k-2]
// 因为后半部分有f[k-2]个元素, 则继续拆分f[k-2] = f[k-3] + f[k-4]成立
// 即在f[k-2]个元素的前半部分f[k-3]继续查找,所以k = k - 2,
// 则下次循环mid = low + f[k - 2 - 1] - 1;
k = k - 2;
} else {
// 当条件成立的时候,则找到元素
if (mid <= high)
return mid;
else
// 出现这种情况是查找到补充的元素
// 而补充的元素与high位置的元素一样
return high;
}
}
return -1;
}
斐波那契查找算法的核心在于 :
1 ) 当 key=a[mid] 时,查找就成功。
2 ) 当 key
3 ) 当 key>a[mid]时,新范围是第mid+l个到第high个,此时范围个数为f[k-2]-1个。
也就是说,如果要查找的记录在右侧,则左侧的数据都不用再判断了,不断反复进行下去,对处于当中的大部分数据,其工作效率要高一些。所以尽管斐波那契查找的时间复杂也为O(logn),但就平均性能来说,斐波那契查找要优于折半查找。可惜如果是最坏情况,比如这里key=l,那么始终都处于左侧长半区在查找,则查找效率要低于折半查找。
6.3.6数值和字符串运算语句数值间有加减乘除等运算,字符串有相连定位查找等运算,时间有加减等运算。知识点10:有理数加减混合运算:根据有理数减法的法则,一切加法和减法的运算,都可以统一成加法运算,然后省略括号和加号,并运用加法法则、加法运算律进行计算。后者做了 4 × 3 个乘法,2 个加法 +4 × 3 个乘法,2 个加法 +4 个除法,共 24 个乘法,16 个加法,4 个除法,运算简化了不少,如果是大图,效率的提高将是非常客观的。
应该说,三种有序表的查找本质上是分隔点的选择不同,各有优劣,实际开发时可根据数据的特点综合考虑再做出选择。
我们前面讲的几种比较高效的查找方法都是基于有序的基础之上的,但事实上,很多数据集可能增长非常快,如果要保证记录全部是按照当中的某个关键字有序,其时间代价是非常高昂的,所以这种数据通常都是按先后顺序存储。
那么对于这样的查找表,我们如何能够快速查找到需要的数据呢?办法就是--索引。
10.如权利要求9所述存储介质,每一地理位置的数据包括一个为识别与相关位置关联的数据结构的位置代码,其特征在于,所述位置代码包含在索引记录中,所述索引记录包含有对与所述相关位置关联的数据结构的引用。如果找不到收发记录表中对应的记录,系统应自动产生一条期初结存数据,存货、自由项、批号与货位期初结存一致,但数据和件数为零,并自动建立关联。说明称,为了实现对彩票销售数据快速的检索、查找及统计,系统对每张彩票销售数据都建立对应的索引记录,系统也需为该记录分配对应的索引记录空间。
map是键值对映射容器,与list和set有明显的区别,而set存储的零散的元素且不允许有重复元素(数学中的集合也是如此),list是线性结构的容器,适用于按数值索引访问元素的情形。一个对象可以被反复存储进list中,每调用一次add方法,这个对象就被插入进集合中一次,其实,并不是把这个对象本身存储进了集合中,而是在集合中用一个索引变量指向这个对象,当这个对象被add多次时,即相当于集合中有多个索引指向了这个对象,如图x所示。接口索引集合(interfaces):用于描述这个类实现了哪些接口,这些被实现的接口将按implements语句后的接口顺序从左到右在接口索引集合中(u2类型数据集合)。
稠密索引是指性索引中,将数据集中的每个记录对应一个索引项,如图下图所示。
对于稠密索引这个索引表来说,索引项一定是按照关键码有序的排列。
当加入一定量的砝码后,发现天平的指针偏向分度盘的左侧,再加入最小的砝码,指针偏向分度盘的右侧,这时应该,直至天平平衡。给予③中的刺激点适宜刺激后,随着动作电位的传导,当左侧电极为动作电位时,右侧电极尚处于静息电位状态,因而指针会向左偏转,当动作电位传到右侧电极时,左侧电极处已恢复为静息电位,因而,指针会向右偏转一次。如此继续,我们就可以保证左指针i的左侧元素都不大于切分元素,右指针j的右侧元素都不小于切分元素。
这显然是稠密索引优点,但是如果数据集非常大,比如上亿,那也就意味着索引也得同样的数据集长度规模,对于内存有限的计算机来说,可能就需要反复去访问磁盘,查找性能反而大大下降了。
稠密索引因为索引项与数据集的记录个数相同,所以空间代价很大。为了减少索引项的个数,我们可以对数据集进行分块,使其分块有序,然后再对每一块建立一个索引项,从而减少索引项的个数。
分块有序,是把数据集的记录分成了若干块,并且这些块需要满足两个条件:
• 块内无序,即每一块内的记录不要求有序。当然 ,你如果能够让块内有序对查找来说更理想,不过这就要付出大量时间和空间的代价,因此通常我们不要求块内有序 。
④ 厂②坝段乙块试验区以外透水率及注入率递减情况部位 孔序 灌前平均透水率(lu) 平均单位注入量 kg/m 备注 厂②坝段乙块试验区以外 Ⅰ xxxxxx xxxxx Ⅱ xxxxx xxxxx 厂②坝段乙块试验区以外固结灌浆透水率及水泥注入量资料分析统计表明:各序孔之间平均单位注入量均随孔序增加呈明显递减,平均透水率qi﹤qii灌前压水平均透水率:Ⅰ序孔平均透水率q xxxlu,Ⅱ序孔平均透水率q xxxlu, qi﹤qii,据资料分析可能的原因有:灌浆孔gzc2-43-10、gzc2-45-10、gzc2-47-10、gzc2-49-10、gzc2-51-10均处在灌浆区域边缘,距临空面一米的距离,裂隙封闭性较差,没有形成闭合区域,导致Ⅱ序孔透水率较大。)1328(贪心)1458(最长公共子序列)1647(很好的真题,考临场分析准确和下手迅速)1654(学会多边形面积的三角形求法)1655(一类无根树的dp问题)1804(逆序对)2084(经典组合数学问题)2187(用凸包求最远点对,求出凸包后应该有o(n)的求法,可我就是调不出来)2195(二分图的最佳匹配)2242(计算几何经典)2295(等式处理)2353(dp,但要记录最佳路径)2354(立体解析几何)2362(搜索好题)2410(读懂题是关键)2411(经典dp)24、1067(很难的数学,但仔细研究,是一片广阔的领域)1147(有o(n)的算法,需要思考)1240(直到一棵树的先序和后序遍历,那么有几种中序遍历呢。1990年左右,张舍庄东白家和西白家有白氏35户200,有清道光六年编修的族谱一部,序言“吾族白氏世籍山左莱郡平度州,历年既久,族姓自繁,其中亲疏远近莫能指其分支劈流所从出,每念及之无所归咎,因就历世相承之序,详加追访,不取臆废,不得不各宗其宗各谱其谱",序末注明“东白家西白家俱係一族,因先世失传故名叙其谱,白氏五世圖说明一世祖名九坤字兴全,二世东杰字圣艺,三世魁公字擢先,四世率宗字昌后号坦心,太学生封承德郎赠中议大夫”。
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-106859-2.html