当前位置: 首页 > news >正文

(10)STL算法之搜索(二) 二分查找

之前在(3)STL算法之搜索里边介绍过几个查找函数,如find()、search(),这些函数的底层实现的都是顺序查找,采用的是遍历的方式,在某些时候效率并不高,例如当指定区域里的数据处于有序状态时,查找某个目标值,显示二分查找的效率更高。然后我是昨天写算法题的时候才看到别人用到了STL算法里的lower_bound()。才了解到了lower_bound()等其他几个底层为二分查找的查找函数。先来看下我遇到的那道题。

问题:给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。你可以假设数组中无重复元素。

题目来源:LeeCode 35 搜索插入位置

下面的答案是采用二分查找的解法。自从发现STL提供的二分查找算法,我就不喜欢这种写法了,哈哈哈哈。

class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        int n = nums.size();
        int left = 0, right = n - 1, ans = n;
        while (left <= right) {
            int mid = ((right - left) >> 1) + left;
            if (target <= nums[mid]) {
                ans = mid;
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }
        return ans;
    }
};

再来看看用轮子的写法...,看着简直太舒服了

int searchInsert(vector<int>& nums, int target) 
{
    return lower_bound(nums.begin(), nums.end(), target) - nums.begin();
}

接下来介绍这四个基于二分查找的算法。

二分查找:二分查找是一种效率较高的查找方法。但二分查找有一定的条件限制:要求线性表必须采用顺序存储结构,且表中元素必须按关键字有序(升序或降序均可)。在有序表中,取中间的记录作为比较对象,如果要查找记录的关键字等于中间记录的关键字,则查找成功;若要查找记录的关键字大于中间记录的关键字,则在中间记录的右半区继续查找。不断重述上述查找过程,直到查找成功,或有序表中没有所要查找的记录,查找失败。 

// 代码描述

template <class ElemType, class KeyType>
int BinSerach(ElemType elem[], int n, KeyType key)
{                   // 操作结果: 在有序表中查找其关键字的值等于key的记录,如查找成功,则返回此记录的序号,否则返回-1
    int low = 0, high = n - 1;		    // 区间初值
    while (low <= high)
    {
        int mid = (low + high) / 2;     // 区间中间位置
        if (key == elem[mid]) 
            return mid;                 // 查找成功
        else if (key < elem[mid]) 
            high = mid - 1;             // 继续在左半区间进行查找  
        else 
            low = mid + 1;              // 继续在右半区间进行查找
    }
    return -1;	                        // 查找失败
}

一、lower_bound() & upper_bound()

lower_bound()算法可以在参数一和参数二指定的范围内查找不小于参数三的第一个元素,即大于等于参数三的第一个元素。前两个参数必须是正向迭代器.

// 声明与定义
template <class ForwardIterator, class T>
ForwardIterator lower_bound(ForwardIterator first, ForwardIterator last,const T& val);

template <class ForwardIterator, class T, class Compare>
ForwardIterator lower_bound(ForwardIterator first, ForwardIterator last,const T& val, Compare comp);
comp是自定义规则,用于查找区域内第一个不符合comp规则的元素

template <class ForwardIterator, class T>
ForwardIterator lower_bound(ForwardIterator first, ForwardIterator last, const T& val)
{
    ForwardIterator it;
    iterator_traits<ForwardIterator>::difference_type count, step;
    count = distance(first, last);
    while (count > 0)
    {
        it = first; 
        step = count / 2; 
        advance(it, step);    // it迭代器移动到step位置
        if (*it < val)                  // if (comp(*it,val))     version 2
        {
            first = ++it;    // 首地址变为中间地址的下一个地址
            count -= step + 1;   // 求出从剩下的元素个数
        }
        else 
            count = step;
    }
    return first;
}

 upper_bound()算法用于在参数一和参数二指定的范围内查找大于参数三的第一个元素,

template <class ForwardIterator, class T>
ForwardIterator upper_bound (ForwardIterator first, ForwardIterator last, const T& val);
template <class ForwardIterator, class T, class Compare>
ForwardIterator upper_bound (ForwardIterator first, ForwardIterator last, const T& val, Compare comp);

template <class ForwardIterator, class T>
ForwardIterator upper_bound (ForwardIterator first, ForwardIterator last, const T& val)
{
    ForwardIterator it;
    iterator_traits<ForwardIterator>::difference_type count, step;
    count = std::distance(first,last);
    while (count>0)
    {
        it = first; step=count/2; std::advance (it,step);
        if (!(val<*it))                 // or: if (!comp(val,*it))      version (2)
        { 
            first=++it; count-=step+1;  
        }
        else 
            count=step;
    }
    return first;
}

 

用法示例:

#include <iostream>  
#include <algorithm>    // std::lower_bound, std::upper_bound, std::sort
#include <vector>      

int main() {
    int arr[] = { 10,20,30,30,20,10,10,20 };
    std::vector<int> v(arr, arr + 8);           // 10 20 30 30 20 10 10 20
    std::sort(v.begin(), v.end());              // 10 10 10 20 20 20 30 30

    std::vector<int>::iterator low, upp;
    low = std::lower_bound(v.begin(), v.end(), 20); // 查找返回 >=20 的第一个数的位置
    upp = std::upper_bound(v.begin(), v.end(), 20);  // 查找返回 >20 的第一个数的位置                 ^

    std::cout << "lower_bound at position " << (low - v.begin()) << '\n';
    std::cout << "upper_bound at position " << (upp - v.begin()) << '\n';
    return 0;
}
#include <iostream>     
#include <algorithm>    // std::lower_bound
#include <vector>     
using namespace std;

bool mycomp(int it, int val)  //普通函数 定义查找规则
{ 
    return it > val; 
}


class mycomp2        //以函数对象的形式定义查找规则
{
public:
    bool operator()(const int& it, const int& val) 
    {
        return it > val;
    }
};

int main() {
    int a[5] = { 1,2,3,4,5 };
    //从 a 数组中找到第一个不小于 3 的元素
    int* p = lower_bound(a, a + 5, 3);
    cout << "*p = " << *p << endl;

    vector<int> myvector{ 4,5,3,1,2 };
    //根据 mycomp2 规则,从 myvector 容器中找到第一个违背 mycomp2 规则的元素
    vector<int>::iterator iter = lower_bound(myvector.begin(), myvector.end(), 3, mycomp2());// 这里的3作为自定义规则的参数二
    cout << "*iter = " << *iter;
    return 0;
}

 此例程来源: http://c.biancheng.net/view/7521.html

// 晚上再写,现在要去干饭了。

二、binary_search()

在指定范围内查找是否存在某元素。

函数实现

template <class ForwardIterator, class T>
bool binary_search(ForwardIterator first, ForwardIterator last, const T& val);

template <class ForwardIterator, class T, class Compare>
bool binary_search(ForwardIterator first, ForwardIterator last, const T& val, Compare comp);

template <class ForwardIterator, class T>
bool binary_search(ForwardIterator first, ForwardIterator last, const T& val)
{
    first = std::lower_bound(first, last, val);
    return (first != last && !(val < *first)); 
//调用的是lower_bound,第一行返回第1个与查找值相等的迭代器并赋值给first,
//若不存在则first为第1个大于查找值的迭代器。   first还未指向last,且 val < *first 为假,则说明存在val
}

template <class ForwardIterator, class T, class Compare>
bool binary_search(ForwardIterator first, ForwardIterator last, const T& val, Compare comp)
{
    first = std::lower_bound(first, last, val, comp);
    return (!(first == last) && !(comp(val, *first)));
}

用法示例:

#include <iostream>    
#include <algorithm>    
#include <vector>    

bool myfunction(int val, int it) 
{ 
    return (val < it); 
}

int main() {
    int arr[] = { 1,2,3,4,5,4,3,2,1 };
    std::vector<int> v(arr, arr + 9);                         // 1 2 3 4 5 4 3 2 1
    std::sort(v.begin(), v.end());

    std::cout << "search elem 3  ";
    if (std::binary_search(v.begin(), v.end(), 3))
        std::cout << "found!\n"; 
    else 
        std::cout << "not found.\n";

    std::sort(v.begin(), v.end(), myfunction);
    std::cout << "search elem 6  ";
    if (std::binary_search(v.begin(), v.end(), 6, myfunction))
        std::cout << "found!\n"; 
    else 
        std::cout << "not found.\n";
    return 0;
}

三、equal_range()

用于在指定范围【first,last)内查找等于目标值的所有元素

template <class ForwardIterator, class T> pair<ForwardIterator, ForwardIterator>
equal_range(ForwardIterator first, ForwardIterator last, const T& val);

template <class ForwardIterator, class T, class Compare> pair<ForwardIterator, ForwardIterator>
equal_range(ForwardIterator first, ForwardIterator last, const T& val, Compare comp);

template <class ForwardIterator, class T> pair<ForwardIterator,ForwardIterator>
equal_range (ForwardIterator first, ForwardIterator last, const T& val)
{
    ForwardIterator it = std::lower_bound (first,last,val);
    return std::make_pair ( it, std::upper_bound(it,last,val) );
// 该函数会返回一个pair类型,包括两个正向迭代器,第一个迭代器指向【first,last)中第一个等于val的值
//第二个迭代器执行第一个【first,last)中第一个大于val的值,如果有自定义规则,就按自定义规则执行,如下边的例程。
}

// version (2)
template <class ForwardIterator, class T> pair<ForwardIterator, ForwardIterator>
equal_range(ForwardIterator first, ForwardIterator last, const T& val)
{
	ForwardIterator it = std::lower_bound(first, last, val, comp);
	return std::make_pair(it, std::upper_bound(it, last, val, comp));
}

用法示例:

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

bool comp(int i, int j)
{
	return (i > j);
}

int main()
{
	vector<int> v{ 10, 20, 30, 30, 20, 10, 10, 20 };
	pair<vector<int>::iterator, vector<int>::iterator> bounds;

	sort(v.begin(), v.end());
	for (int val : v)
		cout << val << " ";
	cout << endl;
	bounds = equal_range(v.begin(), v.end(), 20);
	cout << (bounds.first - v.begin()) << " and " << (bounds.second - v.begin()) << endl;

	sort(v.begin(), v.end(), comp);
	for (int val : v)
		cout << val << " ";
	cout << endl;
	bounds = equal_range(v.begin(), v.end(), 20, comp);
	cout << (bounds.first - v.begin()) << " and " << (bounds.second - v.begin()) << endl;
}

/*
10 10 10 20 20 20 30 30
3 and 6
30 30 20 20 20 10 10 10
2 and 5
*/

 

相关文章:

  • 单例模式(懒汉模式、饿汉模式)
  • 工厂模式抽象工厂
  • 链表翻转
  • LRU缓存算法
  • 判断单链表中是否存在环
  • LeeCode61 旋转链表
  • LeeCode74 搜索二维矩阵
  • (0)Nginx 功能特性
  • (2)nginx 安装、启停
  • (3)nginx 配置(nginx.conf)
  • 汇编语言学习笔记--基础知识篇
  • IAR for 430软件的简单使用
  • 430单片机时钟系统与复位系统的配置(1)
  • 430单片机时钟系统与复位系统的配置(2)
  • STM32F103芯片的一些小知识
  • 【140天】尚学堂高淇Java300集视频精华笔记(86-87)
  • 230. Kth Smallest Element in a BST
  • Angular2开发踩坑系列-生产环境编译
  • CAP 一致性协议及应用解析
  • CentOS7 安装JDK
  • echarts花样作死的坑
  • gcc介绍及安装
  • Java Agent 学习笔记
  • Java反射-动态类加载和重新加载
  • Java深入 - 深入理解Java集合
  • java中具有继承关系的类及其对象初始化顺序
  • Linux编程学习笔记 | Linux多线程学习[2] - 线程的同步
  • 给初学者:JavaScript 中数组操作注意点
  • 官方新出的 Kotlin 扩展库 KTX,到底帮你干了什么?
  • 将回调地狱按在地上摩擦的Promise
  • 微信公众号开发小记——5.python微信红包
  • 我有几个粽子,和一个故事
  • 线上 python http server profile 实践
  • 学习ES6 变量的解构赋值
  • 延迟脚本的方式
  • elasticsearch-head插件安装
  • 带你开发类似Pokemon Go的AR游戏
  • ​什么是bug?bug的源头在哪里?
  • ###C语言程序设计-----C语言学习(6)#
  • #Linux杂记--将Python3的源码编译为.so文件方法与Linux环境下的交叉编译方法
  • $.type 怎么精确判断对象类型的 --(源码学习2)
  • $GOPATH/go.mod exists but should not goland
  • (70min)字节暑假实习二面(已挂)
  • (附源码)springboot课程在线考试系统 毕业设计 655127
  • (附源码)ssm码农论坛 毕业设计 231126
  • (力扣题库)跳跃游戏II(c++)
  • (免费领源码)Java#Springboot#mysql农产品销售管理系统47627-计算机毕业设计项目选题推荐
  • (南京观海微电子)——COF介绍
  • (详细版)Vary: Scaling up the Vision Vocabulary for Large Vision-Language Models
  • (转)h264中avc和flv数据的解析
  • ... 是什么 ?... 有什么用处?
  • .\OBJ\test1.axf: Error: L6230W: Ignoring --entry command. Cannot find argumen 'Reset_Handler'
  • .NET Core/Framework 创建委托以大幅度提高反射调用的性能
  • .net refrector
  • .NET企业级应用架构设计系列之开场白