zoukankan      html  css  js  c++  java
  • 面试中的排序算法总结

    一 快速排序

    快速排序一听名字就觉得很高端,在实际应用当中快速排序确实也是表现最好的排序算法。快速排序虽然高端,但其实其思想是来自冒泡排序,冒泡排序是通过相邻元素的比较和交换把最小的冒泡到最顶端,而快速排序是比较和交换小数和大数,这样一来不仅把小数冒泡到上面同时也把大数沉到下面。

    举个栗子:对5,3,8,6,4这个无序序列进行快速排序,思路是右指针找比基准数小的,左指针找比基准数大的,交换之。

    5,3,8,6,4 用5作为比较的基准,最终会把5小的移动到5的左边,比5大的移动到5的右边。

    5,3,8,6,4 首先设置i,j两个指针分别指向两端,j指针先扫描(思考一下为什么?)4比5小停止。然后i扫描,8比5大停止。交换i,j位置。

    5,3,4,6,8 然后j指针再扫描,这时j扫描4时两指针相遇。停止。然后交换4和基准数。

    4,3,5,6,8 一次划分后达到了左边比5小,右边比5大的目的。之后对左右子序列递归排序,最终得到有序序列。

    上面留下来了一个问题为什么一定要j指针先动呢?首先这也不是绝对的,这取决于基准数的位置,因为在最后两个指针相遇的时候,要交换基准数到相遇的位置。一般选取第一个数作为基准数,那么就是在左边,所以最后相遇的数要和基准数交换,那么相遇的数一定要比基准数小。所以j指针先移动才能先找到比基准数小的数。

    快速排序是不稳定的,其时间平均时间复杂度是O(nlgn)。

    实现代码:

    public class QuickSort {
        //一次划分
        public static int partition(int[] arr, int left, int right) {
            int pivotKey = arr[left];
            int pivotPointer = left;
            
            while(left < right) {
                while(left < right && arr[right] >= pivotKey)
                    right --;
                while(left < right && arr[left] <= pivotKey)
                    left ++;
                swap(arr, left, right); //把大的交换到右边,把小的交换到左边。
            }
            swap(arr, pivotPointer, left); //最后把pivot交换到中间
            return left;
        }
        
        public static void quickSort(int[] arr, int left, int right) {
            if(left >= right)
                return ;
            int pivotPos = partition(arr, left, right);
            quickSort(arr, left, pivotPos-1);
            quickSort(arr, pivotPos+1, right);
        }
        
        public static void sort(int[] arr) {
            if(arr == null || arr.length == 0)
                return ;
            quickSort(arr, 0, arr.length-1);
        }
        
        public static void swap(int[] arr, int left, int right) {
            int temp = arr[left];
            arr[left] = arr[right];
            arr[right] = temp;
        }
        
    }

     二、归并排序

    归并排序是另一种不同的排序方法,因为归并排序使用了递归分治的思想,所以理解起来比较容易。其基本思想是,先递归划分子问题,然后合并结果。把待排序列看成由两个有序的子序列,然后合并两个子序列,然后把子序列看成由两个有序序列。。。。。倒着来看,其实就是先两两合并,然后四四合并。。。最终形成有序序列。空间复杂度为O(n),时间复杂度为O(nlogn)。

    举个栗子:

    public class MergeSort {
    
        public static void mergeSort(int[] arr) {
            mSort(arr, 0, arr.length-1);
        }
    
        /**
         * 递归分治
         * @param arr 待排数组
         * @param left 左指针
         * @param right 右指针
         */
        public static void mSort(int[] arr, int left, int right) {
            if(left >= right)
                return ;
            int mid = (left + right) / 2;
    
            mSort(arr, left, mid); //递归排序左边
            mSort(arr, mid+1, right); //递归排序右边
            merge(arr, left, mid, right); //合并
        }
    
        /**
         * 合并两个有序数组
         * @param arr 待合并数组
         * @param left 左指针
         * @param mid 中间指针
         * @param right 右指针
         */
        public static void merge(int[] arr, int left, int mid, int right) {
            //[left, mid] [mid+1, right]
            int[] temp = new int[right - left + 1]; //中间数组
    
            int i = left;
            int j = mid + 1;
            int k = 0;
            while(i <= mid && j <= right) {
                if(arr[i] <= arr[j]) {
                    temp[k++] = arr[i++];
                }
                else {
                    temp[k++] = arr[j++];
                }
            }
    
            while(i <= mid) {
                temp[k++] = arr[i++];
            }
    
            while(j <= right) {
                temp[k++] = arr[j++];
            }
    
            for(int p=0; p<temp.length; p++) {
                arr[left + p] = temp[p];
            }
    
        }
    
        public static void main(String[] args) {
    
            int[] arr = {3,7,8,2,1,9,6,0};
            mergeSort(arr);
            for (int i: arr)
                System.out.print(i+" ");
    
        }
    }

    三、冒泡排序

    package com.yyh.se.algorithm;
    
    /**
     * 冒泡排序
     * 冒泡排序是最简单的排序之一了,其大体思想就是通过与相邻元素的比较和交换来把小的数交换到最前面。
     * 这个过程类似于水泡向上升一样,因此而得名。举个栗子,对5,3,8,6,4这个无序序列进行冒泡排序。
     * 首先从后向前冒泡,4和6比较,把4交换到前面,序列变成5,3,8,4,6。
     * 同理4和8交换,变成5,3,4,8,6,3和4无需交换。5和3交换,变成3,5,4,8,6,3.这样一次冒泡就完了,
     * 把最小的数3排到最前面了。对剩下的序列依次冒泡就会得到一个有序序列。冒泡排序的时间复杂度为O(n^2)。
     * Created by yuyouhua on 2016/4/24.
     */
    public class BubbleSort extends AbstractSort {
    
        public static void bubbleSort(int[] arr){
            if(arr == null || arr.length == 0)
                return;
            for (int i=0; i<arr.length; i++){
                for (int j=arr.length-1; j>i; j--){
                    if (arr[j]<arr[j-1])
                        swap(arr,j-1,j);
                }
            }
        }
    
        public static void main(String[] args) {
            int[] arr = {3,7,8,2,1,9,6,0};
            bubbleSort(arr);
            for (int i: arr)
                System.out.print(i+" ");
    
        }
    }

    四、选择排序

    package com.yyh.se.algorithm;
    
    /**
     *
     * 选择排序的思想其实和冒泡排序有点类似,都是在一次排序后把最小的元素放到最前面。
     * 但是过程不同,冒泡排序是通过相邻的比较和交换。而选择排序是通过对整体的选择。
     * 举个栗子,对5,3,8,6,4这个无序序列进行简单选择排序,首先要选择5以外的最小数来和5交换,
     * 也就是选择3和5交换,一次排序后就变成了3,5,8,6,4.对剩下的序列一次进行选择和交换,最终就会得到一个有序序列。
     * 其实选择排序可以看成冒泡排序的优化,因为其目的相同,只是选择排序只有在确定了最小数的前提下才进行交换,大大减少了交换的次数。
     * 选择排序的时间复杂度为O(n^2)
     * Created by yuyouhua on 2016/4/24.
     */
    public class SelectSort extends AbstractSort{
    
        public static void selectSort(int[] arr){
            if(arr == null || arr.length == 0)
                return;
            int minIndex = 0;
            for(int i=0; i<arr.length; i++){
    
                minIndex = i;
                for (int j = i+1; j <arr.length ; j++) {
                    if(arr[j]<arr[minIndex])
                        minIndex = j;
                }
    
                if(minIndex != i)
                    swap(arr,i,minIndex);
            }
    
        }
    
        public static void main(String[] args) {
            int[] arr = {3,7,8,2,1,9,6,0};
            selectSort(arr);
            for (int i: arr)
                System.out.print(i+" ");
    
        }
    
    
    }

    五、插入排序

    package com.yyh.se.algorithm;
    
    /**
     *
     * 插入排序不是通过交换位置而是通过比较找到合适的位置插入元素来达到排序的目的的。
     * 相信大家都有过打扑克牌的经历,特别是牌数较大的。在分牌时可能要整理自己的牌,牌多的时候怎么整理呢?
     * 就是拿到一张牌,找到一个合适的位置插入。这个原理其实和插入排序是一样的。
     * 举个栗子,对5,3,8,6,4这个无序序列进行简单插入排序,首先假设第一个数的位置时正确的,
     * 想一下在拿到第一张牌的时候,没必要整理。然后3要插到5前面,把5后移一位,变成3,5,8,6,4.想一下整理牌的时候应该也是这样吧。
     * 然后8不用动,6插在8前面,8后移一位,4插在5前面,从5开始都向后移一位。注意在插入一个数的时候要保证这个数前面的数已经有序。
     * 简单插入排序的时间复杂度也是O(n^2)。
     * Created by yuyouhua on 2016/4/24.
     */
    public class InsertSort extends AbstractSort {
    
        public static void insertSort(int[] arr) {
            if(arr == null || arr.length == 0)
                return;
    
            for (int i = 1; i <arr.length ; i++) {
                int j=i;
                int target = arr[i];
    
                while (j>0 && target<arr[j-1]){
                    arr[j] = arr[j-1];
                    j--;
                }
    
                arr[j] = target;
    
            }
    
    
        }
    
        public static void main(String[] args) {
    
            int[] arr = {3,7,8,2,1,9,6,0};
            insertSort(arr);
            for (int i: arr)
                System.out.print(i+" ");
    
        }
    }
  • 相关阅读:
    P1074 靶形数独
    PYTHON-模块 sys os random shutil-练习
    PYTHON-模块-time&datetime-练习 +目录规范
    PYTHON-模块time&datetime+ 目录规范
    PYTHON-模块定义 搜索路径
    PYTHON-匿名函数,递归与二分法,面向过程编程-练习
    PYTHON-匿名函数,递归与二分法,面向过程编程
    PYTHON-有参装饰器,无参装饰器,语法糖
    PYTHON-迭代器,xxx生成式
    PYTHON-函数对象,嵌套,名称空间与作用域,闭包函数
  • 原文地址:https://www.cnblogs.com/yyh19890221/p/5428713.html
Copyright © 2011-2022 走看看