【209】VS2022 C++对排好序的vector使用二分查找算法的例子

本文介绍了如何对已经排序的 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 查找的效率,可以使用二分法查找(也叫折半查找)。

算法实现的思路如下:

  1. 假设 vector 的长度是 N,循环次数是以 2 为底 N 的对数。程序先强制转换为 int,然后给整型变量 times 赋值循环次数。

  2. 循环开始前,设置 begin 是 0,代表第一个元素的下标。end 是最后一个元素的下标。

  3. 开启循环,循环次数就是 times。只要循环次数达到 times 就停止循环。

  4. 在循环中,计算中间元素的下标,取出中间元素。

  5. 如果输入 id 小于中间元素的下标,给 end 赋值中间元素坐标减一。反之,给 begin 赋值中间元素坐标。通过循环不断缩小 begin 和 end 之间的范围。因为 vector 的长度不可能总是等于 2 整数次方,再加上循环次数强制转换为整型,所以 begin 和 end之间要么相等,要么 end 比 begin 大一。

  6. 上一个循环结束后,开启从 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");
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值