二分查找(分治法)。
二分查找也是一种分治法的实现,每一次查找将数据分为两个部分,问题规模都减小一半。这样查找的时间复杂度为logN。因为其实查找过程建立了一棵有N个节点的二叉树,查找次数是这棵树的高度 logN+1,所以复杂度是O(logN)。
Java实现的二分查找
查找基类
/**
* 查找基类
* @author junjun
*
*/
public class Search
{
/**
* 在数组s[p...q]上查找r
* @param s 待查数组
* @param p
* @param q
* @param r 待查的数
* @return 找到返回坐标,否则返回-1
*/
public int search(int [] s,int p,int q,int r) {return -1;};
}
二分查找类
/**
* 二分查找类
* @author junjun
*
*/
public class BinarySearch extends Search
{
/**
* 二分查找 在s[p...q]上查找r
* @param s 待查找的数组 按照升序排列
* @param p
* @param q
* @return 如果找到 返回找到元素的下标,否则返回-1
*/
public int search(int[] s, int p, int q,int r)
{
while(p <=q)
{
int middle = (p+q)/2; //取中间的位置进行二分
if(r == s[middle]) return middle; //如果找到返回middle
else if(r<s[middle]) q = middle-1;
else p = middle+1;
}
return -1;
}
/**
* @param args
*/
public static void main(String[] args)
{
BinarySearch bs = new BinarySearch();
int [] s = {0 ,1 ,1 ,2 ,3 ,3 ,4 ,5 ,8 ,9};
for(int i = 0; i < s.length;i++)
{
System.out.print("s["+i+"] = "+s[i]+" ");
}
System.out.println("\n3 is at the position:"+bs.search(s, 0, s.length-1, 3));
System.out.println("9 is at the position:"+bs.search(s, 0, s.length-1, 9));
System.out.println("11 is at the position:"+bs.search(s, 0, s.length-1, 11));
}
}
使用二分查找改进插入排序
插入排序算法需要与已排序的数组中从后向前一次比较比较的平均次数是O(n)的,我们可以由于是往已排序的子数组插入,所以可以使用二分法确定插入位置,比较次数减少到O(logN).下面就是二分法改进后的插入排序。虽然元素的比较次数减少到了O(logN),但是还是需要移动O(N2)次元素。
/**
* 使用二分搜索改进的插入排序
* @author buptjunjun
* @time 2013-1-22
*
*/
public class InsertSortBS extends Sort
{
/**
* 插入排序
* @param s 带排数列
* @param incOrDec 控制升序还是降序
* @return
*/
@Override
public int[] sort(int[] s,int incOrDec)
{
if(s == null || s.length <=1) return s;
for(int i = 1;i <s.length; i++)
{
int key = s[i];
//下面进行二分查找 寻找插入点
int insertPos = -1;// p就是要插入的位置
int p = 0;
int q = i-1;
int middle = p;
while(p<q)
{
middle = (p+q)/2;
if(s[middle] == key)
break;
else if(key < s[middle]) q = middle-1;
else p=middle+1;
}
//找到插入点 根据上面循环终止条件 决定插入点
//如果 是由p==q跳出来的 插入点就是p
//如果是由if(s[middle] == key) 跳出来的 插入点就是middle
insertPos = p==q?q:middle;
//将插入点后的元素依次往后移动
for(int j = i; j>insertPos ; j--)
{
s[j] = s[j-1];
}
//将元素插入插入点
s[insertPos] = key;
}
return s;
}
/**
* @param args
*/
public static void main(String[] args)
{
int [] s = {2,5,3,1,1,3,4,4,9,0};
int [] ret = new InsertSortBS().sort(s,Sort.INCREMENT);
new InsertSortBS().print(s);
}
}
2. 3.7 一个跪了的面试题
记得去年面试的时候一家公司,有这么一个题:”给定一个整数x和一个整数集合S,问S中是否能够找到两个数a,b使得a+b = x.” 现在在算法导论的第二章第23页找到了出处。开卷有益啊!
其实这就是一个二分查找的应用。
基本思路是:
遍历S中每一个的数,假设当前元素为为Si ,要找另一个数b使得Si+b = x,我们变换一下形式得到 b=x-Si .
其实问题就转化为在S中是否存在一个元素为b,先对S进行排序(可以使用合并排序时间是O(NlogN)),然后使用二分查找法,在logN的时间内就可以得到答案。
整个算法需要遍历整个集合S,总的时间复杂度为O(NlogN).