一、 查找(顺序查找、二分(折半)查找、分块查找)
(1) 顺序查找
基本思想:
从表的一端开始,顺序扫描线性表,依次将扫描到的结点关键宇和给定值K相比较。若当前扫描到的结点关键字与K相等,则查找成功;若扫描结束后,仍未找到关键字等于K的结点,则查找失败。
public int SeqSearch(int[] r, int k)
{
// 在顺序表R[0..n]中顺序查找关键字为k的结点,
// 成功时返回找到的结点位置,失败时返回0
for(int i = r.length – 1; i >= 0; i–) //从表后往前找
{
if(r[i] == k)
{
return i; //若i为-1,表示查找失败,否则R[i]是要找的结点
}
}
return -1;
}
优点:
算法简单,且对表的结构无任何要求,无论是用向量还是用链表来存放结点,也无论结点之间是否按关键字有序,它都同样适用。
缺点:
查找效率低,因此,当n较大时不宜采用顺序查找。
(2) 二分查找(折半查找)
条件:数组递增
基本思想:
1. 查找区间R[low..high],首先确定该区间的中点位置:mid = (low + high) / 2;
2. 然后将待查的K值与R[mid]比较:若相等,则查找成功并返回此位置,否则须确定新的查找区间,继续二分查找。
① 若R[mid] > K,则由数组的有序性可知R[mid..n]均大于K,因此该结点必定是在位置mid左边的R[0..mid-1]中
② 若R[mid] < K,则要查找的K必在mid的右边的R[mid+1..n]中,下一次查找是针对新的查找区间进行的。
3. 因此,从初始的查找区间R[1..n]开始,每经过一次与当前查找区间的中点位置上的结点关键字的比较,就可确定查找是否成功,不成功则当前的查找区间就缩小一半。这一过程重复直至找到关键字为K的结点,或者直至当前的查找区间为空(即查找失败)时为止。
案例分析:
/**
* 折半查找
* 要求查找的数组有序,递增
*/
public int partSearch(int[] data, int tmpData)
{
int mid;
int low = 0;
int high = data.length – 1;
while(low <= high)
{
mid = (low + high) / 2; // 中间位置
if(tmpData == data[mid])
{
return mid; // 查找到,要查找的元素的位置
}
else if(tmpData < data[mid])
{
high = mid – 1;
}
else
{
low = mid + 1;
}
}
return -1; // 没有查找到
}
(3) 分块查找
分块查找是将顺序查找与折半查找相结合的一种查找方法。
基本思想:
1. 首先将查找表分成若干块,在每一块中数据元素的存放是任意的,但块与块之间必须是有序的(假设这种排序是按关键字值递增的,也就是说在第一块中任意一个数据元素的关键字都小于第二块中所有数据元素的关键字,第二块中任意一个数据元素的关键字都小于第三块中所有数据元素的关键字,依次类推);
2. 建立一个索引表,把每块中最大的关键字值按块的顺序存放在一个辅助数组中,这个索引表也按升序排列;
3. 查找时先用给定的关键字值在索引表中查找,确定满足条件的数据元素存放在哪个块中,查找方法既可以是折半方法,也可以是顺序查找。
4. 再到相应的块中顺序查找,便可以得到查找的结果。
案例分析:
一个查找表共有15个数据元素,现将它分成三块,每块5个数据元素,各块采用顺序方式独立存放在数组B1、B2、B3之中,辅助数组A的每个元素包含两个域,关键字域中存放该块中关键字的上确界(本块中最大的关键字值),指针域存放该块的数组始址,存放状态如图6-5所示。
如果要查找关键字为66的数据元素,首先用66与A中各个元素的key比较,由于66<74,确定66在第三块中(如果存在),然后按A[2].link 到数组B3中采用顺序查找找到B3[2],查找成功。
/**
* 分块查找
*
* @param index 索引表,其中放的是各块的最大值
* @param st 顺序表,
* @param key 要查找的值
* @param m 顺序表中各块的长度相等,为m
* @return
*/
private int BlockSearch(int[ ] index, int[ ] st, int key, int m)
{
// 在序列st数组中,用分块查找方法查找关键字为key的记录
// 1.在index[ ] 中折半查找,确定要查找的key属于哪个块中
int i = partSearch(index, key);
if(i >= 0)
{
int j = i > 0 ? i * m : i;
int len = (i + 1) * m;
// 在确定的块中用顺序查找方法查找key
for(int k = j; k < len; k++)
{
if(key == st[k])
{
System.out.println(“查询成功”);
return k;
}
}
}
System.out.println(“查找失败”);
return -1;
}
public int partSearchs(int[] data, int tmpData)
{
int mid;
int low = 0;
int high = data.length – 1;
while(low <= high)
{
mid = (low + high) / 2; // 中间位置
if(tmpData == data[mid])
{
return mid;
}
else if(tmpData < data[mid])
{
high = mid – 1;
}
else
{
low = mid + 1;
return low;
}
}
return -1; // 没有查找到
}
优点:
①在表中插入或删除一个记录时,只要找到该记录所属的块,就在该块内进行插入和删除运算。
②因块内记录的存放是任意的,所以插入或删除比较容易,无须移动大量记录。
分块查找的主要代价是增加一个辅助数组的存储空间和将初始表分块排序的运算。
分块查找算法的效率介于顺序查找和二分查找之间。
若表中有10000个结点,则应把它分成100个块,每块中含100个结点。用顺序查找确定块,分块查找平均需要做100次比较,而顺序查找平均需做5000次比较,二分查找最多需14次比较。