数据结构和算法基础(一)[初识复杂度,对数器,二分法,位运算及简单应用:找到二进制最右面的1,找到数组中的唯一/唯二奇数次出现的数]

前言

  • 这系列主要是学习一些基础的算法,算法是一个程序员的内功,内功的高低决定了我们的武功上限.
  • 程序 = 数据结构 + 算法
    我们设计的程序中,程序的"逻辑流程"就是由一个个算法组成,而算法是基于某个/某些数据结构的(数组,hashmap等).

评估算法优劣的核心指标

一般只考虑样本数据为最差的情况,比如我们要正序排序一个数组,就考虑它现在是逆序的情况.

  • 1.时间复杂度(流程决定)
  • 2.额外空间复杂度(流程决定)
  • 3.常数项时间(实现细节决定)
    一般值比较时间复杂度就够啦;
    如果想精确比较,那么就由1到3一次比较,权重越来越小

一般情况下,认为解决一个问题的算法流程,在时间复杂度的指标上,一定要尽可能的低,先满足了时间复杂度最低这个指标之后,使用最少的空间的算法流程,叫这个问题的最优解。
一般说起最优解都是忽略掉常数项这个因素的,因为这个因素只决定了实现层次的优化和考虑,而和怎么解决整个问题的思想无关。

时间复杂度(流程决定)

时间复杂度就是总操作数量与样本数量之间的表达式关系,用来衡量一个算法(流程)中,发生了多少次常数操作.
我们只关心最高阶项的阶数,因为样本量足够大的时候,起决定性作用的就是它.

比如f(n1)=3n2,它的时间复杂度就是O(n2),
f(n2) = an + b,它的时间复杂度就是O(n),
我们就认为f(n2)是比f(n1)更优秀的算法

常数操作

如果一个操作的执行时间不以具体的样本量为转移,每次执行都是固定时间,这样的操作称作常数时间的操作.

常见的常数时间的操作
  • 常见的算术运算(+、-、*、/、% 等)
  • 常见的位运算(>>、>>>、<<、|、&、^等)
    “>>” 带符号右移,正数用0补高位,负数用1补
    ">>>"不带符号右移,都用0补高位
  • 赋值、比较、自增、自减操作等
  • 数组的寻址操作.(相反,链表的寻址就不是常数操作)

如何确定算法流程的总操作数量与样本数量之间的表达式关系

  1. 想象该算法流程所处理的数据状况,要按照最差情况来。
  2. 把整个流程彻底拆分为一个个基本动作,保证每个动作都是常数时间的操作。
  3. 如果数据量为N,看看基本动作的数量和N是什么关系。

常见的时间复杂度

排名从好到差:
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!)

简单分析 选择排序,冒泡排序,插入排序的时间复杂度

其实都是O(n2),只不过选择排序和冒泡排序不受样本数据的影响,而插入排序可能会因为样本数据的局部有序而节约时间.

选择排序

有一个数组,长度为n,那么
第一次从0~n-1中找出最小的一个,让它和0位置上的数交换,n此的看+比,和1次交换,n*(2)+1
第二次从1~n-1中选择最小的一个,让它和1位置上的数交换,(n-1)*(2)+1;

第n次从n-1~n-1中选择最小的一个,让它和n-1位置上的数交换;(其实啥也不用做),1

时间复杂度为:2*(n+n-1+…+1) + n = x * n2 + y * n + n + c = O(n2)

等差数列可以写成aN2+bN+c,1+2+3+…+n = (n+1)n/2 = 1/2n2 + 1/2*n

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

    public static void swap(int[] arr, int i, int j) {
   
        if (i == j) {
   
            return;
        }
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
冒泡排序

有一个数组arr,长度为n,
我们设一个操作为f(a):比较两个元素 如果arr[a]>arr[a+1],则把这两个位置的元素交换
那么
第一次在0到n-1之间,从0到n-2做f(a)操作,f(0),f(1),f(2)…f(n-1);
第二次从0到n-2之间,从0到n-3做f(a)操作;

第n-1次完事.

这也是个等差数列,所以时间复杂度也是O(n2)

	public static void sort(int[] arr) {
   
        if (arr == null || arr.length < 2) {
   
            return;
        }
        /*for (int i = 0; i < arr.length - 1; i++) {
            for (int j = 0; j < arr.length - 1 - i; j++) {
                if (arr[j] > arr[j + 1]) {
                    swap(arr, j, j + 1);
                }
            }
        }*/
        for (int e = arr.length - 1; e > 0; e--) {
    // 0 ~ e
            for (int i = 0; i < e; i++) {
   
                if (arr[i] > arr[i + 1]) {
   
                    swap(arr, i, i + 1);
                }
            }
        }
    }
插入排序

有一个数组arr,长度为n,
第一次,保持位置0到0上有序,啥也

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值