zoukankan      html  css  js  c++  java
  • 【左程云算法 01】算法的 概念 与 性质

    【左程云 01】算法的 概念 与 性质




    算法要求:在保证时间复杂度最低的前提下,尽可能减小空间复杂度与常数项时间复杂度;



    优劣算法评估

    • 时间复杂度(流程决定)
      • 忽略低阶项 与 高阶项的系数,留下来的就是时间复杂度;
      • 如一个流程最后得到的算式是 3 * N4 + 4 * N2 + C ,则最终时间复杂度就是 O( N4 )
    • 额外空间复杂度(流程决定)
      • 与功能无关,必须自己开辟的额外空间
      • 用户要求的功能,(如复制一个数组),则实现这个功能所使用的代码空间不算
    • 常数项时间(实现细节决定)


    常数与非常数时间操作

    • 什么是常数时间操作?
      • 概念:一个数据改变偏移量,操作所需要的时间不变就是常数时间操作;

      • 实例:从数组中取第一个与取第100000个元素所需要的时间一样,则这就是一个常数操作;

      • 常见的此类操作:

        • 加 / 减 / 乘 / 除 / 取余 :+ - * / %
        • 常见的位运算操作(>> / >>> / << / | & / ^ 等)
        • 赋值、比较、自增、自减操作等
        • 数组寻址操作
    • 什么是常数时间操作:
      • 除了常数时间操作都是非常数时间操作;
      • 举例:LinkedList,寻址的时候需要遍历;
    • 排序算法中的常见优劣性

    尽量可以背下来;


    中文名称 英文名称 平均时间复杂度 最坏时间复杂度 最好时间复杂度 空间复杂度 稳定性
    选择排序 Selection 1 ×
    冒泡排序 Bubble n 1
    插入排序 Insertion n 1
    堆排序     √ Heap n log2 n n log2 n n log2 n 1 ×
    希尔排序 Shell n1.3 n2 n 1 ×
    归并排序 Merge n log2 n n log2 n n log2 n n
    快速排序 Quick n log2 n n2 n log2 n log2 n ×
    桶排序 Bucket n + k n2 n n + k
    计数排序 Counting n + k n + k n + k n + k
    基数排序 Radix n * k n * k n * k n + k

    举例:插入排序的最优与最差时间复杂度

    • 最优例子:【0 1 2 3 4 5 6 】
    • 最差例子:【6 5 4 3 2 1 0 】



    合理使用对数器



    • 认识对数器
      • 想要测试的 方法 a
      • 实现复杂度不好但使用以实现的 方法 b
      • 实现一个随机样本产生器
      • 把 方法 a 和 方法 b 跑相同的随机样本,看得到的结果是否一致
      • 如果一个随机样本使比对结果不一致,打印样本进行人工干预,改对 方法 a 和 方法 b;
      • 当样本数量很多时比对测试依然正确,则可以确定方法 a 已经正确。





    算法示例(重要!)



    • 从一个长度为 n 的 有序数组 中寻找一个数字 m;

    方法一:遍历: O( n )

    方法二:二分查找法: O( log2 N )




    • 从一个长度为 n 的 序数组(相邻不等) 中寻找一个局部最小值(任意一个);




    • 一个数组中有一种数出现了奇数次,其他的都是偶数次,找出这个奇数次的数字:
    // 用一个 0 从头异或到尾,最后这个 0 数字就会变成 那个 奇数数字;
    int eor = 0;
    
    int[]  arr = [ 2,1,3,2,2,4,1,3,1,2,4,1,4];
    
    for(int i = 0;i < arr.size();i++){
    
    	eor = aor^arr.get(i);
    
    }
    



    • abcde 五个数字异或的结果一样,顺序无所谓( 满足 交换律结合律

    异或常用公式

    0 ^ N == N

    N ^ N == 0

    a b 两个数字交换值:(自己推导一下,很简单,不懂得可以看推导过程 A01。)

    a = a ^ b

    b = a ^ b

    a = a ^ b




    • 一个数字,取出最右侧的 1 ,如 14 二进制之后变成【 1 1 1 0】 取出下划线的 1

    如 要计算的是 a

    则 b = a取反 + 1

    a = a ^ b;




    • 一个数组中有两种数(这两个数字不相等且 a ! = 0,b != 0 )出现了奇数次,其他的都是偶数次,找出这两种奇数次的数字:

    根据第三题的理论:好比这两个数字是 a 和 b,eor 异或之后 得到的结果是 a ^ b(a != b)

    根据第五题的理论:eor 一定在某一个位置有个1(记此位置为 n ),那这个位置对于 a 和 b 来说,一定不一样,(a 和 b 在这一位上一定一个是 0 一个是 1)

    那整个数组一定可以分为两大类 :第n位为 0 的数 与 第n位为 1 的数,出现偶数次的数无论怎么分都无所谓,所以执行下面的步骤:

    eor异或之前 + 一个条件,只要第 n 位为 0 (或者只异或 为 1的)

    int rightOne = eor & (~eor + 1); // 取出最右侧的那个 1
    int onlyOne  = 0;// eor'
    for(int i = 0;i < arr.length;i++){
        if((arr[i]) & rightOne)!=0){
            onlyOne ^= arr[i];
        }
    }
    System.out.println(onlyOne+""+(eor^onlyOne));
    

    那么一个数已经出来了,就是eor,另一个数就是 eor ^ eor' ( eor 异或上 eor 取反)




    • 找一个二进制数字中所有的 1

    找出最右侧的 1 记为 rightOne

    rightOne 与 这个数字 异或,这就消除了最右侧的 1

    重复第一步与第二部,直到找不到 rightOne





    补充



    推导过程 A01

    甲 乙 两个数字交换值,让int a = 甲,int b = 乙:

    • a = a ^ b

    此时 a = 甲 ^ 乙 ;b = 乙;

    • b = a ^ b

    此时 b = 甲 ^ 乙 ^ 乙;(由于 a 在上一步已经等于 “甲 ^ 乙” 了)

    根据公式:N ^ N == 0,所以 b ^ b == 0;(异或满足交换定律,运算顺序无所谓)

    根据公式:0 ^ N == N,所以 a ^ 0 == a , 所以 b = a;

    • a = a ^ b

    此时的 a == 甲 ^ 乙,b == 甲,所以这个算是可以写成:a = 甲 ^ 乙 ^ 甲;

    所以得到 a = 乙(原理同第二步)



    二分法注意事项:

    方法一:mid = ( L + R ) / 2

    改 进:mid = L + ( R - L ) / 2

    N / 2 == N >> 1 (位运算速度比除运算快)

    mid = L + ( R - L ) >> 1

  • 相关阅读:
    MySQL数据库触发器
    软碟通制作fedora17 U盘启动的方法
    编译自己功能定制的Fedora7内核
    SUSE Linux 10配置裸设备(raw devices)
    linux之cut用法
    python---opencv常用函数
    vscode安装以及如何连接服务器
    pip 安装包问题汇总
    conda创建环境失败的解决方法
    git操作
  • 原文地址:https://www.cnblogs.com/hskcool/p/14360761.html
Copyright © 2011-2022 走看看