美文网首页
android知识巩固(算法)

android知识巩固(算法)

作者: 只会敲代码的键盘手 | 来源:发表于2020-05-14 17:54 被阅读0次

算法(Algorithm)是指解题方案的准确而完整的描述,是一系列解决问题的清晰指令

1.前言

数据结构和算法是一个软件开发者的基本功,如果把数据结构类比成内功,那么算法就是心法.算法在计算机科学中的地位至关重要,相当于程序设计的灵魂,现在很多大公司都会考察面试者的算法掌握程度

2.目录

目录

3.算法的相关介绍

3.1.算法的特征

1.又穷性:有限个步骤后终止
2.确切性:有确切的含义
3.输入项:有0个或多个输入
4.输出项:有一个或多个输出
5.可行性:可以在有限时间内完成

3.2.算法的评定

1.时间复杂度:执行算法所需要的计算的工作量,问题规模n的函数f(n)的时间复杂度记做O(f(n))
2.空间复杂度:执行算法所需要消耗的内存空间
3.正确性:算法优劣的最重要标准
4.可读性:阅读,理解的容易程度
5.健壮性:对不合理数据输入的反应和处理能力

3.3.算法设计的方法

1.递推法:按照一定的规律来计算序列中的每个项
2.递归法:函数直接或间接的调用自身,必须有一个递归结束条件
3.穷举法:列出它所有的可能情况,尝试所有情况暴力破解
4.贪心算法:分级处理,每一步贪心选择获取局部最优解,最后得到文字最优解
5.分治法:把问题分成两个或更多相同或类似的子问题,直到子问题可以被简单求解,原问题即为子问题解的合并
6.动态规划:用于求解包含重叠子问题的最优化问题,思想为将原问题分解为相似的子问题,然后通过子问题求出原问题的解
7.迭代法:每次执行重复的指令,将变量的原值推导出新值,常见的如二分法
8.分支界限法:把全部的可行解不断分割为越来越小的子集,为每个子集内的解的值计算上界和下界,不在范围内的不再拆分,直至找到可行解,该可行解不大于任何自己的界限
9.回溯法:按照深度优先搜索的策略,从根开始探索,判断各探索结点相关问题的解,不包含则回溯到根,直至搜索出问题的最优解

3.4.八大排序算法

3.4.1.冒泡排序

冒泡排序(Bubble Sort):重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来,直到没有再需要交换,也就是说该数列已经排序完成.

/**
 * 冒泡排序,时间复杂度为O(n^2)
 * @param arr 排序数组
 */
public static void bobbleSort(int[] arr) {
    // 是否进行过交换,默认为false
    boolean flag = false;
    // 需要遍历的次数
    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]) {
                flag = true;
                int temp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = temp;
            }
        }
        // 如果flag==false,表示没有进行过交换,直接退出即可(优化)
        // 即已经排好了序
        if (!flag) {
            break;
        } else {
            // 要将flag重置,进行下一次的判断
            flag = false;
        }
    }
    System.out.println(Arrays.toString(arr));
}

3.4.2.选择排序

选择排序(Selection Sort):从待排序的数据元素中取出最小(大)的存放在起始位置,再从剩余的数据元素中取出最小(大)放在其后,依次进行,直至全部排序

/**
 * 选择排序 时间复杂度O(n^2)
 * @param arr 排序数组
 */
public static void selectSort(int[] arr) {
    for (int i = 0; i < arr.length-1; i++) {
        // 记录最小值的下标,从0开始
        int minIndex = i;
        // 记录最小值,假设是a[0]开始
        int minValue = arr[i];
        // 从i后开始循环
        for (int j = i+1; j < arr.length; j++) {
            // 如果最小的值,并不是a[i],重置minIndex和minValue
            if (minValue > arr[j]){
                // 获取最小值,和最小值的下标
                minValue = arr[j];
                minIndex = j;
            }
        }
        // 将最小的值放在a[i],比较并进行交换
        if (minIndex != i) {
            // 把a[0]第一个值先放在a[minIndex]处
            arr[minIndex] = arr[i];
            // 把保存下来的最小值回填到a[0],即找到了全局的最小值
            arr[i] = minValue;
        }
    }
    System.out.println(Arrays.toString(arr));
}

3.4.3.插入排序

插入排序(Insertion Sort):每次从待排序元素中取出第一个元素,将它与前面的有序元素作比较,插入合适的位置,至成为有序表

    /**
     * 插入排序
     * @param array 带排序数组
     */
    public static void insertSort(int[] array) {
        for (int i = 1; i < array.length; i++) {
            // 定义待插入的数 从第二个数开始和第一个数进行比较
            int insertValue = array[I];
            // 第一个数的下标
            int insertIndex = i -1;
            //与前面的数作比较
            while (insertIndex >= 0 && insertValue < array[insertIndex]) {
                array[insertIndex+1] = array[insertIndex];
                insertIndex--;
            }
            // 将新数据插入
            if (insertIndex + 1 != i){
                array[insertIndex + 1] = insertValue;
            }
        }
        System.out.println(Arrays.toString(array));
    }

3.4.4.希尔排序

希尔排序(Shell's Sort):优化的插入排序,防止插入值过小,导致数组频繁移动效率低下.它将数据按下标进行增量分组,然后对每组进行插入排序,然后减小增量,再次排序,直至为1,排序完成(先分组排序,将每组的小值移动到前面,防止后面排序频繁移动)

希尔排序.png
/**
 * 希尔排序
 * @param arr 排序数组
 */
public static void shellSort(int[] arr){

    for (int step = arr.length / 2; step > 0 ; step = step / 2) {
        // 每次缩小遍历次数增量 每次折半,缩小增量
        for (int i = step; i < arr.length; i++) {
            // 从step个位置,逐步对其所在的元素进行插入排序
            // 假定最小值是arr[i]
            int minValue = arr[i];
            // 记录i
            int minIndex = i;
            //1.minIdx-gap >=0 确保插入的位置不会越界
            //2.minVal < arr[minIdx-gap] 找到了待插入的数
            while (minIndex - step >= 0 && minValue < arr[minIndex - step]){
                // 同插入排序
                arr[minIndex] = arr[minIndex - step];
                minIndex = minIndex - step;
            }
            arr[minIndex] = minValue;
        }
    }
    System.out.println(Arrays.toString(arr));
}

3.4.5.快速排序

快速排序(Quick Sort):通过一趟排序将要排序的数据分割成独立的两个部分,其中一部分的数据比另一部分的数据都要小,然后在按照此方法对这两部分数据进行快速排序,整个排序通过递归实现,以此达到整个数据排列有序(适用于数据量较大且线性结构数据)

/**
 * 快速排序
 * @param arr 排序数组
 * @param left 数组的开始索引
 * @param right 数组的结束索引
 */
public static void quickSort(int[] arr,int left,int right) {
    int start = left;
    int end = right;
    //排序的基点
    int pivot = arr[(left + right) / 2];
    //左右两端扫描
    while (start <= end) {
        //从前向后寻找大于基点的值
        while (pivot > arr[start]) start++;
        //从后向前寻找小于基点的值
        while (pivot < arr[end]) end--;
        //交换值进行下一次循环
        if (start <= end) {
            int temp = arr[start];
            arr[start] = arr[end];
            arr[end] = temp;
            start++;
            end--;
        }
    }
    //从左边将剩余的树再排序
    if (end > left) quickSort(arr,left,end);
    //从有边将剩余的树再排序
    if (start < right) quickSort(arr,start,right);
    System.out.println(Arrays.toString(arr));
}

3.4.6.归并排序

归并排序(Merge Sort):该算法采用分治法,先使每个子序列有序,再使有序的子序列合并,得到完全有序的序列(适用于数据量大且有较多重复数据的场景,需要空间较大)

分治法归并排序.png
/**
 * 归并排序
 * @param arr  原数组
 * @param left  左下标
 * @param right 右下标
 * @param temp 临时数组
 */
public static void mergeSort(int[] arr,int left,int right,int[] temp){
    if (left < right){
        int mid =( left + right )/2;
        // 向左递归
        mergeSort(arr,left,mid,temp);
        // 向右递归
        mergeSort(arr,mid+1,right,temp);
        // 合并
        merge(arr,left,mid,right,temp);
    }
}
/**
 * 合并
 * @param arr 需要排序的原始数组
 * @param left 左边有序序列的初始索引
 * @param mid  中间索引
 * @param right 右边有序序列的初始索引
 * @param temp 临时数组
 */
public static void merge(int[] arr,int left,int mid,int right,int[] temp) {
    // 初始化i,左边有序序列的初始索引
    int i = left;
    // 初始化j,右边有序序列的初始索引
    int j = mid + 1;
    // 临时数组的索引
    int t = 0;
    //1.将左右两边的有序数据按照规则填充到temp中,直到左右两边的有序序列有一边处理完为止
    while (i <= mid && j <= right) {
        //如果左边的有序序列当前的元素小于右边有序序列当前的元素
        if (arr[i] <= arr[j]) {
            // 将左边有序序列的元素填充到temp中
            temp[t] = arr[i];
            t = t + 1;
            i = i + 1;
        } else {
            //如果右边的有序序列当前的元素小于左边有序序列当前的元素
            // 将右边有序序列的元素填充到temp中
            temp[t] = arr[j];
            t = t + 1;
            j = j + 1;
        }
    }
    //2.把剩余的数据的一边依次全部填充到temp中
    while (i <=  mid){
        // 左边的有效数据还有剩余就全部填充到temp中
        temp[t] = arr[i];
        t = t + 1;
        i = i + 1;
    }
    while (j <= right){
        // 右边的有效数据还有剩余就全部填充到temp中
        temp[t] = arr[j];
        t = t + 1;
        j = j + 1;
    }
    //3.将temp数组的元素拷贝到arr中,每次都要拷贝
    // t要清空
    t = 0;
    int tempLeft = left;
    while (tempLeft <= right){
        arr[tempLeft] = temp[t];
        t = t + 1;
        tempLeft = tempLeft + 1;
    }
}

3.4.7.基数排序

基数排序(Radix Sort):将所有待比较的数统一为同样的数位长度,数位较短的数前补零.然后从最低位开始,依次进行一次排序.这样从最低位排序一直到最高位排序完成后数列就成了有序序列了.

基数排序.png
/**
 * 基数排序
 * @param arr 待排序数组
 */
public static void radixSort(int[] arr) {
    // 获取最大的数是多少位
    int digit = getMaxDigit(arr);
    for (int i = 0; i < digit; i++) {
        // 执行 digit 次 bucketSort 排序即可
        bucketSort(arr, i);
    }
    System.out.println(Arrays.toString(arr));
}
private static int getMaxDigit(int[] arr) {
    // 默认只有一位
    int digit = 1;
    // 十进制每多一位,代表其值大了10倍
    int base = 10;
    for (int i : arr) {
        while (i >= base) {
            digit ++;
            base = base * 10;
        }
    }
    return digit;
}
private static void bucketSort(int[] arr, int digit) {
    int base = (int) Math.pow(10, digit);
    // init buckets
    ArrayList<ArrayList<Integer>> buckets = new ArrayList<>();
    // 只有0~9这十个数,所以准备十个桶
    for (int i = 0; i < 10; i++) {
        buckets.add(new ArrayList<Integer>());
    }
    // sort
    for (int i : arr) {
        int index = i / base % 10;
        buckets.get(index).add(i);
    }
    // output: copy back to arr
    int index = 0;
    for (ArrayList<Integer> bucket : buckets) {
        for (int i : bucket) {
            arr[index++] = i;
        }
    }
}

3.4.8.堆排序

堆排序(Heap Sort):利用堆这种数据结构所设计的一种排序算法,堆是一个近似完全二叉树的结构,将待排序的序列构造成一个大顶堆.此时整个序列的最大值就是堆顶的根节点,把它和末尾的元素进行交换.此时末尾的元素就成了最大值.然后将剩余n-1个元素重新构造成一个堆.如此反复执行就能到一个有序的序列

排序数调整为最大堆.png
/**
 * 堆排序
 * @param array 待排序数组
 */
public static void heapSort(int[] array){
    int len = array.length;
    //建堆  len/2-1最后一个非叶子节点
    for(int i = len / 2 - 1; i >= 0;i--){
        maxHeapAdjust(array,i,len-1);
    }
    //排序,根节点和最后一个节点交换
    //换完以后,取走根,重新建堆
    //len-1 最后一个节点
    for(int i=len-1;i>0;i--){
        int temp=array[0];
        array[0]=array[I];
        array[i]=temp;
        maxHeapAdjust(array,0,i-1);
    }
    System.out.println(Arrays.toString(array));
}
/**
 * 堆调整
 * @param array 待排序数组
 * @param start 起始点
 * @param end 结束点
 */
private static void maxHeapAdjust(int array[],int start,int end){
    //父亲的位置
    int father = start;
    //儿子的位置
    int son = father * 2 + 1;
    //如果子节点下标在可以调整的范围内就一直调整下去
    while(son <= end){
        //如果没有右孩子就不用比,有的话,比较两个儿子,选择最大的出来
        if(son + 1 <= end && array[son] < array[son+1]){
            son++;
        }
        //和父节点比大小
        if(array[father] > array[son]){
            return;
        } else {//父亲比儿子小,就要对整个子树进行调整
            int temp = array[son];
            array[son] = array[father];
            array[father] = temp;
            //递归下一层
            father = son;
            son = father * 2 + 1;
        }
    }
}

3.5.其它算法

3.5.1.KMP算法

KMP算法:改进的字符串匹配算法,利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。

  • 一个序列A任意删除若干个字符得到新序列B,则A叫做B的子序列
  • 最长公共子序列(Longest Common Subsequence,lcs):两个序列X和Y的公共子序列中,长度最长的那个,定义为X和Y的最长公共子序列
  • 应用于DNA对比(亲自鉴定),非法数据过滤(不良言论,岛国爱情动作片)

3.6.2.加密算法
3.6.2.1.SHA-1算法

安全哈希算法(Secure Hash Algorithm SHA-1):是一个用来进行数字签名的算法,对于长度小于2^64位的消息,SHA1会产生一个160位的消息摘要,这个消息摘要可以用来验证数据的完整性

  • android中会通过签名的sha1来绑定三方服务

3.6.2.2.RSA算法

RSA公钥加密算法:是1977年由罗纳德·李维斯特(Ron Rivest),阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)一起提出的.1987年7月首次在美国公布,当时他们三人都在麻省理工学院工作实习.RSA就是他们三人姓氏开头字母拼在一起组成的

  • RSA属于应用最为广泛的非对称加密算法,其基本安全原理是建立在大素数因子很难分解的基础上,属于分组密码体制.

3.6.2.3.AES算法

AES技术是一种对称的分组加密技术,使用128位分组加密数据,提供比WEP/TKIPS的RC4算法更高的加密强度

  • DES算法的替代者,最流行的加密算法之一
  • 是一种区块加密技术,把原文分成128位每块的数据,对每块单独进行加密
  • 实际中,一般是通过RSA加密AES的密钥,传输到接收方,接收方解密得到AES密钥,然后发送方和接收方用AES密钥来通信

4.总结

本章主要是算法的基本知识的讲解,学习了常用的八大排序算法,也了解了部分在计算机科学中使用的高级算法.算法是一个十分复杂的技术,一般只需要了解基础的算法及其原理,感兴趣的话可以做些深入的研究


八大排序算法地址:github


相关文章

网友评论

      本文标题:android知识巩固(算法)

      本文链接:https://www.haomeiwen.com/subject/zdvznhtx.html