1. 程式人生 > >演算法導論 8-3思考題 之變長資料項的排序

演算法導論 8-3思考題 之變長資料項的排序

題目

給定一個整數陣列,其中不同的整數所包含的數字的位數可能不同,但該陣列中,所有整數中包含的總數字為n。設計一個演算法,使其可以在O(n)時間內對該陣列進行排序。

演算法思路

首先,我們可以用計數排序按數字的位數進行排序。然後,用基數排序來對每組數位相同的數字進行排序。

每個數字的數位在1到n之間,令i為數字的位數,mi為位數為i的數字的個數。由於一共n位數,So,我們有如下公式:

i=1nimi=n

Java程式碼實現

package hanxl.insist.eightchapter;

import java.util.Arrays;

public
class Exercise_8_3 { public static void main(String[] args) { int[] a = { 133, 2, 433, 124, 3432434, 2322, 2345, 1, 231, 12, 45, 84, 21, 3457, 132356, 12, 5, 67773, 233 }; System.out.println(Arrays.toString(Exercise_8_3.sort(a))); } public static int[] sort(int[] a) { int
maximumDigit = getMaximumDigit(a); // θ(n) int[] count = new int[maximumDigit]; int[] countingSort = countingSort(a, count); // θ(n) int lo = 0; int hi = 0; for (int i = 0; i < count.length; i++) { int nums = count[i]; if (nums > 1) { lo = hi; hi += nums; radixSort(countingSort, lo, hi, i + 1
); } } return countingSort; } /** * 基數排序 * * @param a * 待排序的陣列 * @param d * 陣列中元素的位數 */ public static void radixSort(int[] a, int lo, int hi, int d) { int[] countArray = new int[10]; // 記錄每個基數的數量 int[][] kindArray = new int[10][hi]; // 把陣列a中的元素按照基數歸類,一維的含義是0-9個數字,二維的含義是含有一維基數的a中的元素 int n = 1; while (d-- > 0) { int k = lo; for (int i = lo; i < hi; i++) { int radix = (a[i] / n) % 10; // n的作用在這體現出來,每一輪while迴圈,n就會擴大10倍 kindArray[radix][countArray[radix]] = a[i]; countArray[radix]++; } // 將每個基數排完序後的結果重新放入a陣列中 for (int i = 0; i < kindArray.length; i++) { for (int j = 0; j < countArray[i]; j++) { a[k] = kindArray[i][j]; k++; } countArray[i] = 0; // 為了其它的基數計算,計數陣列必須清0 } n *= 10; } } /** * 按照數字的位數進行計數排序 * * @param a * @param count * @param nums * @return */ private static int[] countingSort(int[] a, int[] count) { int[] sortedArr = new int[a.length]; for (int i = 0; i < a.length; i++) { // θ(n) 和getMaximumDigit方法同理 int currentDigit = getNumDigit(a[i]) - 1; // 由於陣列下標從0開始,所以下標為0的代表1位數字,下標為1的代表2位數字...... count[currentDigit]++; } int[] nums = count.clone(); // 為了保留count陣列中的元素不被破壞 θ(count.length) count.length <= n for (int i = 1; i < nums.length; i++) //θ(count.length) nums[i] += nums[i - 1]; for (int i = a.length - 1; i >= 0; i--) { // θ(n) 和getMaximumDigit方法同理 int currentDigit = getNumDigit(a[i]) - 1; // 由於陣列下標從0開始,所以下標為0的代表1位數字,下標為1的代表2位數字...... sortedArr[nums[currentDigit] - 1] = a[i]; nums[currentDigit]--; } return sortedArr; } /** * 獲取給定陣列中所有元素中的最大位數 */ public static int getMaximumDigit(int[] a) { int largest = getNumDigit(a[0]); for (int i = 1; i < a.length; i++) { int currentDigit = getNumDigit(a[i]); if (currentDigit > largest) largest = currentDigit; } return largest; } /** * 獲取整數的位數 */ private static int getNumDigit(int num) { int digit = 0; while (num > 0) { digit++; num /= 10; } return digit; } }

演算法時間複雜度分析

getMaximumDigit方法雖然for迴圈中巢狀著一個while迴圈,但是這個子程式的目的是求出陣列中所有元素中的最大位數。也就是說它會遍歷每個數字的每一位,由於總位數為n,所以演算法的時間複雜度為θ(n)。countingSort這個子程式為的時間複雜度為O(n)。接著下面的for迴圈和radixSort方法的目的是排序具有相同位數的數字,那麼它也會遍歷陣列中每一位元素的所有數位,所以它的時間複雜度為O(n)。

所以,演算法的總時間複雜度為O(n)。