认识复杂度、对数器、二分法

简介: 认识复杂度、对数器、二分法

常数操作

评估算法的优劣指标

1.时间复杂度(流程决定)

2.额外空间复杂度(流程决定)

3.常数项时间(实现细节决定)

常数时间的操作指的是固定时间的操作

例如:

有两个int型整数的a,b相加

在Java中由于int型都占32位,所以a+b 位数一样 因此是固定时间

在数组中的寻址,在内存中是连续区间,直接根据偏移量去寻找,直接去找200万位置的数组也是常数时间

常见的常数实现操作:

•常见的算术运算 (+、-、*、/、%)

•常见的位运算(>>、>>>、<<、|、&、^等)

•赋值、比较、自增、自减操作等

•数组寻址操作

时间复杂度

在一个要计算的流程操作中,常数量位为N的话,执行完整个流程操作,用来描述我的常数量操作是个什么样的关系。

选择排序

选择算法思路

在一个整数数组中,长度为N,实现从小到大的排序操作。描述整个算法流程

arr[0~N-1]范围上,找到最小值,把最大值交换到0位置处。(1次交换)

arr[1~N-1]范围上,找到最小值,把最大值交换到1位置处。(1次交换)

arr[2~N-1]范围上,找到最小值,把最大值交换到2位置处。(1次交换)

每一步的操作都是常数级别,交换N次,通过看加比较的方式得出时间复杂度便是一个等差数列:aN^2+ bN^2+c

便得到选择排序的时间复杂度就是O(N^2)

选择排序的代码实现

public static void selectSort(int[] arr){
  if(arr == null || arr.length < 2){
    return;
  }
  for(int i = 0;i < arr.length -1 ;i++){
    int minIndex = i;
    for(int j = i + 1;j < arr.length;j++){
      minIndex = arr[j] < arr[minIndex] ? j : minIndex;
    }
    swap(arr,i,minIndex);
  }
}
public static void swap(int[] arr,int i,int j){
  int tmp = arr[i];
  arr[i] = arr[j];
  arr[j] = tmp;
}

冒泡排序

冒泡算法思路

在arr[0~N-1]范围上:arr[0]和arr[1],谁大谁来到1位置;arr[1]和arr[2],谁大谁来到2位置…arr[N-2]和arr[N-1],谁大谁来到N-1位置


在arr[0~N-2]范围上,重复上面的过程,但最后一步是arr[N-3]和arr[N-2],谁大谁来到N-2位置

在arr[0~N-3]范围上,重复上面的过程,但最后一步是arr[N-4]和arr[N-3],谁大谁来到N-3位置 …

最后在arr[0~1]范围上,重复上面的过程,但最后一步是arr[0]和arr[1],谁大谁来到1位置

很明显,如果arr长度为N,每一步常数操作的数量,依然如等差数列一般。所以,总的常数操作数量 =a*(N^2)+ b*N + c (a、b、c都是常数)

所以冒泡排序的时间复杂度为O(N^2)。

代码实现

public static void bobbleSort(int[] arr){
  if(arr == null || arr.length < 2){
    return;
  }
  for(int e = arr.length -1 ;e > 0;e--){
    for(int i = 0;i < e; i ++){
      if(arr[i] > arr[i+1]){
        swap(arr,i,i+1)
      }
    }
  }
}
public static void swap(int[] arr,int i,int j){
  arr[i] = arr[i] ^ arr[j];
  arr[j] = arr[i] ^ arr[j];
  arr[i] = arr[i] ^ arr[j];

插入排序

插入算法思路

想让arr[0~0]上有序,这个范围只有一个数,当然是有序的。

想让arr[0~1]上有序,所以从arr[1]开始往前看,如果arr[1]<arr[0],就交换。否则什么也不做。 …

想让arr[0~i]上有序,所以从arr[i]开始往前看,arr[i]这个数不停向左移动,一直移动到左边的数字不再比自己大,停止移动。

最后一步,想让arr[0~N-1]上有序,arr[N-1]这个数不停向左移动,一直移动到左边的数字不再比自己大,停止移动。

估算时发现这个算法流程的复杂程度,会因为数据状况的不同而不同。

最差情况估计

最差情况下,如果arr长度为N,插入排序的每一步常数操作的数量,还是如等差数列一般;所以,总的常数操作数量 =a*(N^2)+ b*N + c (a、b、c都是常数)

代码实现

pupublic static void insertionSort(int[] arr){
  if(arr == null || arr.length < 2){
    return;
  }
  for(int i = 1;i<arr.length;i++){
    for(int j = i - 1;j >= 0 && a[j] > arr[j+1] ;j--){
      swap(arr,j,j+1);
    }
  } 
}

public static void swap(int[] arr,int i,int j){
  arr[i] = arr[i] ^ arr[j];
  arr[j] = arr[i] ^ arr[j];
  arr[i] = arr[i] ^ arr[j];
}

额外的空间复杂度

1.算法的过程,和具体的语言是无关的。

2.想分析一个算法流程的时间复杂度的前提,是对该流程非常熟悉

3.一定要确保在拆分算法流程时,拆分出来的所有行为都是常数时间的操作。这意味着你写算法时,对自己的用过的每一个系统api,都非常的熟悉。否则会影响你对时间复杂度的估算。

4.如果你的流程只需要开辟有限几个变量,额外空间复杂度就是O(1)。就是常数时间,开辟空间来办事 最差情况考虑开辟空间的复杂度 注意有限几个变量这个很关键 用户要求不算

5.作为输入参数的空间,不算额外空间。程序员的自主空间;作为输出结果的空间,也不算额外空间。因为这些都是必要的、和现实目标有关的。所以都不算。

6.除此之外,你的流程如果还需要开辟空间才能让你的流程继续下去。这部分空间就是额外空间。如果你的流程只需要开辟有限几个变量,额外空间复杂度就是O(1)。

算法流程的常数项

当两个算法的时间复杂度一样的时候 拼常数时间 常数项

冒泡 和 插入 插入就是比冒泡快 冒泡排序就是没有插入的好 尽管时间复杂度相同

如何分析?

放弃理论分析 生成随机数直接测试

例如加操作就是没有异或快 除法操作就是没有加号快

常数操作类型是很多的 那就实际跑来估计

算法的最优解

时间复杂度尽可能低

空间复杂度小

常数时间操作在这两者之外 就比至高处项系数

时间复杂度从好到差的排名

O(1)

O(logN)

O(N)

O(N*logN)

O(N^2) O(N^3) … O(N^K)

O(2^N) O(3^N) … O(K^N)

O(N!)

对数器的认识

认识对数器:

1,你想要测的方法a

2,实现复杂度不好但是容易实现的方法b

3,实现一个随机样本产生器

4,把方法a和方法b跑相同的随机样本,看看得到的结果是否一样

5,如果有一个随机样本使得比对结果不一致,打印样本进行人工干预,断点调试,改对方法a和方法b

6,当样本数量很多时比对测试依然正确,可以确定方法a已经正确。

计算机0~1进行离散是有穷个的

认识二分法

只要能正确构建左右两侧的淘汰逻辑,就可以使用二分法

估计时间复杂度最差法来估计 下标换算 数组寻址 常数时间 每次砍掉一半查看

常数时间O(1) O(log2N)时间复杂度 简写为 O(logN)

  1. 在一个有序数组中,找>=某个数最左侧的位置
    思想还是不变 二分可以实现

二分法的代码实现

  public static boolean exist(int[] sortedArr, int num) {
    if (sortedArr == null || sortedArr.length == 0) {
      return false;
    }
    int L = 0;
    int R = sortedArr.length - 1;
    int mid = 0;
    // L..R
    while (L < R) { // L..R 至少两个数的时候
      mid = L + ((R - L) >> 1);
      if (sortedArr[mid] == num) {
        return true;
      } else if (sortedArr[mid] > num) {
        R = mid - 1;
      } else {
        L = mid + 1;
      }
    }
    return sortedArr[L] == num;
  }
相关文章
|
机器学习/深度学习 人工智能 算法
机器学习是什么?
机器学习是什么?
955 4
|
JavaScript
vue可拖拽悬浮按钮组件
vue封装一个可拖拽,贴边吸附的悬浮按钮组件。
2632 0
vue可拖拽悬浮按钮组件
|
Linux Go iOS开发
IDA 9.2 发布:Golang 改进、新 UI 组件、类型解析等
IDA Pro 9.2 (macOS, Linux, Windows) - 强大的反汇编程序、反编译器和多功能调试器
1762 0
|
11月前
|
安全 前端开发 Java
从反射到方法句柄:深入探索Java动态编程的终极解决方案
从反射到方法句柄,Java 动态编程不断演进。方法句柄以强类型、低开销、易优化的特性,解决反射性能差、类型弱、安全性低等问题,结合 `invokedynamic` 成为支撑 Lambda 与动态语言的终极方案。
413 0
|
11月前
|
Java API 开发工具
百宝箱开放平台 ✖️ Java SDK
百宝箱提供Java SDK,支持开发者集成其开放能力。需先发布应用,准备Java 8+及Maven环境,通过添加依赖安装SDK,并初始化客户端调用对话型或生成型智能体,实现会话管理、消息查询与文件上传等功能。
1916 0
百宝箱开放平台 ✖️ Java SDK
|
存储 安全 Java
基于Java+MySQL停车场车位管理系统详细设计和实现(源码+LW+调试文档+讲解等)
基于Java+MySQL停车场车位管理系统详细设计和实现(源码+LW+调试文档+讲解等)
|
SQL 数据可视化 数据挖掘
本地数据调用析言的解决方案
本文介绍了通过API创建虚拟数据库,利用阿里云百炼/析言GBI平台实现数据查询、分析及可视化的方法。方案结合本地与云端资源,确保数据安全,同时提供灵活的API调用方式,支持按需调用析言的各项功能,有效降低已有本地数据库系统的迁移成本,提升数据分析效率。
1750 11
|
搜索推荐
有趣的网站分享——福音戰士標題生成器
有趣的网站分享——福音戰士標題生成器
3294 0
|
人工智能 自然语言处理 监控
PMP认证有哪些要求:干货分享
本文详细介绍PMP的报考要求、考试内容与形式、认证有效期及价值,以及备考策略和推荐的学习工具,帮助考生高效准备。
782 2
|
Prometheus 监控 Cloud Native
调用淘宝 API 时如何处理错误信息?
调用淘宝API时,需熟悉其错误码体系,处理客户端(如参数错误、权限不足)和服务器(如内部错误、网络问题)错误,编写错误处理逻辑,并进行充分测试与监控,确保API调用稳定可靠。

热门文章

最新文章