二分查找(基于有序数组)
二分查找的具体步骤:
1.求取数组长中间数;
2.比较查找键(key)和中间键(mid->key),
1)如果key大,则选择数组后半部分,
2)如果key小,则选择数组前半部分,
3)相等,则输出值,结束查找;
3.返回选择的数组继续前两步;
4.如果没有找到键,返回键应该存在的位置。
很明显可以看出二分查找需要数组有序做为前提,所以在符号表中必然会有排序函数。
同时我们使用数组的原因是,数组能够减少每次查找所需的比较次数,且我们可以通过数组标识来快速锁定中间数和查找范围。
二分查找代码:
1.符号表(基于有序数组)
class ST
{
private:
vector<string> keys;
vector<string> datas;
public:
bool Empty(){ return size() == 0 ? 1 : 0; }
int size(){ return keys.size(); }
};
2.查找
string get(string key)//查找
{
if (Empty())return "No key";
int i = rank(key);
if (i < size() && keys[i] == key)
return datas[i];
return "No key";
}
1)二分查找递归形式
int rank(string key){ return rank(key, 0, size() - 1); }
int rank(string key, int lo, int hi)
{//二分查找
if (hi < lo)return lo;//没有键值,返回他应该存在的位置
int mid = lo + (hi - lo) / 2;
if (key < keys[mid])return rank(key, lo, mid - 1);
else if (key > keys[mid])return rank(key, mid + 1, hi);
else return mid;
}
2)二分查找迭代形式
int rank(string key)
{
int lo = 0, hi = size() - 1;
while (lo <= hi)
{
int mid = lo + (hi - lo) / 2;
if (key < keys[mid])hi = mid - 1;
else if (key > keys[mid])lo = mid + 1;
else return mid;
}
return lo;
}
3.插入
因为二分查找是基于有序数组的,所以我们在插入的时候必须是有序插入。
插入操作分两种情况:
1.是原本存在键值,则替换值
2.是不存在键值,则利用rank函数返回的插入位序,插入元素
1)首先为数组添加新空间
2)返回位序以后的元素全部后移
3)插入元素
void put(string key, string item)//插入
{
int i = rank(key), N = size();
if (i<N && key == keys[i])//如果键存在,直接修改值
{
datas[i] = item;
return;
}
keys.push_back(key);//添加新元素
datas.push_back(item);
for (int j = N; j > i; j--)//新元素排序
{//移位方式
keys[j] = keys[j - 1];
datas[j] = datas[j - 1];
}
keys[i] = key;
datas[i] = item;
}
4.删除
删除操作与插入相对应,首先判断删除键值是否存在,存在则移位删除,不存在则返回"No key"。
string delete_1(string key)//删除
{
int i = rank(key), N = size();
if (i < N && keys[i] == key)
{
string item = datas[i];
for (int j = i; j < N - 1; j++)
{
keys[j] = keys[j + 1];
datas[j] = datas[j + 1];
}
keys.pop_back();
datas.pop_back();
return item;
}
return "No key";
}
5.基于二分查找的其余操作
string min(){ return keys[0]; }//最小键
string max(){ return keys[size() - 1]; }//最大键
string select(int k){ return keys[k]; }//第k个键(从0开始)
string ceiling(string key)//大于等于key的最小键
{
int i = rank(key);
return keys[i];
}
string floor(string key)//小于等于key的最大键
{
int i = rank(key);
return keys[i - 1];
}
queue<string> Keys(string lo, string hi)//键的集合
{
queue<string> q;
for (int i = rank(lo); i < rank(hi); i++)
q.push(keys[i]);
if (hi == keys[rank(hi)])
q.push(keys[rank(hi)]);
return q;
}
一般情况下二分查找都比顺序查找快得多,他也是众多实际应用程序的最佳选择。