有序数组是一种特殊的数组,里面的元素,按一定的顺序排列,我们这里假设由小到大排列。
有序数组的优缺点:
最主要的好处是查找的速度比无序数组快多了。不好的方面是在插入操作中由于所有靠后的数据都需要移动以腾开空间,所以速度较慢。有序数组和
无序数组的删除操作都很慢,这是因为数据项必须向前移动来填补已删除数据项的洞。有序数组在查找频繁的情况下十分有用,但若是插入和删除较为
频繁是,则无法高效工作。例如,有序数组适合于公司雇员的数据库,雇佣和解雇雇员同读取一个已存在雇员的有关信息或更改薪水,住址等信息相比
,前两者是不经常发生的。另一方面,零售商店的存货清单不适合用有序数组来实现,这是由于与频繁的进货和出货相应的插入与删除操作都会执行的
很慢。
对于这种特殊的数组,我们可以采用前面提到的二分法来查找数组中特定的元素,这种算法的思想是:每查找一次,便将查找的范围缩小一半,所以叫做二分法查找。
有序数组的优点就是增加了查询的效率,但是它并没有提高删除和插入元素的效率,因此,对于有序数组更适合用于查询的领域。
二分查找(算法复杂度log2(n))
find()方法在开始设置lowerBound和upperBound指向数组的第一个和最后一个非空数据项。通过设置这些变量可以确定查找searchKey数据项的范围。然后在while循环中,当前的下标被设置为这个范围的中间值。 如果足够幸运的话,curIn可能直接就指向所需的数据项,所以应先查看是否相等。如果是,则意味着到了该数据项, 下一部变返回的的下标curIn。循环中的每一步将范围缩小一半。最终这个范围会小到无法再分隔。在下一条语句中会判断:如果lowerBound比upperBound大,则范围已经不存在了。(当lowerBound等于upperBound,范围是一个数据项所以需要再循环一次。)当范围不再有效是查找停止,但没有找到所需的数据项,所以返回数据项总数nElems。由于数组的最后一个非空数据 项的下标为nElems-1,所以下标nElems是无效的,类用户把这个值解释为没有找到特定数据项。如果curIn没有指向所需的数据项,但范围仍足够大,此时需要将范围缩小一半,比较当前下标所指的a[curIn],即范围中部值与待查数据的值searchKey。如果searchKey较大,则应该将范围设为当前范围的后半部。因此将lowerBound移到curIn。实际上是将lowerBound移到curIn后的一个单元。这是由于在循环的开始已经检查了curIn。如果searchKey 比a[curIn]小,则应将范围设为当前范围的前半部。因此将upperBound移至curIn的前一个数据项。下图显示了这两种情况下范围的变化:
find()方法在开始设置lowerBound和upperBound指向数组的第一个和最后一个非空数据项。通过设置这些变量可以确定查找searchKey数据项的范围。然后在while循环中,当前的下标被设置为这个范围的中间值。 如果足够幸运的话,curIn可能直接就指向所需的数据项,所以应先查看是否相等。如果是,则意味着到了该数据项, 下一部变返回的的下标curIn。循环中的每一步将范围缩小一半。最终这个范围会小到无法再分隔。在下一条语句中会判断:如果lowerBound比upperBound大,则范围已经不存在了。(当lowerBound等于upperBound,范围是一个数据项所以需要再循环一次。)当范围不再有效是查找停止,但没有找到所需的数据项,所以返回数据项总数nElems。由于数组的最后一个非空数据 项的下标为nElems-1,所以下标nElems是无效的,类用户把这个值解释为没有找到特定数据项。如果curIn没有指向所需的数据项,但范围仍足够大,此时需要将范围缩小一半,比较当前下标所指的a[curIn],即范围中部值与待查数据的值searchKey。如果searchKey较大,则应该将范围设为当前范围的后半部。因此将lowerBound移到curIn。实际上是将lowerBound移到curIn后的一个单元。这是由于在循环的开始已经检查了curIn。如果searchKey 比a[curIn]小,则应将范围设为当前范围的前半部。因此将upperBound移至curIn的前一个数据项。下图显示了这两种情况下范围的变化:
二分查找的Java代码如下:
public int find(long searchKey){
int lowerBound=0;
int upperBound=nElems-1;
int curIn;
while(true){
curIn=(lowerBound+upperBound)/2;
if(a[curIn]==searchKey)
return curIn;
else if(lowerBound>upperBound)
return nElems;
else{
if(a[curIn]<searchKey)
lowerBound=curIn+1;
else
upperBound=curIn-1;
}
}
}
//有序插入元素
public void insert(long value){
int j;
for(j=0;j<nElems;j++)
if(a[j]>value)
break;
for(int k=nElems;k>j;k--)
a[k]=a[k-1];
a[j]=value;
nElems++;
}
//删除元素
public boolean delete(long value){
int j=find(value);
if(j==nElems)
return false;
else{
for(int k=j;k<nElems;k++)
a[k]=a[k+1];
nElems--;
return true;
}
}