初级排序

来源:互联网 发布:bat java工程师面试题 编辑:程序博客网 时间:2024/05/17 00:17

介绍

Algorithm part1的总结篇第三篇:伟大的排序(上)初级排序。初级排序大概就是在规模小的情况下比较快的排序和比较容易理解且实用的排序。(total order)

选择排序 selection sort

先实现两个方法作为后面的基础:

private static boolean less(Comparable v, Comparable w){ return v.compareTo(w) < 0; }
private static void exch(Comparable[] a, int i, int j){ Comparable swap = a[i]; a[i] = a[j]; a[j] = swap;}

其中Comparable就是实现了Comparable的数据结构,类似:

public class Date implements Comparable<Date>{ private final int month, day, year; public Date(int m, int d, int y) { month = m; day = d; year = y; } public int compareTo(Date that) { if (this.year < that.year ) return -1; if (this.year > that.year ) return +1; if (this.month < that.month) return -1; if (this.month > that.month) return +1; if (this.day < that.day ) return -1; if (this.day > that.day ) return +1; return 0; }}

返回正数是大于,负数为小于,0为等于,后面的排序都满足这些规则。

定义:

  1. 将变量i从0循环到capacity-1,每一次找到数组最小的数的下标。
  2. 将a[i]与a[min]交换

图示:

这里写图片描述

这里写图片描述

代码实现:

 public static void sort(String[] a) {        int n = a.length;        for (int i = 0; i < n; i++) {            int min = i;            for (int j = i+1; j < n; j++) {                if (less(a[j], a[min])) min = j;            }            exch(a, i, min);        }    }

作为一个逻辑清晰,实现简单的基本排序来说,应该不存在什么难点,容易遗忘的点大概有可以使i为0~n-1而不是0~n,因为最后一次已经完全有序所以不必再排,还有j的初值为i+1,每个数不用和自己比较。

分析:
随机顺序数组
比较:(N– 1) + (N– 2) + … + 1 + 0 ~ N^2 / 2
交换:N

插入排序 insertion sort

定义:
1. 将变量i从0循环到capacity-1,a[i]以左包括a[i]从右到左每个数如果左邻一个数大于自己则进行交换,直到出现一个数小于自己。
这个定义得到结果并不是最优,但胜在定义清晰,作为第一个版本。

图示:

这里写图片描述

这里写图片描述

这是一个保证部分有序的算法,前半部分有序,后半部分无序。这个排序就要难懂不少,尽管与冒泡排序有些相像。最主要的是要明白内层循环在做什么。

代码实现:

public static void sort(Comparable[] a) { int N = a.length; for (int i = 0; i < N; i++) for (int j = i; j > 0; j--) if (less(a[j], a[j-1])) exch(a, j, j-1); else break; }

分析:
随机顺序数组
比较: ~ 1/4 N^2
交换:~ 1/4 N^2
最好情况:完全升序
比较: ~ N-1
交换:0
最坏情况:完全降序
比较: ~ 1/2 N^2
交换:~ 1/2 N^2

这里可以看出插入排序在完全有序时或者进一步说是部分有序是时间消耗较少,所以这是一个在规模大的时候处理子部分有序数组的排序。
进一步地可以证明交换次数等于逆序对的数量。逆序对简单来说就是当i < j且a[i] > a[j]时,a[i]与a[j]是一对逆序对。

这样效率不高,所以进一步改进,主要改进点是增加了哨兵和规避了交换。

代码实现:

    public static void sort(Comparable[] a) {        int n = a.length;        // put smallest element in position to serve as sentinel        int exchanges = 0;        for (int i = n-1; i > 0; i--) {            if (less(a[i], a[i-1])) {                exch(a, i, i-1);                exchanges++;            }        }        if (exchanges == 0) return;        // insertion sort with half-exchanges        for (int i = 2; i < n; i++) {            Comparable v = a[i];            int j = i;            while (less(v, a[j-1])) {                a[j] = a[j-1];                j--;            }            a[j] = v;        }        assert isSorted(a);    }

希尔排序shellsort

希尔排序是插入排序的一个变种,解决了插入排序只能逐个交换导致交换次数过多的问题。如果说插入排序是1-sort ,那么希尔排序就是h-sort。

于是怎么处理这个长度h就有问题了。在此就使用h=3*x+1

图示:

这里写图片描述

这里写图片描述

代码实现:

public static void sort(Comparable[] a) { int N = a.length; int h = 1; while (h < N/3) h = 3*h + 1; // 1, 4, 13, 40, 121, 364, ... while (h >= 1) { // h-sort the array. for (int i = h; i < N; i++) { for (int j = i; j >= h && less(a[j], a[j-h]); j -= h) exch(a, j, j-h); } h = h/3; } }

这里的h=3*x+1<=capacity,h从大到小直到1进行h-sort。

分析:
最坏情况
比较:N^3/2

洗牌排序shuffling

洗牌排序算是排序的反面算法也就是乱序算法,非常实用但却有不少细节要注意。

定义:

  1. 将变量i从0循环到capacity-1,取一个随机数r在0~i包括i。
  2. 交换a[i]和a[r]

图示:

这里写图片描述

代码实现:

public static void shuffle(Object[] a) { int N = a.length; for (int i = 0; i < N; i++) { int r = StdRandom.uniform(i + 1); exch(a, i, r); } }

在我的理解中,洗牌算法是一种等可能的随机,就是每张牌一定能够随机到一次,避免随机进行在部分的数组中。

continuing…

0 0