数据结构之对半查找

这是我写的第二篇博客,本篇介绍如何利用Java编写对半查找的程序(其中可能有许多错漏之处,但大部分内容是可以接受的。见谅)

顾名思义,对半查找就是从一列数据中间--也就是对半所划分的地方查找。对半查找适用于使用顺序存储的有序线性表。这里所说的“顺序存储”即是数据被写入内存中的一块连续的存储空间,而连续的这些内存空间是有其相应的编号即其内存地址来标志的,我们可以通过地址来访问这些数据。

而所谓的“有序”指的是数据的有序,例如:1,2,3,4,7,99,567,...,'a','b','h',...,这些是大小顺序。至于“线性表”系指一种一个接连着一个的“one by one”的、前面接着一个前驱并且后面跟着一个后继的数据结构。

对半查找的主要思想是在查找数据的时候总是查找当前数据范围内的中间数据,以确定数据在“那一半”,有“左半边”和“右半边”,如:有序的数据2,3,5,7,11,13,17(一堆素数),我现在要找11,编号从1开始数到7,中间的数据是(1+7)/2=4,直接去找地4个数据--它就是7,11>7,看来11在“右半边”,“右半边”为:11,13,17,从第5个(4+1=5)到第11个,当前数据范围中间的是第6个((5+7)/2=6)数据--它是13,11<13,11在其“左半边”,...这样不断地进行这样的操作,直至“一半”缩短至一个数据--是11。

以上过程示意如下:

2 3 5 7 11 13 17 

11 13 17

11

我们再举一例,数据为c,d,e,k,p,x,编号从1到6,我们现在要查找数据f,先找中间的数据即第3个数据e,e<f,则f在“右半边”,“右半边”为“k,p,x”,编号从4到6,中间的数据为第5个,即p,f<p,则f在它的“左半边”,“左半边”为“k”,从4到4,只有一个数据,但k!=f,于是原数据列表中没有数据f,查找完毕,查找失败。
以上过程示意如下:

c d e k p x

k p x

k


现在我们清楚,对半查找的输入为一个待查找的数据(如上面的f)和一存储数据元素的列表(如上面的“cdekpz”),可以为数组,而数据的类型有int,char,String等,前两者为Integer,Character的包装类(为实现将数据类型变为对象来使用),我们可以用Object类(所有类的父类)来代替,或者使用泛型。为了能更号地说明问题,我们使用char。输出为该待查找的数据在该列表中的编号即索引。
    为了明确每次我们要查找哪里的元素,我们设置了两个int指针front和l,front和l分别指向当前数据列表的首尾,中间是(front+l)/2,下面是代码。

package testpackage;

public class Demo {
	public static int halfSear(char[] orderArra,char item) {
		int l=orderArra.length-1;
		int front=0;
		while(front<=l) {
			int s=orderArra[(l+front)/2]-item;
			if(s==0){
				return (l+front)/2;
				}else if(s>0) {/*若item小于中间值*/
					l=(l+front)/2-1;//l往前移至“左半边”尾端
					}else if(s<0){/*否则*/
						front=(l+front)/2+1;//front往后移至“右半边”首
						}
		}
		return -1;
	}
	public static void main(String[] args) {
		char ch[]= {'a','b','f','g','l','x'};
		char t='l';
		int r=halfSear(ch,t);
		System.out.println(r);
	}
}


输出:

4

在这里所输出的是数组的索引值,数组从0开始数起。
    从对半查找的过程来看,我们能根据索引快速访问数据,数据排列的有序,能使我们快速的判断待查数据在列表中的那一部分,从而省去了查找更多的元素。下面我们分析一下该算法的时间复杂度。粗略地估计,查找的次数不会超过\log (2)m+1(底数为2),为什么?请读者思考一下再往下看。
    我们记数据元素的长度为m,查找次数为n,则经过n+1次(n次对半,n+1次比较)对半查找后,有,

m/2^{n}=1 \leqslant \frac{m}{2^{n}}

于是,2^{n}\leqslant m\Rightarrow n\leqslant \log {_2{}}^{}m
   
   特别说明,我们有

对于一个实数a,

[a]/2=[[a]/2]=[a/2^{2}]

证:[a]\leqslant a\Rightarrow \frac{[a]}{2}\leqslant \frac{a}{2}\Rightarrow [\frac{[a]}{2}]\leqslant [\frac{a}{2}]

[\frac{[a]}{2}]< [\frac{a}{2}] ,则

\frac{a}{2}-\frac{[a]}{2}\geqslant 1\Rightarrow a-2\geqslant [a]\Rightarrow a-[a]\geqslant 2

矛盾,因此

[a]/2=[[a]/2]=[a/2^{2}]

时间复杂度:O(\log _{2}^{}m)

另附:

package workModel;

/**
 * 本类封装了一些XXX的方法
 * @author CHY
 *
 */
public class search<T> {
	
		public int compareTo(T m,T n) {/*可重写*/
			String a=m.toString();
			String b=n.toString();
			return a.compareTo(b);
		}
		
	/**
	 * 本方法实现对有序数组的分半查找
	 * @param orderArra T[] 一泛型T的有序数组
	 * @param item T 一泛型T的数据元素
	 * @return int 索引
	 */
	public int halfSear(T[] orderArra,T item) {
		int l=orderArra.length-1;
		int front=0;
		while(front<=l) {
			int s=compareTo(orderArra[(l+front)/2],item);
			if(s==0){
				return (l+front)/2;
				}else if(s>0) {
					l=(l+front)/2-1;
					}else if(s<0){
						front=(l+front)/2+1;
						}
		}
		return -1;
	}
	public static void main(String[] args) {
		Character[] str= {'a','b','c','d','e','f'};
		Character ch='j';
		int i=new search<Character>().halfSear(str, ch);
		System.out.println(i);
	}
}


---------------------------------完--------------

  • 20
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值