常用的排序算法介绍和在JAVA的实现及源码分析(一)

一、写随笔的原因:排序比较常用,借此文介绍下排序常用的算法及实现,借此来MARK一下,方便以后的复习。(本人总是忘得比较快)

二、具体的内容:

1.插入排序

插入排序:在前面已经排好序的序列中找到合适的插入位置。

插入排序又可细分为:直接插入排序,二分法插入排序,希尔排序

(1)直接插入排序:简单来说,就是在每一步将一个待排序的数据,从后向前找到合适的位置插入,知道全部数据插入完,排序结束。(从第二个数开始,第一个数插入时没有其他数据,所以直接不用管)

Java代码实现如下:

    /*
    直接插入排序
    */
    public static int[] directInsertSort(int[] a) {
        // 实现从小到大排序
        for (int i = 1; i < a.length; i++) {
            int temp = a[i]; // 存入当前待排序的数据
            int j;
            for (j = i - 1; j >= 0; j--) { // 从后向前依次比较,插入到合适的位置
                // (a[j]大于temp往后移动一位)
                if (temp < a[j]) {
                    a[j + 1] = a[j];
                } else {
                    break;  // a[j]不大于temp,则找到了合适的位置,直接退出循环
                }
            }
            a[j + 1] = temp; // 把当前待排序的数据放入到找的位置(因为是从大到小所以放到j的后面)
        }
        return a;
    }

(2)二分法插入排序:思想都是插入排序的思想,只是在查找合适位置时是通过二分法来找位置的。

下面有个图可以很好的解释(盗图的):

Java代码实现如下:

    /*
    二分法插入排序
     */
    public static void binaryInsertSort(int[] a) {
        for (int i = 0; i < a.length; i++) {
            int temp = a[i];  // 存入当前待排序的数据
            int low = 0;  //low的下标
            int high = i - 1; //high的下标
            int mid = 0;  //mid下标
            while (low <= high) {
                mid = (low + high) / 2;  //算出mid位置
                if (temp < a[mid]) {   // temp小于mid位置的值,则继续与左边部分的位置使用二分
                    high = mid - 1;  // 这时候的右下标需要修改为mid前一位
                } else {       // temp不小于mid位置的值,则继续与右边边部分的位置使用二分
                    low = mid + 1;   // 这时候的右下标需要修改为mid后一位
                }
            }
            // 找到位置后需要将原先位置上及后面的的数据向后平移一位,腾出位置插入
            for (int j = i - 1; j >= low; j--) {
                a[j + 1] = a[j];
            }
            if (low != i) {  //low == i,则不需要变化,本来的位置就是正确的
                a[low] = temp;
            }
        }
    }

(3)希尔排序:先取一个小于n的整数d1作为第一个增量,把文件的全部记录分成d1个组。所有距离为d1的倍数的记录放在同一个组中。先在各组内进行直接插入排序;然后,取第二个增量d2,直重复上述分组和排序操作;至di=1,即所有记录放进一个组中排序为止。

Java代码实现如下:

    public static void shellSort(int[] a) {
        int d = a.length;
        // 不断缩小d,直至d=1
        while (true) {
            d = d -1;
            for (int x = 0; x < d; x++) {
                for (int i = x + d; i < a.length; i = i + d) {
                    int temp = a[i];
                    int j;
                    // 相当于把直接插入排序的1变成了d
                    for (j = i - d; j >= 0 && a[j] > temp; j = j - d) {
                        a[j + d] = a[j];
                    }
                    a[j + d] = temp;
                }
            }
            if (d == 1) {
                break;
            }
        }
    }

2.选择排序

选择排序:在剩余的待排序记录序列中找到最小数据。

选择排序又可细分为:直接选择排序,堆排序。

(1)直接选择排序:在要排序的一组数中,选出最小的一个数与第一个位置的数交换;然后在剩下的数当中再找最小的与第二个位置的数交换,如此循环到倒数第二个数和最后一个数比较为止。

如下图:

Java代码实现如下:

    /*
    直接选择排序
    */
    public static void directSelectSort(int[] a) {
        for (int i = 0; i < a.length; i++) {
            int min = a[i];
            int n = i; // 最小数的索引
            for (int j = i + 1; j < a.length; j++) {
                if (a[j] < min) { // 找出最小的
                    min = a[j];
                    n = j;
                }
            }
            // 最小的一个数与i位置的数交换
            a[n] = a[i]; // n为最小的一个数的索引
            a[i] = min;
        }
    }

(2)堆排序:堆排序是利用堆这种数据结构而设计的一种排序算法,堆排序是一种选择排序,它的最坏,最好,平均时间复杂度均为O(nlogn),它也是不稳定排序。

  首先简单了解下堆结构:堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。如下图:

同时,我们对堆中的结点按层进行编号,将这种逻辑结构映射到数组中就是下面这个样子

 我们用简单的公式来描述一下堆的定义就是:

大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]  

小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]

堆排序的基本思想是:

1.将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。

2.将其与末尾元素进行交换,此时末尾就为最大值。

3.然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了。

代码实现如下:

    /*
    堆排序
    */
    public static void heapSort (int[] arr) {
        //1.构建大顶堆
        for(int i=arr.length/2-1;i>=0;i--){
            //从第一个非叶子结点从下至上,从右至左调整结构
            adjustHeap(arr,i,arr.length);
        }
        //2.调整堆结构+交换堆顶元素与末尾元素
        for(int j=arr.length-1;j>0;j--){
            swap(arr,0,j);//将堆顶元素与末尾元素进行交换
            adjustHeap(arr,0,j);//重新对堆进行调整
        }
    }
    /**
     * 调整大顶堆(仅是调整过程,建立在大顶堆已构建的基础上)
     * @param arr
     * @param i
     * @param length
     */
    public static void adjustHeap(int []arr,int i,int length){
        int temp = arr[i];//先取出当前元素i
        for(int k=i*2+1;k<length;k=k*2+1){//从i结点的左子结点开始,也就是2i+1处开始
            if(k+1<length && arr[k]<arr[k+1]){//如果左子结点小于右子结点,k指向右子结点
                k++;
            }
            if(arr[k] >temp){//如果子节点大于父节点,将子节点值赋给父节点(不用进行交换)
                arr[i] = arr[k];
                i = k;
            }else{
                break;
            }
        }
        arr[i] = temp;//将temp值放到最终的位置
    }

    /**
     * 交换元素
     * @param arr
     * @param a
     * @param b
     */
    public static void swap(int []arr,int a ,int b){
        int temp=arr[a];
        arr[a] = arr[b];
        arr[b] = temp;
    }

三、总结:

  本文主要是对插入排序和选择排序进行了讲解,并通过代码来实现。还有一些像交换排序,归并排序,基数排序等就作为第二部分内容在下次在写吧。

猜你喜欢

转载自www.cnblogs.com/black-fact/p/10965048.html