《Java数据结构与算法》中在对二分查找法做分析时,是以序数组是前提
有序数组
总所周知,在一系列面试题中都会对有序数组的特点进行描述
查找快,增删慢
如下代码中,可以得到分析
- 插入数据时,会线性查询出“第一个”值比插入数据大的下标索引,然后对索引(包含)后的值进行向后挪移,空出索引前一个下标位置,最后赋值
- 删除数据时,同理会线性查询,不同的是,值是向前进行挪移
由此可以得出在有序数组中,进行增删,是相对 消耗性能的
class OrderArray{
private long[] a;
//指针(索引)体现数组a中实际元素个数
private int nElems;
//构造函数:当对象初始化后,指针(索引)也随之初始化
public OrderArray(int max){
a = new long[max];
nElems = 0;
}
/**
* (线性法)
* 提供数据插入方法:随着每一次数据的插入,指针都相应递增
* @param value
*/
public void insert(long value){
int j;
//找出比传入值大的第一个值的下标(有序)
for (j=0;j<nElems;j++){
if (a[j]>value) {
break;
}
}
if (j == nElems){
//将该下标后的值都向后一个下标位,这里采用逆循环防止覆盖
for (int k=nElems;k>j;k--){
a[k]=a[k-1];
}
//前面都执行完后,下标j的位置就空出来了,将值填入即可
a[j]=value;
nElems ++ ;
}else {
throw new RuntimeException("元素以存在");
}
}
/**
* 提供数据删除方法:随着每一次数据成功删除,指针都相应递减
* @param value
*/
public boolean delete(long value){
int j;
//该循环会筛选出存在元素的下标
for(j=0;j<nElems;j++){
if (value == a[j]){
break;
}
}
//上方循环若没有筛选出对应下标则会返回false
if (j == nElems){
return false;
}else {
//筛选出结果则进入此代码块进行数据下标调整、覆盖
for (int k=j; k<nElems;k++){
//这里的判断书中没有提到,如果不加上会会报数组下标越界
if (k+1==nElems){
break;
}
a[k]=a[k+1];
}
nElems --;
return true;
}
}
}
二分查找
这里的案例函数中的‘ nElems ’,‘ a[] ’ 成员变量沿用上面代码块中
- 可以看出二分查找需要有局部变量:头、尾区间以及局部指针去进行定位
- 传入指定值随后返回对应下标
- 这里没有做细化处理,使用的是while(true),如果在之前的插入、删除的方法中想调用二分查找来缩短线性查找的时间是会出现死循环情况的
/**
* 提供数据查询方法(二分法)
* return 下标值
*/
public int find(long searchKey){
//二分指针
int curIn;
//头区间
int lowerBound = 0;
//尾区间
int upperBound = nElems-1;
while (true){
//设置二分指针值
curIn = (lowerBound - upperBound)/2;
//判断是否相等
if (a[curIn] == searchKey){
return curIn;
//进入此代码块返回指针(则表示数组内没有查到)
}else if (lowerBound>upperBound){
return nElems;
//二分匹配
}else {
//若二分指针值小于指定内容则头区间+1
if (a[curIn]<searchKey){
lowerBound=curIn+1;
}else {
upperBound=curIn-1;
}
}
}
}
书中也用方程来体现二分法的步数,以及元素个数的范围,详细公式这里不做赘述