• 算法路漫漫(一) 简单排序


    1.认识时间复杂度

    常数时间的操作:一个操作如果和数据量没有关系,每次都是固定时间内完成的操作,叫做常数操作。
    时间复杂度为一个算法流程中,常数操作数量的指标。常用O(读作big O)来表示。具体来说,在常数操作数量的表达式中,只要高阶项,不要低阶项,也不要高阶项的系数,
    剩下的部分如果记为f(N),那么时间复杂度为O(f(N))。评价一个算法流程的好坏,先看时间复杂度的指标,然后再分析不同数据样本下的实际运行时间,也就是常数项时间。

    衡量算法复杂度

    1.内存(Memory)
    2.时间(Time)
    3.指令的数量(Number of Steps)
    4.特定操作的数量
        磁盘访问数量
        网络包数量
    5.渐进复杂度(Asymptotic Complexity)

    算法的运行时间与什么相关

    1.输入的数据。(例如:如果数据已经是排好序的,时间消耗可能会减少。)
    2.输入数据的规模。(例如:6 和 6 * 1093.运行时间的上限。(因为运行时间的上限是对使用者的承诺。)

    算法分析要保持大局观(Big Idea),其基本思路:

    1.忽略掉那些依赖于机器的常量。
    2.关注运行时间的增长趋势。

    比如:T(n) = n3 + 99n3 + 9999 的趋势就相当于 T(n) = Θ(n3)。

    渐近记号(Asymptotic Notation)通常有 O、 Θ 和 Ω 记号法。big O 表示按算法最差表现估算,big θ  表示按算法平均表现估算,big Ω 表示按算法最好表现估算。尽管技术上 Θ 记号较为准确,但通常仍然使用 O 记号表示。

    使用 O 记号法(Big O Notation)表示最坏运行情况的上界。例如,

    1.线性复杂度 O(n) 表示每个元素都要被处理一次。
    2.平方复杂度 O(n2) 表示每个元素都要被处理 n 次。
    Notation Intuition Informal Definition

    f is bounded above by g asymptotically

    Two definitions :
    Number theory:

    f is not dominated by g asymptotically

    Complexity theory:

    f is bounded below by g asymptotically

    f is bounded both above and below by g asymptotically

     2.简单排序

    2.1 选择排序

    选择排序(Select Sort) 是直观的排序,通过确定一个 Key 最大或最小值,再从带排序的的数中找出最大或最小的交换到对应位置。再选择次之。双重循环时间复杂度为 O(n^2) 

     sellection sort

        private void sellectionSorted(int[] arr){
            if(arr == null || arr.length <=1){
                return;
            }
            // i ~ N-1
            for(int i=0;i<arr.length-1;i++){
                int minIndex=i;
                // i ~ N-1 get min
                for(int j=i+1;j<arr.length;j++){
                    minIndex = arr[j] < arr[minIndex] ? j : minIndex;    
                }
                swap(arr ,minIndex,i);
            }
        }
    
    
        private void swap(int[] arr, int a, int b){
            int tmp = arr[a];
            arr[a] = arr[b];
            arr[b] = tmp;
        }

    2.2 冒泡排序

    冒泡排序(Bubble Sort) 最为简单的一种排序,通过重复走完数组的所有元素,通过打擂台的方式两个两个比较,直到没有数可以交换的时候结束这个数,再到下个数,直到整个数组排好顺序。因一个个浮出所以叫冒泡排序。双重循环时间 O(n^2)

     bubble sort

        private void bubbleSorted(int[] arr){
            if(arr == null || arr.length <=1){
                return;
            }
            // i ~ N-1
            for(int i=arr.length-1;i>0;i--){
                // i ~ N-1 set max to the end of arr
                for(int j=0;j<i;j++){
                    if(arr[j+1] < arr[j]) {
                        swap(arr ,j+1,j);
                    } 
                }
                
            }
        }
    
        // 异或运算
        private void swap(int[] arr, int a, int b){
            arr[a] = arr[a]^arr[b];
            arr[b] = arr[a]^arr[b];
            arr[a] = arr[a]^arr[b];
        }

    2.3 插入排序

    插入排序(Insertion-Sort)的算法描述是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,通常采用in-place排序(即只需用到O(1)的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。

    insertion sort

        private void insertSorted(int[] arr){
            if(arr == null || arr.length <=1){
                return;
            }
            // 0 ~1 compare and swap
            // 0 ~2 compare and swap
            for(int i=0;i<arr.length;i++){
                // i ~ N-1 compare and swap
                for(int j=i;j>0;j--){
                    if(arr[j-1] > arr[j]) {
                        swap(arr ,j-1,j);
                    } 
                }
                
            }
        }
    
        // 异或运算
        private void swap(int[] arr, int a, int b){
            arr[a] = arr[a]^arr[b];
            arr[b] = arr[a]^arr[b];
            arr[a] = arr[a]^arr[b];
        }

    2.4 二分法

    二分法查找,也称为折半法,是一种在有序数组中查找特定元素的搜索算法。首先,从数组的中间元素开始搜索,如果该元素正好是目标元素,则搜索过程结束,否则执行下一步。如果目标元素大于/小于中间元素,则在数组大于/小于中间元素的那一半区域查找,然后重复上面的的操作。如果某一步数组为空,则表示找不到目标元素。二分法查找的时间复杂度O(logn)。

        private int searchMatch(int[] arr, int low, int high, int target){
            if(low > high){
                return -1;
            }
            int mid = low + ((high-low)>>1);
    
            // 
            if(arr[mid] == target){
                return mid;
            } else if(arr[mid] < target){
                // search in [mid+1, high]
                return searchMatch(arr, mid+1, high, target);
            } else {
                // search in [low, mid-1]
                return searchMatch(arr, low, mid-1, target);
            }
        }

    一般下面几个场景适合二分查找

    1.在一个有序数组中,查找某个数是否存在
    
    2.在一个有序数组中,查找>=某个数最左侧位置
    
    3. 在一个无序数组中,任何相邻两数不相等,寻找局部最小值

    3.补充

    3.1 异或运算

    1. 0^N=N, N^N=0

    2.异或运算满足交换率和结合率
    A^B = B^A, A^(B^C) = (A^B)^C
    3.同样一批数,异或运算,先后顺序变化 不影响结果
    A^B^...^Z = Z^Y^...^A
    4.无进位加法,相同为0,不同为1;
    int a= 3= 011
    int b= 5= 101
    int c= a^b = 011^101=110=6

    3.2 取出一个数对应二进制最右侧的1对应的整数

    // 与上自己的取反加1
    int
    a= A & (~A+1);

     3.3 对数器

    1.有一个你想要测的方法A
    2.实现一个绝对正确但是复杂度不好的方法B
    3.实现一个随机样本产生器
    4.实现比对的方法
    5.把方法A和方法B比对很多次来验证方法B是否正确。
    6.如果有一个样本使得比对出错,打印样本分析是哪个方法出错
    7.当样本数量很多时比对测试(假设10万次测试)依然正确,可以确定方法A已经正确
  • 相关阅读:
    easyUI的汇总列,在前端生成
    return返回两个三元表达式的和,返回不正确,同样要注意在JavaScript中,也是如此
    清除浮动有哪些方式?比较好的方式是哪一种?
    浏览器重置样式
    MUI框架的缩写输入
    会自动消失的提示信息
    JSON.stringify转化报错
    Jquery on方法绑定事件后执行多次
    属性索引选择元素
    字符串赋值数字时的问题
  • 原文地址:https://www.cnblogs.com/hlkawa/p/14883359.html
Copyright © 2020-2023  润新知