排序算法

初级排序算法:

选择排序  从数组中选择最小元素,将它与数组的第一个元素交换位置。再从数组剩下的元素中选择出最小的元素,将它与数组的第二个元素交换位置。不断进行这样的操作,直到将整个数组排序。
    特点:时间与输入数组的性质无关(已经很慢了),就算排好了也全走一遍
       数据移动少,每次移动俩。

public class Selection<T extends Comparable<T>> extends Sort<T> {

    @Override
    public void sort(T[] nums) {
        int N = nums.length;
        for (int i = 0; i < N - 1; i++) {
            int min = i;
            for (int j = i + 1; j < N; j++) {
                if (less(nums[j], nums[min])) {
                    min = j;
                }
            }
            swap(nums, i, min);
        }
    }
}

冒泡排序  从左到右不断交换相邻逆序的元素,在一轮的循环之后,可以让未排序的最大元素上浮到右侧。在一轮循环中,如果没有发生交换,那么说明数组已经是有序的,此时可以直接退出。

public class Bubble<T extends Comparable<T>> extends Sort<T> {

    @Override
    public void sort(T[] nums) {
        int N = nums.length;
        boolean isSorted = false;
        for (int i = N - 1; i > 0 && !isSorted; i--) {
            isSorted = true;
            for (int j = 0; j < i; j++) {
                if (less(nums[j + 1], nums[j])) {
                    isSorted = false;
                    swap(nums, j, j + 1);
                }
            }
        }
    }
}

插入排序  从a[0]开始,与它前面的元素做比较,满足小于等于则做交换(将当前元素插入到左侧已经排序的数组),使得插入之后左侧数组依然有序。
    特点:对部分有序的数组性能优越
       适合小规模数组

public class Insertion<T extends Comparable<T>> extends Sort<T> {

    @Override
    public void sort(T[] nums) {
        int N = nums.length;
        for (int i = 1; i < N; i++) {
            for (int j = i; j > 0 && less(nums[j], nums[j - 1]); j--) {
                swap(nums, j, j - 1);
            }
        }
    }
}

  

希尔排序  将h间隔内的数进行插入排序(跨着排),逐步减小h。其中h初始化为1,4,13,40(3*h+1)且满足N/3的最大值。每次h/3
    特点:数组越大,优势越明显。
       代码量小
       无需额外空间
      实际上是优化了的插入排序,减小低效交换次数加快速度

public class Shell<T extends Comparable<T>> extends Sort<T> {

    @Override
    public void sort(T[] nums) {

        int N = nums.length;
        int h = 1;

        while (h < N / 3) {
            h = 3 * h + 1; // 1, 4, 13, 40, ...
        }

        while (h >= 1) {
            for (int i = h; i < N; i++) {
                for (int j = i; j >= h && less(nums[j], nums[j - h]); j -= h) {
                    swap(nums, j, j - h);
                }
            }
            h = h / 3;
        }
    }
}

 

  

 

归并排序  分为自顶向下与自底向上使用归并方法

public abstract class MergeSort<T extends Comparable<T>> extends Sort<T> {

    protected T[] aux;


    protected void merge(T[] nums, int l, int m, int h) {

        int i = l, j = m + 1;

        for (int k = l; k <= h; k++) {
            aux[k] = nums[k]; // 将数据复制到辅助数组
        }

        for (int k = l; k <= h; k++) {
            if (i > m) {
                nums[k] = aux[j++];

            } else if (j > h) {
                nums[k] = aux[i++];

            } else if (aux[i].compareTo(aux[j]) <= 0) {
                nums[k] = aux[i++]; // 先进行这一步,保证稳定性

            } else {
                nums[k] = aux[j++];
            }
        }
    }
}

      自顶向下:递归分成小段排序,归并

public class Up2DownMergeSort<T extends Comparable<T>> extends MergeSort<T> {

    @Override
    public void sort(T[] nums) {
        aux = (T[]) new Comparable[nums.length];
        sort(nums, 0, nums.length - 1);
    }

    private void sort(T[] nums, int l, int h) {
        if (h <= l) {
            return;
        }
        int mid = l + (h - l) / 2;
        sort(nums, l, mid);
        sort(nums, mid + 1, h);
        merge(nums, l, mid, h);
    }
}

      自底向上:先遍历,两两归并(最小的归并就是排序),子数组大小每次加倍。(不需要递归,代码量较少)

public class Down2UpMergeSort<T extends Comparable<T>> extends MergeSort<T> {

    @Override
    public void sort(T[] nums) {

        int N = nums.length;
        aux = (T[]) new Comparable[N];

        for (int sz = 1; sz < N; sz += sz) {
            for (int lo = 0; lo < N - sz; lo += sz + sz) {
                merge(nums, lo, lo + sz - 1, Math.min(lo + sz + sz - 1, N - 1));
            }
        }
    }
}

    特点:需要的额外空间与待排序数组大小N成正比(空间复杂度不佳)
       最差情形与一般情形时间复杂度相同:都为NlgN,在基于比较排序的算法中渐进最优。

堆排序   应用优先队列对元素排序,建堆并且不断调整堆,不断取下最大值调整(递归)
    特点:同时最好地利用时间空间的排序算法
       代码简洁
       无法使用缓存

快速排序  将待排的数组分为两个子数组,其中左子数组全小于中间元素,右子数组全大于中间元素。两个子数组分别排序
    特点:内循环简洁,不在内循环中移动数据,速度快
       比较次数较少,代码量少
       问题较多,较为脆弱
       偏爱随机性,排前打乱数组
      改进版快排----三向切分(适用于待排序的数组中存在大量重复数据,开销由线性对数降低到线性),三个指针分三部分。
  

       

posted @ 2019-04-29 14:25  lvoooop  阅读(152)  评论(0编辑  收藏  举报