• 数据结构排序算法


    数据结构排序算法

    选择排序

    选择排序是一种简单的基于交换的排序。时间复杂度为O(N^2),算法的原理是每一次循环就确定一个坐标排好序的状态。

    public class Main {
        
        private static int aux[];//辅助数组
        
        public static void main(String[] args) {
            int a[] = {9, 8, 7, 6, 3};
            aux = new int[a.length];
            MergeSort(a);
            System.out.println(Arrays.toString(a));
        }
    
        /**
         * 交换
         *
         * @param a 数组
         * @param preindex
         * @param lastindex
         */
        private static void Swap(int a[], int preindex, int lastindex) {
            int temp = a[preindex];
            a[preindex] = a[lastindex];
            a[lastindex] = temp;
        }
        
     /**
         * 选择排序
         *
         * @param a
         */
        public static void SelectionSort(int[] a) {
            int length = a.length;
            for (int i = 0; i < length; i++) {
                //外层循环找到是这个坐标对应的最小值。
                int minIndex = i;
                for (int j = i + 1; j < length; j++) {
                    if (a[j] < a[i]) minIndex = j;
                }
                Swap(a, minIndex, i);
            }
        }
    
    }
    
    

    算法特点:

    • 运行时间与输入无关。也就是一个数组无论有序或者无序,在长度一定的情况下,他们所花费的时间是相同的。
    • 数据移动是最少的。整个算法一共需要N次交换(尽管那个数据已经在所在的位置了,算法依然进行了一次交换)交换的次数与数组的长度是线性的关系,这是其他所有排序算法所不具备的。

    插入排序

    插入排序与打牌时整理牌的过程是一样的。我们拿到属于我们的全部牌后,整理时从左边开始,依次同前一张牌进行比较,如果较小就进行交换,如果前一张牌比这张牌小,那么就停止。因为前面的所有牌都比这张牌小了。

    /**
         * 插入排序
         *
         * @param a
         */
        public static void InsertSort(int a[]) {
            int length = a.length;
            for (int i = 1; i < length; i++) {
                //依次与上一个坐标的数字比较,如果小就互换,如果大就停止此次循环。
                for (int j = i; j > 0 && a[j] < a[j - 1]; j--) {
                    Swap(a, j, j - 1);
                }
            }
        }
    

    算法特点:

    • 从代码实现中就可以看出,“有序”对插入排序影响十分大(因为有停止条件)
    • 当每个元素距离它的最终位置都不远时;
    • 部分有序。

    插入算法的改进

    这是插入算法的另一种实现,《算法》一书中说可以节省时间,但是我还没有认真思考。这种升级的实现确实没有进行交换,但是频繁的数组移位应该也是一笔不小时间开销。PS:《算法》P168,2.1.25

     /**
         * 插入排序的升级版
         * @param a
         */
        public static void InsertUpdateSort(int a[]) {
            int length = a.length;
            for (int i = 1; i < length; i++) {
                int temp = a[i];
                int j;
                for (j = i; j > 0 && temp < a[j - 1]; j--) {
                    a[j] = a[j - 1];
                }
                //说明temp比a[j-1]要大了,那么就是a[j]就是它这次循环的归宿。
                //或者j=0了,触发了另一个终止循环的条件。那么a[j]也是它这次循环的归宿
                a[j] = temp;
            }
        }
    

    希尔排序

    希尔排序是多次的插入排序,该算法的思想是每隔h个为一组,对这个组进行排序。排完之后再对下一个组进行排序。

    如:

    1,5,9;一组。

    2,6,10;二组。

    3,7,11;三组;

    ……

    这都是坐标。

    当每隔h组排完之后,在每隔h/2成一组再排序,一直到h=1变为了插入排序在最后依次排序。

    Q:为什么不直接使用插入排序呢?

    A:一般情况下,希尔排序的要比插入排序更快。

        /**
         * 希尔排序
         *
         * @param a
         */
        public static void ShellsSort(int a[]) {
            int length = a.length;
            int h = length / 2;
            while (h >= 1) {
                //注意,代码最终的效果一样,并不是把一个组内所有的数据都拿出来然后归并排序。
                for (int i = h; i < length; i++) {
                    for (int j = i; j >= h && a[j] < a[j - h]; j -= h) {
                        swap(a, j, j - h);
                    }
                }
                h /= 2;
            }
        }
    

    归并排序

    归并排序是一种基于分治策略的排序方法。它的主要方法是将数组一直对半分组,分到每一组中只有一个数字为止。需要注意的是这些分组并不是一个新的数组,而是使用坐标记录下这个虚拟分组的起止位置。这是数组的“排序”阶段。

    接下来是归并。归并一定是两个相邻的数组才可以归并,而这两个数组之间通过一个坐标进行隔断。例如:low,mid,high。这里的mid就是隔断点。[low,mid]是一个数组,[mid+1,high]是另一个数组。

     /**
         * 归并排序
         * @param a
         */
        public static void MergeSort(int a[]) {
            sort(a, 0, a.length - 1);
        }
    
        /**
         * 分治
         * @param a
         * @param lo
         * @param hi
         */
        private static void sort(int a[], int lo, int hi) {
            if (hi<=lo) return;
            int mid = lo + (hi - lo) / 2;
            sort(a, lo, mid);
            sort(a, mid+1, hi);
            merge(a, lo, mid, hi);
        }
    
        /**
         * 归并操作
         * @param a
         * @param lo
         * @param mid
         * @param hi
         */
        private static void merge(int a[], int lo, int mid, int hi) {
            //在这里的操作,
            int i=lo;
            int j = mid + 1;
            for (int k = lo; k <= hi; k++) {
                aux[k] = a[k];
            }
    
            for (int k = lo; k <= hi; k++) {
                if (i>mid) a[k] = aux[j++];
                else if (j>hi) a[k] = aux[i++];
                else if(aux[j]<aux[i]) a[k] = aux[j++];
                else a[k] = aux[i++];
            }
        }
    

    快速排序

    快速排序是一种基于分治策略的排序,一般认为是最优秀的内部排序方法。

    算法的基本思路是挑选一个点,设其为基点,建立两个指针,右指针然后从数组的右边开始向前依次查询,当碰到一个数小于基点时暂停;左指针从数组的左边开始向后一次查询,当碰到一个数大于基点时暂停,随后交换这两个数。依次进行,直到这两个指针相遇或者右指针跑到了左指针的左面。

    当一次快速排序结束时,基点便位于它最终的位置,即它的左边都是不大于它的数,它的右边都是不小于它的数。随后以基点作为分界线,它的左边数组继续进行快速排序,右边数组也进行快速排序。这些都是在原来数组上以index来进行划分的。最终整个数组都是有序的 。

    一般挑选数组最左边的数作为基点。

      /**
         * 快速排序
         * @param a
         * @param lo
         * @param hi
         */
        private static void QuickSort(int a[], int lo, int hi) {
            if (lo >= hi) return;
            int mid = partionsh(a, lo, hi);
            QuickSort(a, lo, mid);
            QuickSort(a, mid + 1, hi);
        }
    
        /**
         * 筛选出基点
         * @param a
         * @param lo
         * @param hi
         * @return
         */
        private static int partionsh(int[] a, int lo, int hi) {
            int begin = a[lo];
            int i = lo;
            int j = hi;
    
            while (true) {
    
                //貌似必须从数组的右边先开始,如果和下面的代码交换顺序,会导致崩溃。
                while (i < j && a[--j] > begin) {
                    if (j <= lo) break;
                }
                while (i < j && a[++i] < begin) {
                    if (i >= hi - 1) break;
                }
    
                if (i >= j) {
                    break;
                }
                Swap(a, i, j);
            }
            Swap(a, lo, i);
            return i;
        }
    
  • 相关阅读:
    怀才就像怀孕,时间久了才能让人看出来
    程序开发基础学习五(json配置、解析文件,c++篇)
    PHP 代码 加密
    2010年8月22日周日_StylingAndSkinningOverview_6.10
    2010年8月21日周六_GeoprocessingTasks_6.7
    2010年8月19日周四_insideTheAPI_Using Extent_6.4
    2010年8月22日周日_StylingAndSkinningNavigation_6.11
    2010年8月20日周五_UsingGraphic_6.5
    2010年8月18日周三_insideTheAPI_usingMap_6.2
    2010年8月21日周六_RoutingTasks_6.8
  • 原文地址:https://www.cnblogs.com/HyPhoenix/p/13779696.html
Copyright © 2020-2023  润新知