演算法導論 8-3思考題 之變長資料項的排序
阿新 • • 發佈:2019-02-07
題目
給定一個整數陣列,其中不同的整數所包含的數字的位數可能不同,但該陣列中,所有整數中包含的總數字為n。設計一個演算法,使其可以在O(n)時間內對該陣列進行排序。
演算法思路
首先,我們可以用計數排序按數字的位數進行排序。然後,用基數排序來對每組數位相同的數字進行排序。
每個數字的數位在1到n之間,令i為數字的位數,
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,所以演算法的時間複雜度為
所以,演算法的總時間複雜度為O(n)。