本文介绍了如何对已经排序的 vector 进行二分法查找。
首先,我们先看一下存储数据的类,我们假设所有数据的 id 是唯一的:
DataItem.h
#pragma once
#include<string>
namespace zc {
class DataItem
{
public:
int m_id;
std::string m_name;
std::string m_data;
DataItem(int id, std::string name, std::string data);
~DataItem();
};
}
DataItem.cpp
#include "DataItem.h"
using namespace zc;
DataItem::DataItem(int id, std::string name, std::string data)
:m_id(id), m_name(name), m_data(data)
{
}
DataItem::~DataItem(){}
存储在 vector 中的元素,都是 DataItem
类实例化的对象。这些对象在 vector 中按照 id 从低到高排列。如果我们要提高按照 id 查找的效率,可以使用二分法查找(也叫折半查找)。
算法实现的思路如下:
-
假设 vector 的长度是 N,循环次数是以 2 为底 N 的对数。程序先强制转换为 int,然后给整型变量 times 赋值循环次数。
-
循环开始前,设置
begin
是 0,代表第一个元素的下标。end
是最后一个元素的下标。 -
开启循环,循环次数就是 times。只要循环次数达到 times 就停止循环。
-
在循环中,计算中间元素的下标,取出中间元素。
-
如果输入 id 小于中间元素的下标,给 end 赋值中间元素坐标减一。反之,给 begin 赋值中间元素坐标。通过循环不断缩小 begin 和 end 之间的范围。因为 vector 的长度不可能总是等于 2 整数次方,再加上循环次数强制转换为整型,所以 begin 和 end之间要么相等,要么 end 比 begin 大一。
-
上一个循环结束后,开启从 begin 到 end 的循环,如果能找到 id 相同的元素并返回下标,如果没找到就返回 -1.
下面是具体实现的 C++ 代码:
#include <stdlib.h>
#include <stdio.h>
#include <vector>
#include <cmath>
#include "DataItem.h"
#include <chrono>
// 向 vector 中添加元素
void addItem(std::vector<zc::DataItem> &vector) {
for (int i = 1; i < 100000; i++) {
std::string no = std::to_string(i);
vector.push_back(zc::DataItem(i, "name_" + no, "data_" + no));
}
}
// 使用二分法查找算法,从 vector 中查找指定 id 的对象的序号.
// vector: 已经排好序的列表
// id: 要查找的指定ID
int findIndexByBinary(const std::vector<zc::DataItem> vector, const int id) {
int result = -1;
int size = vector.size();
if (0 == size) {
return -1;
}
// 只有一条数据就直接判断然后返回。
if (1 == size) {
if (vector[0].m_id == id) {
result = 0;
}
return result;
}
int begin = 0;
int end = size - 1;
// 计算以2为底的对数,进而确定要循环多少次找到目标数据
double log2Result = log2((double)size);
int times = (int)log2Result;
// 不断缩小begin和end的范围。有可能 begin 等于 end,也有可能 end 比 begin 大一。
for (int i = 0; i < times; i++) {
int tmpLen = end - begin + 1;
int halfIndex = begin + (tmpLen / 2);
zc::DataItem item = vector[halfIndex];
if (id < item.m_id) {
end = halfIndex - 1;
}
else {
begin = halfIndex;
}
}
//printf("begin=%d, end=%d\n", begin, end);
for (int i = begin; i <= end; i++) {
zc::DataItem tmp = vector[i];
if (id == tmp.m_id) {
result = i;
break;
}
}
return result;
}
// 遍历vector,从 vector 中查找指定 id 的对象的序号.
int findIndex(const std::vector<zc::DataItem> vector, const int id) {
int result = -1;
int size = vector.size();
if (0 == size) {
return -1;
}
for (int i = 0; i < size; i++) {
zc::DataItem tmp = vector[i];
if (id == tmp.m_id) {
result = i;
break;
}
}
return result;
}
long long nowMS()
{
long long result =std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch())
.count();
return result;
}
int main(int argc, char** argv) {
system("color 02");
printf("hello argc=%d argv[0]=%s\n\n", argc, argv[0]);
long long t1 = 0, t2 = 0;
int index = -1;
std::vector<zc::DataItem> vector;
addItem(vector);
t1 = nowMS();
const int id = 95001;
index = findIndexByBinary(vector, id);
t2 = nowMS();
if (index < 0) {
return -1;
}
zc::DataItem item = vector[index];
printf("index=%d, id=%d, name=%s, data=%s, time=%lld\n", index, item.m_id, item.m_name.c_str(), item.m_data.c_str(), (t2 - t1));
t1 = nowMS();
index = findIndex(vector, id);
t2 = nowMS();
zc::DataItem item2 = vector[index];
printf("index=%d, id=%d, name=%s, data=%s, time=%lld\n", index, item2.m_id, item2.m_name.c_str(), item2.m_data.c_str(), (t2 - t1));
printf("\n\n");
}