前菜:查找
根据某个给定的关键字K,从集合R中找出与K相同的记录。
- 静态查找:集合中记录是固定的,没有删除和插入操作。
- 动态查找:有插入、删除操作。
顺序查找
e.g. 静态查找例子:表如下,表头指针Tbl,
Tbl -> Length = 10
表示表的长度。查找时候从表尾,从后往前查找,返回元素K的下标。表头(下标为0)不存放数据。
常规写法是这样:
/* 在Tbl[n]到Tbl[1]中查找元素K的位置 */
int SequentialSearch (StaticTable *Tbl, ElementType K){
int i; // now index
for(i = Tbl -> Length; i > 0 && Tbl -> Element[i] != K; i--);
return i;
}
注意到每次for循环中总要判断i > 0
,这是为了防止查找越界。那么换一种思路,如果查找到最后一个元素,其实也是到达查找的边界。把下标为0的元素的值设为K,最终总会得到Tbl -> Element[i] = K
,i的值若为0,则说明遍历了整个列表都没查找到K;若不为0,则返回位置i。这样可以减少循环中的一次比较次数。
int SequentialSearch (StaticTable *Tbl, ElementType K){
int i; // now index
Tbl -> Element[0] = K; // Sentinel element
for(i = Tbl -> Length; i > 0 && Tbl -> Element[i] != K; i--);
return i; // returns: the index of Element K ; 0 if K is no found
}
这种查找方法,平均查找次数N/2
,时间复杂度O(N)
。
二分查找
前提:数据有序存放。
代码如下:
int BinarySearch( StaticTable *Tbl, ElementType K) {
int left, right, mid, NotFound = -1;
// 初始值
left = 1;
right = Tbl -> length;
while (left <= right) {
mid = ( left + right) / 2; // 计算中间元素坐标
if (Tbl -> Element[mid] < K){
left = mid + 1;
}else if (Tbl -> Element[mid] > k){
right = mid - 1;
}else{
return mid
}
}
return NotFound;
}
算法复杂度O(logN)
。
[Note]: 如果left
和right
的更新由left = mid+1
、right = mid-1
改为left = mid
、right = mid
,结果会出错,这跟while
里面的条件有关的。本例中条件为left > right
时(查找失败)退出查找,假如在三个数
6 9 10
中查找8,为了方便,直接用left
、mid
、right
代表6
、9
、10
。
初始:
left = 6
right = 10
进入比较
mid = 9
9 > 8 -->left不变 left = 6
10 > 8 -->right变为mid的值 right = 9
mid = 6
6 < 8 --> left变为mid的值 left = 6
6 < 10 --> right不变 right = 9
此时left
和right
相邻,并不破坏while循环规则left <= right
,因此进入死循