《算法导论的Java实现》 9 线性时间排序

9 线性时间排序

 

9.2 计数排序


伪代码:

COUNTING-SORT(A, B, k)
 1  for i ← 0 to k
 2     do C[i] ← 0
 3  for j ← 1 to length[A]
 4     do C[A[j]] ← C[A[j]] + 1
 5  ▹ C[i] now contains the number of elements equal to i.
 6  for i ← 1 to k
 7     do C[i] ← C[i] + C[i - 1]
 8  ▹ C[i] now contains the number of elements less than or equal to i.
 9  for j ← length[A] downto 1
10     do B[C[A[j]]] ← A[j]
11        C[A[j]] ← C[A[j]] - 1

 

 

Java代码:

public class CountingSort {

	public static int[] countingSort(int[] A, int k) {
		int[] B = new int[A.length];
		int[] C = new int[k];

		for (int i = 0; i < k; i++)
			C[i] = 0;
		for (int i = 0; i < A.length; i++)
			C[A[i]] += 1;
		for (int i = 1; i < k; i++)
			C[i] += C[i - 1];
		for (int i = A.length - 1; i >= 0; i--) {
			B[C[A[i]] - 1] = A[i];
			C[A[i]] -= 1;
		}
		return B;
	}

	public static void main(String[] args) {
		int[] A = { 2, 5, 3, 0, 2, 3, 0, 3 };
		int[] B = countingSort(A, 6);
		for (int b : B) {
			System.out.print(b + " ");
		}
		System.out.println();
	}

}

 

 

输出:
0 0 2 2 3 3 3 5

 

code后感
不同于“比较排序”(插入排序,堆排序,快速排序,都是要“比较”数组的值的,所以都是“比较排序”),计数排序的速度非常快,而且是个“稳定排序”。
程序本身非常干净,一共只是4个循环。
最后一个循环稍微有一点点难解,Java程序的数组下标的起始值和伪代码里面的不太相同。
《算法导论》第一版和第二版在这段伪代码里面的数组下标的起始值本身就不同。幸亏代码本身不难,所以也不会有太大困扰。
建议单步跟踪着来运行Java代码,观察A,B,C数组的值的变化,可以看出A,B,C数组的值和《算法导论》里面的图示是一致的。这样可以更容易的去理解这个算法。

 

 

9.3 基数排序


伪代码:

RADIX-SORT(A, d)
1  for i ← 1 to d
2     do use a stable sort to sort array A on digit i

 

 

Java代码:

public class RadixSort {

	private static int[] countingSort(int[] A, int d) {
		int[] B = new int[A.length];
		int[] C = new int[10];
		for (int i = 0; i < 10; i++)
			C[i] = 0;
		for (int i = 0; i < A.length; i++)
			C[A[i] % ((int) Math.pow(10, d + 1)) / ((int) Math.pow(10, d))] += 1;
		for (int i = 1; i < 10; i++)
			C[i] += C[i - 1];
		for (int i = A.length - 1; i >= 0; i--) {
			B[C[A[i] % ((int) Math.pow(10, d + 1)) / ((int) Math.pow(10, d))] - 1] = A[i];
			C[A[i] % ((int) Math.pow(10, d + 1)) / ((int) Math.pow(10, d))] -= 1;
		}
		return B;
	}

	public static int[] radixSort(int[] A, int d) {
		int[] B = new int[A.length];
		for (int i = 0; i < A.length; i++)
			B[i] = A[i];
		for (int i = 0; i < d; i++) {
			B = countingSort(B, i);
		}
		return B;
	}

	public static void main(String[] args) {
		int[] A = { 12, 25, 13, 30, 32, 43, 20, 3 };
		int[] B = radixSort(A, 2);
		for (int b : B) {
			System.out.print(b + " ");
		}
		System.out.println();

	}

}

 

 

输出:
3 12 13 20 25 30 32 43

 

code后感
伪代码很短,Java代码有点长。
因为第二行伪代码是说:以数组A的第i位为键,用一个稳定排序来排。
迄今为止,《算法导论》里介绍过的稳定排序,只有前一节的计数排序,而那一节的计数排序,显然不能直接用在这里。不得不把前一节的计数排序改写一下。
必须注意的是,这一节的计数排序public static int[] countingSort(int[] A, int d)和前一节的参数(int[] A, int d)虽然相同,但是意义完全不同。前一节的计数排序里,第二参数(int k),是表示数组A可能的值的个数。上一节里的例子,数组A在0到5之间,所以k为6。这一节,考虑排列的是10进制数,所以就在程序里定下为10,不由参数引入,而
(int d)是排序的键值所在的位数,测试程序里,d会做循环,从0到1为止。

 

 

9.4 桶排序


伪代码:

BUCKET-SORT(A)
1  n ← length[A]
2  for i ← 1 to n
3     do insert A[i] into list B[⌊n A[i]⌋]
4  for i ← 0 to n - 1
5     do sort list B[i] with insertion sort
6  concatenate the lists B[0], B[1], . . ., B[n - 1] together in order

 

 

Java代码:

import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;

public class BucketSort {
	public static <T> void insertionSort(T[] t, Comparator<? super T> comparator) {
		for (int j = 1; j < t.length; j++) {
			T key = t[j];
			int i = j - 1;
			while (i > -1 && comparator.compare(t[i], key) > 0) {
				t[i + 1] = t[i];
				i--;
			}
			t[i + 1] = key;
		}
	}

	public static Double[] bucketSort(Double[] A) {
		List<Double>[] B = new List[10];

		for (int i = 0; i < B.length; i++)
			B[i] = new LinkedList<Double>();
		for (Double d : A)
			B[(int) (d * 10)].add(d);

		Double[][] BB = new Double[10][];
		List<Double> BBB = new LinkedList<Double>();
		for (int i = 0; i < B.length; i++) {
			BB[i] = B[i].toArray(new Double[0]);
			insertionSort(BB[i], new Comparator<Double>() {
				public int compare(Double o1, Double o2) {
					return (int) (Math.signum(o1 - o2));
				}
			});
			for (Double d : BB[i])
				BBB.add(d);
		}
		return BBB.toArray(new Double[0]);
	}

	public static void main(String[] args) {
		Double[] doubles = new Double[] { 0.78, 0.17, 0.39, 0.26, 0.72, 0.94,
				0.21, 0.12, 0.23, 0.68 };
		doubles = bucketSort(doubles);
		for (Double d : doubles)
			System.out.print(d + " ");
		System.out.println();
	}

}

 

 

输出:
0.12 0.17 0.21 0.23 0.26 0.39 0.68 0.72 0.78 0.94

 

code后感
和上一节一样,伪代码短,Java代码很长。
原因在于,桶排序的第一步是将各个待排元素装到桶里,第二步就是对各个桶进行插入排序。
插入排序insertionSort,在1.1.1节我已经介绍过,在这里,把1.1.1的程序原封不动,copy过来使用。让忘记插入排序的人,再复习一下。

第9章《线性时间排序》就这么结束了,程序都比较简单,相比堆排序和快速排序,无论计数排序还是基数排序,亦或桶排序,理解伪代码并不困难。但是,都用到了前面的知识,所以温故而知新,复习一下前面的插入排序还是有必要的。

总体来说,插入排序等是最慢的,考虑到一般性,堆排序和快速排序都是不错的选择。
如果只是整数,那看情形使用计数或者基数排序,如果是浮点数,那么可以选择桶排序。

你可能感兴趣的:(java,c,算法,String,list,Class)