首页 > 编程语言 >基础数据结构——二分算法及时间复杂度

基础数据结构——二分算法及时间复杂度

时间:2024-08-21 09:58:33浏览次数:11  
标签:二分 log floor int 复杂度 查找 数据结构 fn target

这个算法的前提是,数组是升序排列的

算法描述:

i和j是指针可以表示查找范围

m为中间值

当目标值targat比m大时,设置查找范围在m右边:i =m-1

当目标值targat比m小时,设置查找范围在m左边:j =m+1

当targat的值等于m时,返回m的下标

如果最终没找到就返回-1;

算法实现:

public  int  birthDay(int[] a,int target){
    int i=0;
    int j=a.length-1;
    while(i<=j){
        int m= (i+j)/2;
        if(a[m]<target){
            //                目标值在右边
            i= m-1;
        } else if (target<a[m]) {
            //                目标值在左边
            j = m - 1;

        }else if (a[m] == target){
            //找到
            return m;
        }
    }
    return -1;
}

public static void main(String[] args) {

    int targat=4;
    a1 a1= new  a1();
    int a[]= new int[]{2,4,6,8,9};
    int num=a1.birthDay(a,targat);
    System.out.println(num);
}

问题一:为什么是i<=j ,而不是i<j?

因为i 和 j 指向的元素也会参与比较

问题二:(i+j)/2有没有问题?

在 Java 中,表达式 int a = (i + j) / 2 的结果是向下取整。整数除法会丢弃小数部分 。但是如果遇到非常大的数字呢?

Integer.MAX_VALUE2147483647,即 int 类型能够表示的最大值。

当第一次计算时:i + j 结果是 0 + 2147483647 = 2147483647

当第二次计算时:i = 1073741824j = 2147483647

i + j 结果是 1073741824 + 2147483647 = 3221225471

3221225471这个数字超过了整型能够表示的最大值,所以变成了负数:

public static void main(String[] args) {
    int i= 0;
    int j=Integer.MAX_VALUE;
    int m=(i+j)/2;
    System.out.println(m);
    System.out.println("————————————————————————————————————————————");

    i= m+1;  //结果在右边
    m= (i+j)/2;
    System.out.println(m);
}

>>>移位运算符解决:

>>>将二进制数的每一位向右移动一位,丢弃最右边的位,同时在最左边填充零。

使得结果变为正整数

2^7 ------> 2^6 就可以代替/2

例如:

1001 =9

移位后:

0100 = 4

int类型范围解释:

在 32 位带符号整数中,最高位是符号位,0 表示正数,1 表示负数。

剩下的 31 位用于表示数值。

int整型数据类型有32位,因此,数据的范围不带符号可表示为【0,2^32】;

数据的范围带符号可表示为【-2^31,2^31-1】,那么这里我们为什么要减一呢?因为带符号时,一个符号占位也是一位byte。

那负数为啥不-1????

Java 的 int 类型范围是从 -2^312^31 - 1

是求补码的步骤:

  1. 确定原码:首先,写出整数的原码(即正数的二进制表示)。
  2. 取反:将原码中的每一位取反(0 变 1,1 变 0)。
  3. 加一:在取反后的结果上加 1,得到补码。

线性查找代码:

找到返回索引,找不到返回-1

这个和二分法比起来明明这个线性查找代码更加简洁明了,为什么要使用二分法呢?

    public  int LinearSearch(int[] a, int target){
        for (int i = 0; i<a.length;i++){
            if(a[i]==target){
                return i;
            }
        }
        return -1;
    }
}

事前分析法:

因为每个人电脑配置都不一样,所以运行时间是不一样的,所以运行时间不能评判算法的好坏,所以我们需要在运行前分析一下算法。

线性查找代码分析:

1.最差执行情况

2.假设每行语句的执行时间一样

当元素个数是n时:

最坏情况是查找到最后都没找到,所以 最坏情况return i;语句执行0次

数据元素个数n

执行次数

int i = 0;

1

i<a.length;

n+1

i++

n

a[i]==target

n

return -1;

1

    public  int LinearSearch(int[] a, int target){
        for (int i = 0; i<a.length;i++){
            if(a[i]==target){
                return i;
            }
        }
        return -1;
    }
}

语句执行总次数:3n+3

二分法代码分析:

最差情况:查找范围在右侧,并且没找到

当元素个数是n时:

已经确定的执行语句和次数:

int i=0; 1

int j=a.length-1; 1

return -1; 1

  public  int  birthDay(int[] a,int target){
        int i=0;
        int j=a.length-1;
        while(i<=j){
            int m= (i+j) >>> 1;
            if(a[m]<target){
                i= m-1;
            } else if (target<a[m]) {
                j = m - 1;
            }else if (a[m] == target){
    
                return m;
            }
        }
        return -1;
    }

eg: target=9 [2,3,4,5] 查找次数为 3 也就是说while循环执行了三次

不能得出整数,就向下取整

eg:log2(7)=2.x 就等于2

元素个数

循环次数

4-7

3

floor(log_2(4)) =2 +1

8-15

4

floor(log_2(8)) =3 +1

16-31

5

floor(log_2(16)) =4 +1

32-63

6

floor(log_2(32)) =5 +1

得出规律:n为元素个数

循环次数L:floor(log_2(n)) +1

循环语句:

循环次数

i<=j

L+1

int m= (i+j) >>> 1;

L

a[m]<target

L

target<a[m]

L

j = m - 1;

L

语句执行总次数:5L+4

(floor(log_2(n)) +1 )*5 + 4

化简一下:f(n)=5*log_2(n)+9

对比图象发现:当数据量大的时候还是二分法的执行次数少

时间复杂度O

抛开硬件软件,只考虑算法本身

如何表示时间复杂度??

  • 线性查找算法的函数:fn=3*n+3
  • 二分查找算法的函数:fn=floor(log_2(n))*5 +4

因为我们得出的这两个函数比较复杂,不能让人一眼就看出,所以我们要对fn进行化简,找到一个与它变化趋势相近的表示法

这里:

  • c1代表一个常数
  • f(n)时实际执行代码行数与n的函数
  • g(n)经过花间,变化趋势与f(n)一致的函数

渐进上界:g(n)代表的是最差的一种情况

例一:

  • fn = 3*n+3
  • gn = n
  • 取c=4,在n=3之后,gn可以作为渐进上界,因此表示写法为O(n)

例二:

  • fn=(floor(log_2(n)) +1 )*5 + 4
  • 化简后fn=5*floor(log_2(n)) +9
  • gn = log_2(n)
  • O( log_2(n))

已知f(n),求g(n)

  • 表达式中相乘的常量可以省略: fn = 3*n+3省略 3
  • 多项式中数量规模更小的表达式(低次项): fn = n³+n² 中的n² 因为n²的变化远小于n³
  • 不同底数的对数

渐进下界和渐进紧界:

渐进下界:最优情况 Ω欧米噶

渐进紧界:两种情况都能代表 theta

时间复杂度从低到高:

对应颜色:

空间复杂度:

用O表示:衡量一个算法,随着数据规模的增大,占用的额外空间成本

  • 固定部分:与输入规模无关的内存空间,如固定数量的变量、常量、常数空间等。
  • 可变部分:随着输入规模变化而变化的内存空间,如动态分配的数组、递归调用栈空间等。

二分查找:

需要常数个指针i,j,m,因此额外占用的空间是O(1)

public  int  birthDay(int[] a,int target){
    int i=0;
    int j=a.length-1;
    while(i<=j){
        int m= (i+j)/2;
        if(a[m]<target){
            i= m-1;
        } else if (target<a[m]) {
            j = m - 1;

        }else if (a[m] == target){
            return m;
        }
    }
    return -1;
}

标签:二分,log,floor,int,复杂度,查找,数据结构,fn,target
From: https://blog.csdn.net/qq_62859013/article/details/141384005

相关文章

  • 力扣热题100_二分查找_74_搜索二维矩阵
    文章目录题目链接解题思路解题代码题目链接74.搜索二维矩阵给你一个满足下述两条属性的mxn整数矩阵:每行中的整数从左到右按非严格递增顺序排列。每行的第一个整数大于前一行的最后一个整数。给你一个整数target,如果target在矩阵中,返回true;否则,返回fa......
  • 知识改变命运 数据结构【栈和队列】
    1.栈(Stack)1.1概念栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(LastInFirstOut)的原则。压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。出栈:栈的删......
  • 可持久化数据结构1
    非持久化数据结构一般需要维护数据集最新状态,而可持久化要求查询历史状态。可持久化Trie树朴素:每次修改重新存一遍\(->MLE\)。正解:只存被修改部分,其余不变,即第\(i\)次修改后,树变为第\(i\)次修改产生新的部分加上前\(i-1\)次修改产生部分。增长同规模。用普通线段树维......
  • 二分查找法
    二分查找法定义:二分查找也称折半查找(BinarySearch),它是一种效率较高的查找方法。前提:​1.必须采用顺序存储结构。​2.必须按关键字大小有序排列。我们以一个练习题为例:假如有一组数为3,12,24,36,55,68,75,88要查给定的值24.思路:可设三个变量front,mid,end分别指向数据......
  • 【数据结构】栈和队列的实现
    正文1.栈1.1概念与结构栈:⼀种特殊的线性表,其只允许在固定的⼀端进⾏插⼊和删除元素操作。进⾏数据插⼊和删除操作的⼀端称为栈顶,另⼀端称为栈底。栈中的数据元素遵守后进先出LIFO(LastInFirstOut)的原则。压栈:栈的插⼊操作叫做进栈/压栈/⼊栈,⼊数据在栈顶......
  • 【C++二分查找 前缀和 】1292. 元素和小于等于阈值的正方形的最大边长
    本文涉及的基础知识点C++二分查找C++算法:前缀和、前缀乘积、前缀异或的原理、源码及测试用例包括课程视频LeetCode1292.元素和小于等于阈值的正方形的最大边长给你一个大小为mxn的矩阵mat和一个整数阈值threshold。请你返回元素总和小于或等于阈值的正方形区......
  • 数据结构-队列 c语言使用链表和数组分别实现
    队列定义队列(queue)是一种遵循先入后到规则的线性数据结构,将队列头部称为“队首”,尾部称为“队尾”,把元素加入队尾称为“入队”,删除队首元素称为“出队”。队列实现基于链表的实现将链表的头节点和尾结点分别视为“队首”和“队尾”,规定队尾仅可添加节点,队首仅可删除节点。......
  • 洛谷P3528 [POI2011] PAT-Sticks && 数据结构之堆
    传送门:P3528[POI2011]PAT-Sticks与买桂花同载酒,终不似,少年游这是现在为止洛谷上的最优解!!翻译题目描述小约翰尼的爷爷奶奶送给他一份生日礼物。这份礼物是一盒长度和颜色各异的木棍。约翰尼想知道,在他得到的这组木棍中,是否存在三根木棍能够组成一个三边颜色各不相同的三......
  • 《数据结构》最短路径Dijkstra算法
                                    最短路径Dijkstra算法分析生长点ABCDEFP(A)=FAD(A)=130P(B)=FBD(B)=24P(C)=FCD(C)=10P(D)=——D(D)=无穷P(E)=——D(E)=无穷CP(A)=FAD(A......
  • JAVA的数据结构
    JAVA数据结构一、数组(Arrays)可以存储固定大小的相同类型的元素。int[]array=newint[5];优点:随机访问元素效率高缺点:大小固定,插入和删除元素相对较慢二、列表(Lists)1、ArrayListList<String>arrayList=newArrayList<>();特点:动态数组,可变大小优点:高效的随机访......