排序算法(java)

1、插入排序

插入排序由 N − 1 N-1 N1 趟排序组成,对于 p = 1 p=1 p=1 N − 1 N-1 N1 趟,插入排序保证位置 0 0 0 p p p 上的元素为已排序状态。

  • 排序思路是,在第 p p p 趟时,将位置 p p p上的元素向左移动,直到在前 p + 1 p+1 p+1 个元素中找到正确位置。
public static void insertionSort(int[] array){
        int j;
        for (int p = 1; p < array.length; p++){
            int tmp = array[p];
            for (j = p; j > 0 && tmp < array[j - 1]; j--) {
                array[j] = array[j - 1];
            }
            array[j] = tmp;
        }
    }

2、希尔排序

  • 又称作为缩减增量排序,通过比较一定间隔的元素来工作,各趟比较所用的距离随着算法的进行而减少,直到比较相邻的元素为最后一趟排序为止。
  • 希尔排序使用一个序列为 h 1 h_{1} h1 h 2 h_{2} h2,…, h t h_{t} ht 叫作增量序列,只要 h 1 = 1 h_{1}=1 h1=1 任何增量序列都是可行的,只不过有些增量序列更好。Shell建议(但是不好)的序列是 h t = ⌊ N / 2 ⌋ h_{t}=⌊N/2⌋ ht=N/2 h k = ⌊ h k + 1 / 2 ⌋ h_{k}=⌊h_{k+ 1}/2⌋ hk=hk+1/2,使用希尔增量的最坏情形是 O ( N 2 ) O(N^2) O(N2)
  • 但希尔增量未必互素,Hibbard 提出了一个增量序列 1 , 3 , 7 , … 2 k − 1 1,3,7,…2^k-1 1372k1。这些增量没有公因子,最坏情形为 O ( N 3 / 2 ) O(N^{3/2}) O(N3/2),但还有更优的增量序列。
  • 一趟 h k h_{k} hk 排序就是对 h k h_{k} hk 个小数组进行插入排序。
public static void shellsort(int[] a){
        int j;
        for (int gap = a.length / 2; gap > 0; gap = gap / 2) {
            for (int i = gap; i < a.length; i++){
                int tmp = a[i];
                for (j = i; j >= gap && tmp < a[j - gap]; j -= gap){
                    a[j] = a[j - gap];
                }
                a[j] = tmp;
            }
        }
    }

3、堆排序

第一步以线性时间建立一个堆,然后通过每次将堆中的最后元素与第一个元素交换,执行 N − 1 N-1 N1次deleteMax操作,每次将堆的大小缩减1并进行下滤,当算法终止时,数组则以排好的顺序包含这些元素。

	public static int leftChild(int i){
        return 2 * i + 1;
    }

    public static void perDown(int[] a, int i, int n){
        int child;
        int tmp;
        for (tmp = a[i]; leftChild(i) < n; i = child) {
            child = leftChild(i);
            // 找出较大的子节点
            if(child != n - 1 && a[child] < a[child + 1]){
                child++;
            }
            // 不断比较,直到tmp移动到了合适的位置
            if(tmp < a[child]){
                a[i] = a[child];
            }else {
                break;
            }
        }
        a[i] = tmp;
    }
    public static void heapsort(int[] a){
        for (int i = a.length / 2 - 1; i >= 0; i--) {
            perDown(a, i, a.length);   /* 从最后一个非叶子节点开始向前bulidheap */
        }
        for (int i = a.length - 1; i > 0; i--){
            swapReference(a, 0 ,i); /* 堆的0号节点放的是最大值,每次选出最大值 */
            perDown(a,0, i);  /* 对i个数重新bulidheap */
        }
    }

    private static void swapReference(int[] a, int i, int j) {
        int tmp = a[i];
        a[i] = a[j];
        a[j] = tmp;
    }

4、归并排序

  • 归并排序采用分治的思想,递归的将前半部分数据和后半部分数据各自归并排序,然后再合并。
  • O ( N l o g N ) O(NlogN) O(NlogN)、空间复杂度 O ( N ) O(N) O(N)、稳定
public static void mergesort(int[] a, int left, int right) {
        if(left >= right) return;
        int mid = left + ((right - left) >> 1);
        mergesort(a, left ,mid);
        mergesort(a, mid + 1, right);
        merge(a, left, mid, right);
    }
    public static void merge(int[] a, int leftPos, int rightPos, int rigthEnd) {
        int[] tmp = new int[rigthEnd - leftPos + 1];
        int q1 = leftPos, q2 = rightPos + 1;
        int index = 0;
        while (q1 <= rightPos && q2 <= rigthEnd) {
            tmp[index++] = a[q1] <= a[q2] ? a[q1++] : a[q2++];
        }
        while (q1 <= rightPos) {
            tmp[index++] = a[q1++];
        }
        while (q2 <= rigthEnd) {
            tmp[index++] = a[q2++];
        }
        for (int i = 0; i < tmp.length; i++) {
            a[leftPos + i] = tmp[i];
        }
    }

5、快速排序

(1)、 随便选取一元素为枢纽元,随后将数组被分为两组组,第一组小于被选项,第二组大于被选项,然后对第一组和第二组各自再排列。
(2)、选取枢纽元时要注意:

  • 一种错误的做法通常,无知的做法是选取第一个为枢纽元
  • 一种安全的做法:随机选取枢纽元,不过随机树的生成开销很大
  • 三数中值分割法:选取左端,右端,中心位置的三数中位数为枢纽元

(3)、对于很小的数组(N<20),快排不如插入排序。

// 元素小于CUTOFF时,使用插入排序
    private static int CUTOFF = 3;
    public static void quicksort(int[] a) {
        quicksort(a, 0, a.length - 1);
    }
    public static void quicksort(int[] a, int left, int right) {
        if (left + CUTOFF <= right) {
            int pivot = median3(a, left, right);
            int i = left, j = right - 1;
            for(;;){
                while (a[++i] < pivot){ }
                while (a[--j] > pivot){ }
                if( i < j){
                    swapReference(a, i ,j);
                }else {
                    break;
                }
            }
            swapReference(a, i ,right - 1);
            quicksort(a, left, i - 1);
            quicksort(a, i + 1, right);
        }else {
            insertionSort(a, left, right);
        }
    }
    public static void insertionSort(int[] array, int left, int right){
        int j;
        for (int p = left + 1; p <= right; p++){
            int tmp = array[p];
            for (j = p; j > 0 && tmp < array[j - 1]; j--) {
                array[j] = array[j - 1];
            }
            array[j] = tmp;
        }
    }
    /*
      三数中值分割法,将中间值放在 right - 1, 最大的在right
     */
    public static int median3(int[] a, int left, int right) {
        int mid = (left + right) / 2;
        if (a[mid] < a[left]) swapReference(a, left, mid);
        if (a[right] < a[left]) swapReference(a, left, right);
        if (a[right] < a[mid]) swapReference(a, mid, right);
        swapReference(a, mid, right - 1);
        return a[right - 1];
    }

参考文献:Data Structures and Algorithm Analysis in Java, Third Edition, Mark Allen Weiss.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值