常见排序算法浅析

排序算法是面试笔试的一个重点,今天,就让我们把常见的排序算法总结一遍。

  • -

选择排序

思想:遍历一组记录,每次遍历选取一个最大的数(最小的数),来依次替代需要替代的位置(从头开始选择)。

源代码:

/**
     * 选择排序
     * @param a
     * 时间复杂度 n的平方  最好,最差,平均都是
     */
    public static void selectSort(int[] a){
        int min;
        int minNum = 0;
        for(int i = 0;i < a.length;i++){
            min = a[i];
            for(int j = i + 1;j < a.length;j++){
                if(min > a[j]){
                    min = a[j];
                    minNum = j;
                }
            }
            a[minNum] = a[i];
            a[i] = min;
        }
        for(int i = 0;i < a.length;i++){
            System.out.print(a[i] + " ");
        }
        System.out.println();
    }
  • -

插入排序

思想:假设一组数中,第一个数是有序的,其他数是无序的,将后面的数依次插入到前面有序的数中,直到最后。

源代码:

/**
     * 插入排序
     * 事件复杂度为   n的平方,,,最好为n
     */
    public static void insertSort(int[] a){
        for(int i = 1;i < a.length;i++){
            int j = i;
            int num = a[i];
            if(num < a[i-1]){
                while(a[j-1]>num&&j>=1){
                    a[j] = a[j - 1];
                    j--;
            }
            }
            a[j] = num;
        }

        for(int i = 0;i < a.length;i++){
            System.out.print(a[i] + " ");
        }
    }
  • -

冒泡排序

思想:像气泡一样往上升,就是从第一个记录开始,对相邻的记录进行比较,当前面的额数大于后面的数时交换位置,继续比较下一个相邻的,一趟后,最大的数位于最后面,同理第二轮最大的数位于倒数第二个。

源代码:

/**
     * 冒泡排序
     * 时间复杂度
     */
    public static void BubbleSort(int array[]){
        int j = 0;
        for(int i = 0;i < array.length - j;j++){
            if (array.length - j == 0)
                break;
            for(int k = 0;k < array.length - j - 1;k++){
                if(array[k] > array[k+1]){
                    int num = array[k];
                    array[k] = array[k+1];
                    array[k+1] = num;
                }
            }
        }
        for(int i = 0;i < array.length;i++){
            System.out.print(array[i] + " ");
        }
        System.out.println();
    }
  • -

归并排序

思想:利用递归与分治技术将数据划分为越来越小的半子表。先将数组分成长度为1的子序列,然后进行两两归并,得到长度为2或1的子序列,继续两两归并,直到完整。

源代码:

/**
     * 归并排序
     */
    public static void Merge(int array[],int left,int mid,int right){
        int[] L = new int[mid-left+1];
        int[] R = new int[right-mid];
        for(int i = 0,j = left;j <= mid;i++,j++){
            L[i] = array[j];
        }
        for(int i = 0,j = mid + 1;j <= right;i++,j++)
            R[i] = array[j];
        int i=0,j=0,k=left;
        for(;i < L.length && j < R.length;k++){
            if(L[i] > R[j]){
                array[k] = R[j];
                j++;
            }else{
                array[k] = L[i];
                i++;
            }
        }
        if(i < L.length){
            for(;i < L.length;i++){
                array[k] = L[i];
                k++;
            }
        }
        if(j < R.length){
            for(;j < R.length;j++){
                array[k] = R[j];
                k++;
            }
        }
    }
    public static void MergeSort(int array[],int left,int right){
        if(left < right){
            int mid = (right + left)/2;
            MergeSort(array, left, mid);
            MergeSort(array, mid+1, right);
            Merge(array, left, mid, right);
        }
    }

快速排序

思想:顾名思义,快速排序是一种很有效率的算法。采用的思想是“分而治之”,方法是递归。步骤是将一组数据分成两部分,前一部分均比后一部分小,然后继续对前后部分进行快速排序,递归到所有数据有序。
大概步骤:分解—>递归求解—>合并

/**
     * 快速排序
     */
    public static void quickSort(int array[],int left,int right){
        int i,j;
        if(left >= right)
            return;
        i = left;
        j = right;
        int num = array[left];
        while(i < j){
            while(array[j] >= num && i < j){
                j--;
            }
            if(array[j] < num){
                array[i] = array[j];
                i++;
            }
            while(array[i] < num && i < j){
                i++;
            }
            if(array[i] >= num){
                array[j] = array[i];
                j--;
            }
        }
        array[i] = num;
        quickSort(array,left,i-1);
        quickSort(array,i+1,right);
    }

快速排序相关知识点:

  1. 最坏的时间复杂度。当选取的基准关键字,也就是代码中的num,是最大的数或者是最小的数时,此时需要划分(n-1)次区间,此时的时间复杂度为[O(n2)]
  2. 最好的时间复杂度。就是选取的基准关键字左右两边长度相等或者相差1,就是说基准关键字就是中间值,这样时间复杂度为[O(nlogn)]
  3. 平均时间复杂度。快速排序的平均复杂度是[O(nlogn)],虽然最差的时间复杂度为[O(n2)],快速排序的平均性能最好。
  4. 空间复杂度。快速排序需要一个栈辅助进行递归,平均空间复杂度为[O(logn)]
  5. 基准关键字的选取。基准关键字的选取是快速排序性能的体现,所以有不同的基准关键字的选取方式。第一种是三者取一,就是选取最左边、中间、最右边的数进行比较,选取中间值最为基准关键字;第二种是取随机数,就是在取一个随机数作为基准关键字。

希尔排序

思想:先将待排序的数组分成多个子序列,然后对子序列直接进行插入排序,然后当整个待排序序列基本有序后,最后对所有元素进行直接排序。

源代码:

/**
     * 希尔排序
     */
    public static void shellSort(int array[]){
        int length = array.length;
        int h,j;
        for(h = length/2;h > 0;h = h/2){
            for(int i = h;i < length;i++){
                int temp = array[i];
                for(j = i - h;j >= 0;j = j - h){
                    if(temp < array[j]){
                        array[j + h] = array[j];
                    }else{
                        break;
                    }
                }
                array[j + h] = temp;
            }
        }
    }

堆排序

堆是一种特殊的树形结构数据,每个节点都有一个值,通常提到的堆都是指一颗完全二叉树,根节点的值大于或者小于两个节点的值。
思想:对于给定的n个记录,将这些记录看成一颗顺序存储的二叉树,然后将其调整为一个大顶堆,然后将堆的最后一个元素和堆顶的元素交换,继续讲剩余的n-1个记录重新构造堆,直到堆中只剩一个元素。

/**
     * 堆排序
     */
    public static void adjustMinHeap(int[] array,int pos,int len){
        int temp;
        int child;
        for(temp = array[pos];2 * pos + 1 <= len;pos = child){
            child = 2 * pos + 1;
            if(child < len && array[child] > array[child + 1])
                child++;
            if(array[child] < temp)
                array[pos] = array[child];
            else
                break;
        }
        array[pos] = temp;
    }
    public static void myMinHeapSort(int[] array){
        int i;
        int len = array.length;
        for(i = len/2 - 1;i >= 0;i--)
            adjustMinHeap(array,i,len-1);
        for(i = len - 1;i >= 0;i--){
            int temp = array[0];
            array[0] = array[i];
            array[i] = temp;
            adjustMinHeap(array,0,i-1);
        }
    }

总结:

稳定的排序算法:直接插入排序,冒泡排序,归并排序
不稳定的排序算法:希尔排序、快速排序、简单选择排序、堆排序

时间复杂度为[O(n2)]的算法:直接插入排序、冒泡排序、快速排序、简单选择
时间复杂度为[O(nlogn)]的算法为:归并排序、堆排序

空间复杂度为[O(1)]的算法为:简单选择、直接插入、冒泡排序、希尔排序、堆排序
空间复杂度为[O(n)]的算法为:归并排序
空间复杂度为[O(logn)]的算法为:快速排序

初始序列整体或部分有序时,插入和冒泡排序有较高的效率,快速排序效率会下降。
数列小,且要求不稳定,用选择排序效率高,要求稳定用冒泡。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值