算法笔记(1)—— 搜索算法:线性搜索(LS)、二分搜索(BS)、记忆化搜索(MS)

简介: 算法笔记(1)—— 搜索算法:线性搜索(LS)、二分搜索(BS)、记忆化搜索(MS)

线性搜索(LinearSearch)

   线性搜索,从字面意思上理解就是按顺序地线性搜索东西,简单点说就是一个一个地去找。在 C/C++ 中判断某个数组里面是否有某元素时,就会用到它。在Python里判断一个元素是否在一个列表中时,可以用 in(成员运算符)运算符(猜测它的原理就是线性搜索)。


【适用情形】搜索序列不是很大的情况


【序列顺序】顺序、乱序均可


【时间复杂度】O(n)


【空间复杂度】O(1)

【动画演示】

线性搜索(LinearSearch)[动画由Python tkinter模块制作]

【Python实现】

def LinearSearch(lis: list[int], obj: int): #lis:搜索序列 obj:目标值
    for index, value in enumerate(lis):     #遍历搜索序列
        if value == obj:                    #判断是否为目标值
            return index                    #找到目标值,返回索引
    return None                             #未找到值,返回None

【C/C++实现】

int LinearSearch(int arr[], int length, int obj)    //arr:搜索数组 length:数组长度(或搜索长度) obj:目标值
{
  for(int i = 0; i < length; i++)           //遍历数组
    {
    if(arr[i] == obj)               //判断是否为目标值
      return i;                   //返回索引
  }
  return -1;                        //没有目标值,返回-1
}

练手好题】

【洛谷】P3383 【模板】线性筛素数

二分搜索(BinarySearch)

       二分搜索,也叫二分查找,可以解决线性搜索

       二分搜索,也叫二分查找,可以解决线性搜索在搜索时间上的不足。顾名思义,每搜索一次,就二分一次。如果说线性搜索是常数速度,那么二分搜索就是指数速度!


【适用情形】搜索序列单调递增或单调递减(以下实现代码以单调递增为例)


【序列顺序】顺序(顺序增大或顺序减小)


【时间复杂度】O(logn)


【空间复杂度】O(1)

【动画演示】

二分搜索(BinarySearch)[动画由Python tkinter模块制作]

【Python实现】

【循环型二分搜索】

def BinarySearch(lis: list[int], obj: int): #lis:搜索序列 obj:目标值
    max_index, min_index = len(lis)-1, 0    #初始化搜索的上下边界(索引)
    while True:                             #无限循环
        middle = (max_index+min_index)//2   #把上下边界(索引)二分,得中间索引
        if max_index < min_index:           #设定循环终止条件(上边界小于下边界,这是不应该出现的情况,所以终止)
            return None                     #循环终止,没有目标值,返回None
        if lis[middle] > obj:               #判断中间值是否大于目标值
            max_index = middle-1            #中间值大于目标值,说明目标值的索引应该在中间索引之下,但不包括中间索引,所以上边界(索引)缩小至中间索引-1
        elif lis[middle] < obj:             #判断中间值是否小于目标值
            min_index = middle+1            #中间值小于目标值,说明目标值的索引应该在中间索引之上,但不包括中间索引,所以下边界(索引)增大至中间索引+1
        else:                               #中间值恰好等于目标值
            return middle                   #返回中间索引

【递归型二分搜索】

##【递归型】二分搜索 ##
def BinarySearch(lis: list[int], obj: int, max_index: int, min_index: int=0):   #lis:搜索序列 obj:目标值 max_index:上索引 min_index:下索引
    middle = (max_index+min_index)//2                                           #把上下边界(索引)二分,得中间索引
    if max_index < min_index:                                                   #设定递归终止条件(上边界小于下边界,这是不应该出现的情况,所以终止)
        return None                                                             #递归终止,没有目标值,返回None
    if lis[middle] > obj:                                                       #判断中间值是否大于目标值
        return BinarySearch(lis, obj, middle-1, min_index)                      #中间值大于目标值,说明目标值的索引应该在中间索引之下,但不包括中间索引,所以上边界(索引)缩小至中间索引-1,递归函数
    elif lis[middle] < obj:                                                     #判断中间值是否小于目标值
        return BinarySearch(lis, obj, max_index, middle+1)                      #中间值小于目标值,说明目标值的索引应该在中间索引之上,但不包括中间索引,所以下边界(索引)增大至中间索引+1,递归函数
    else:                                                                       #中间值恰好等于目标值
        return middle                                                           #返回中间索引

【C/C++实现】

【循环型二分搜索】

int BinarySearch(int arr[], int length, int obj)      //arr:搜索数组 length:数组长度(或搜索长度) obj:目标值
{
  int max_index = length-1, min_index = 0, middle;  //初始化搜索的上下边界(索引)及中间索引
  while(true)                       //无限循环
  {
    middle = (max_index+min_index)/2;         //把上下边界(索引)二分,得中间索引
    if(max_index < min_index)             //设定循环终止条件(上边界小于下边界,这是不应该出现的情况,所以终止)
      return -1;                    //循环终止,没有目标值,返回-1
    if(arr[middle] > obj)               //判断中间值是否大于目标值
      max_index = middle-1;             //中间值大于目标值,说明目标值的索引应该在中间索引之下,但不包括中间索引,所以上边界(索引)缩小至中间索引-1
    else if(arr[middle] < obj)              //判断中间值是否小于目标值
      min_index = middle+1;             //中间值小于目标值,说明目标值的索引应该在中间索引之上,但不包括中间索引,所以下边界(索引)增大至中间索引+1
    else                        //中间值恰好等于目标值
      return middle;                  //返回中间索引
  }
}

【递归型二分搜索】

int BinarySearch(int arr[], int obj, int max_index, int min_index=0)  //arr:搜索数组 obj:目标值 max_index:上边界(索引) min_index:下边界(索引)
{
  int middle = (max_index+min_index)/2;               //把上下边界(索引)二分,得中间索引
  if(max_index < min_index)                     //设定递归终止条件(上边界小于下边界,这是不应该出现的情况,所以终止)
    return -1;                            //递归终止,没有目标值,返回-1
  if(arr[middle] > obj)                       //判断中间值是否大于目标值
    return BinarySearch(arr, obj, middle-1, min_index);       //中间值大于目标值,说明目标值的索引应该在中间索引之下,但不包括中间索引,所以上边界(索引)缩小至中间索引-1
  else if(arr[middle] < obj)                      //判断中间值是否小于目标值
    return BinarySearch(arr, obj, max_index, middle+1);       //中间值小于目标值,说明目标值的索引应该在中间索引之上,但不包括中间索引,所以下边界(索引)增大至中间索引+1
  else                                //中间值恰好等于目标值
    return middle;                          //返回中间索引
}

【练手好题】

【洛谷】P2249 【深基13.例1】查找

【LeetCode】704. 二分查找

记忆化搜索(MemorySearch)

 记忆化搜索,就是在搜索的过程中同时记住一些值(有动态规划的思想),然后在后面要再次用到该值时可以直接进行调用(就避免重复计算了),有点像递推,但递推是直接对数组元素(或者列表元素)进行操作了,是在记录值的同时往后一步一步推导值,每一步之间都有联系,或者说,递推的规律性比较强,而记忆化搜索就比较普适了(个人认为)。记忆化搜索可以解决递归计算速度慢的问题。


【适用情形】搜索过程中会重复计算某值或者重复进行某一过程


【序列顺序】一般事先未知序列


【时间复杂度】不确定,取决于搜索函数(但同问题下,绝对比递归方法小)


【空间复杂度】不确定,取决于搜索函数

【动画演示】

记忆化搜索(MemorySearch)[动画由Python tkinter模块制作]

【Python实现】

【记忆化搜索一般操作】

MemLis = [None]*1001                                                      #记忆化列表,用于存储搜索过程中的数据
def MS(n):                                                                #定义MS函数,便于读取记忆和存储数据
    if not MemLis[n]:MemLis[n]=MemorySearch(n)                            #如果MemLis[n]的值为None,说明该位置还没有数据被记录,则计算应计算的值并赋值给MemLis[n]
    return MemLis[n]                                                      #返回MemLis[n]的值
def MemorySearch(n:int):                                                  #n:第n项
    if <Condition_1>:return <CertainNum>                                  #直接返回数字的情况
    elif <Condition_2>:return MS(<CertainNum_1>)+MS(<CertainNum_2>)+...   #递归调用的情况
    else:...                                                              #其它情况

【Python特有高级操作】

from functools import cache             #引入记忆化搜索装饰器(旧版为lru_cache)
@cache                                  #装饰函数
def object_function(*args, **kwargs):   #待装饰的目标函数
    ...
'''
装饰完之后的目标函数就是记忆化搜索的函数了,它会在运行的时候申请大量内存来“记忆”
不过该方法不能保证函数递归不超过最大递归深度(1000)
'''

【举个栗子】

       现在要我们求斐波那契数列的第500项,读者不妨先用递归试试!再用上面的模板试试!比较一下速度!

         记忆化搜索一般解法

MemLis = [None]*1001                            #记忆化列表
def MS(n):                                      #定义MS函数,便于读取记忆和存储数据
    if not MemLis[n]:MemLis[n]=MemorySearch(n)  ##如果MemLis[n]的值为None,说明该位置还没有数据被记录,则计算应计算的值并赋值给MemLis[n]
    return MemLis[n]                            #返回MemLis[n]的值
def MemorySearch(n:int):                        #n:第n项
    if n==1:return 0                            #斐波那契数列第1项
    elif n==2:return 1                          #斐波那契数列第2项
    else:return MS(n-1)+MS(n-2)                 #斐波那契数列其它项
print(MemorySearch(500))                        #输出结果

 记忆化装饰器解法

from functools import cache                      #引入装饰器
@cache                                           #装饰函数
def fibonacci(n:int):                            #斐波那契函数
    if n == 1:return 0                           #斐波那契数列第1项
    elif n == 2:return 1                         #斐波那契数列第2项
    else:return fibonacci(n-1)+fibonacci(n-2)    #斐波那契数列其它项
print(fibonacci(500))                            #输出结果

【C/C++实现】

【记忆化搜索一般操作】

int MemArr[1001];                                                            //用于记忆的数组在内存的要求之下尽可能开大
int MS(int n)                                //定义MS函数用于读取记忆和存储数据
{
    int MemorySearch(int n);                         //声明搜索函数
  if(<Condition>)MemArr[n] = MemorySearch(n);                //如果MemArr[n]的值不满足某种条件,说明MemArr[n]还未存储数据,则计算其应有的值并赋值给MemArr[n]
  return MemArr[n];                            //返回MemArr[n]的值
}
int MemorySearch(int n)                                                      //n:第n项
{
  if(<Condition_1>)return <CertainNum>;                                    //直接返回数字的情况
  else if(<Condition_2>)return MS(<CertainNum_1>)+MS(<CertainNum_2>)+...;  //递归调用的情况
  else ...;                                                                //其他类似的情况
}

【C/C++特有高级操作】

#define MS(n) (<Condition>?MemArr[n]:MemArr[n]=MemorySearch(n))              //记忆化的宏函数,使代码结构简单清晰
int MemArr[1001];                                                            //用于记忆的数组在内存的要求之下尽可能开大
int MemorySearch(int n)                                                      //n:第n项
{
  if(<Condition_1>)return <CertainNum>;                                    //直接返回数字的情况
  else if(<Condition_2>)return MS(<CertainNum_1>)+MS(<CertainNum_2>)+...;  //递归调用的情况
  else ...;                                                                //其他类似的情况
}

【举个栗子】

       现在要我们求斐波那契数列的第20项,读者不妨先用递归试试!再用上面的记忆化搜索的模板试试!

          记忆化搜索一般解法

#include <iostream>
using namespace std;
int MemArr[1000];               //记忆数组
int MS(int n)                 //定义MS函数用于读取记忆和存储数据
{
  int MemorySearch(int n);          //声明搜索函数
  if(!MemArr[n])MemArr[n] = MemorySearch(n);  //如果MemArr[n]的值不满足条件,说明MemArr[n]还未存储数据,则计算其应有的值并赋值给MemArr[n]
  return MemArr[n];             //返回MemArr[n]的值
}
int MemorySearch(int n)             //n:第n项
{
  if(n==1)return 0;             //斐波那契数列第1项
  else if(n==2)return 1;            //斐波那契数列第2项
  else return MS(n-1)+MS(n-2);        //斐波那契数列其他项
}
int main(){
  cout<<MemorySearch(20);           //输出斐波那契数列第20项
  return 0;
}

  记忆化宏函数解法

#include <iostream>
#define MS(n) (MemArr[n]?MemArr[n]:MemArr[n]=MemorySearch(n))    //记忆化的宏函数
using namespace std;
int MemArr[1001];                                                //记忆数组
int MemorySearch(int n)                                          //n:第n项
{
  if(n==1)return 0;                                            //斐波那契数列第1项
  else if(n==2)return 1;                                       //斐波那契数列第2项
  else return MS(n-1)+MS(n-2);                                 //斐波那契数列其它项
}
int main()
{
  cout<<MemorySearch(20);                                      //输出斐波那契数列第20项
  return 0;
}
目录
相关文章
|
2月前
|
算法
【算法】二分算法——搜索插入位置
【算法】二分算法——搜索插入位置
|
1天前
|
算法 决策智能
基于禁忌搜索算法的VRP问题求解matlab仿真,带GUI界面,可设置参数
该程序基于禁忌搜索算法求解车辆路径问题(VRP),使用MATLAB2022a版本实现,并带有GUI界面。用户可通过界面设置参数并查看结果。禁忌搜索算法通过迭代改进当前解,并利用记忆机制避免陷入局部最优。程序包含初始化、定义邻域结构、设置禁忌列表等步骤,最终输出最优路径和相关数据图表。
|
2天前
|
数据采集 算法 5G
基于稀疏CoSaMP算法的大规模MIMO信道估计matlab性能仿真,对比LS,OMP,MOMP,CoSaMP
该研究采用MATLAB 2022a仿真大规模MIMO系统中的信道估计,利用压缩感知技术克服传统方法的高开销问题。在稀疏信号恢复理论基础上,通过CoSaMP等算法实现高效信道估计。核心程序对比了LS、OMP、NOMP及CoSaMP等多种算法的均方误差(MSE),验证其在不同信噪比下的性能。仿真结果显示,稀疏CoSaMP表现优异。
10 2
|
14天前
|
大数据 UED 开发者
实战演练:利用Python的Trie树优化搜索算法,性能飙升不是梦!
在数据密集型应用中,高效搜索算法至关重要。Trie树(前缀树/字典树)通过优化字符串处理和搜索效率成为理想选择。本文通过Python实战演示Trie树构建与应用,显著提升搜索性能。Trie树利用公共前缀减少查询时间,支持快速插入、删除和搜索。以下为简单示例代码,展示如何构建及使用Trie树进行搜索与前缀匹配,适用于自动补全、拼写检查等场景,助力提升应用性能与用户体验。
34 2
|
2天前
|
存储 算法 C++
【搜索算法】 跳马问题(C/C++)
【搜索算法】 跳马问题(C/C++)
|
2天前
|
人工智能 算法 Java
【搜索算法】数字游戏(C/C++)
【搜索算法】数字游戏(C/C++)
|
2月前
|
机器学习/深度学习 算法 文件存储
【博士每天一篇文献-算法】 PNN网络启发的神经网络结构搜索算法Progressive neural architecture search
本文提出了一种名为渐进式神经架构搜索(Progressive Neural Architecture Search, PNAS)的方法,它使用顺序模型优化策略和替代模型来逐步搜索并优化卷积神经网络结构,从而提高了搜索效率并减少了训练成本。
40 9
|
2月前
|
算法
【算法】递归、搜索与回溯——汉诺塔
【算法】递归、搜索与回溯——汉诺塔
|
2月前
|
存储 算法 调度
基于和声搜索算法(Harmony Search,HS)的机器设备工作最优调度方案求解matlab仿真
通过和声搜索算法(HS)实现多机器并行工作调度,以最小化任务完成时间。在MATLAB2022a环境下,不仅输出了工作调度甘特图,还展示了算法适应度值的收敛曲线。HS算法模拟音乐家即兴创作过程,随机生成初始解(和声库),并通过选择、微调生成新解,不断迭代直至获得最优调度方案。参数包括和声库大小、记忆考虑率、音调微调率及带宽。编码策略将任务与设备分配映射为和声,目标是最小化完成时间,同时确保满足各种约束条件。
|
2月前
|
算法
【算法】递归、搜索与回溯——简介
【算法】递归、搜索与回溯——简介