- 排序算法的介紹
- 排序算法的分類
- 算法的時間復(fù)雜度
- 衡量一個程序執(zhí)行時間的兩種方法
- 時間頻度
- 時間復(fù)雜度
- 常見的時間復(fù)雜度
- 平均時間復(fù)雜度和最壞時間復(fù)雜度
- 算法的空間復(fù)雜度
- 基本介紹
- 冒泡排序
- 選擇排序
- 插入排序
- 希爾排序
- 快速排序
- 歸并排序
- 基數(shù)排序
- 常用排序的算法比較
- 相關(guān)術(shù)語解釋
1. 排序算法的介紹
- 排序算法(Sort Algorithm)俭驮,排序是將 一組數(shù)據(jù)厘熟,以 指定的順序進(jìn)行 排列的過程
2. 排序算法的分類
內(nèi)部排序:需要處理的數(shù)據(jù)都加載到 內(nèi)部存儲(內(nèi)存)中進(jìn)行排序
外部排序:數(shù)據(jù)量過大谴仙,無法全部加載到內(nèi)存中,需要借助 外部存儲(文件等) 進(jìn)行排序
常見排序算法分類:
3. 算法的時間復(fù)雜度
3.1 衡量一個程序執(zhí)行時間的兩種方法
事后統(tǒng)計法:這種方法可行掂榔,但是有兩個問題:一個是需要實際運行該程序绩郎,另一個是依賴于計算機的硬件、軟件等環(huán)境因素肉拓。這種方式要在同一臺計算機的相同狀態(tài)運行毫痕,才能比較因宇。
事前估算法:通過分析某個算法的 時間復(fù)雜度 來判斷哪個算法更優(yōu)
3.2 時間頻度
- 時間頻度:一個算法中的語句執(zhí)行次數(shù)稱為語句頻度或時間頻度饲化,記為 T(n)
3.3 時間復(fù)雜度
時間復(fù)雜度:算法中的基本操作語句的重復(fù)執(zhí)行次數(shù)是問題規(guī)模 n 的某個函數(shù) 用 T(n) 表示。若有 某個輔助函數(shù) f(n), 使得當(dāng) n 趨近于無窮大時弥姻, T(n)/f(n)的極限值為不等于零的常數(shù),則稱 f(n) 是 T(n) 的同數(shù)量級函數(shù)。記作 T(n)=O(f(n)), O(f(n))稱為算法的漸進(jìn)時間復(fù)雜度,簡稱時間復(fù)雜度
-
計算時間復(fù)雜度的方法
① 用常數(shù) 1 代替運行時間中的所有加法常數(shù)
② 修改后的運行次數(shù)函數(shù)中判耕,只保留最高階項
③ 去除最高階項的系數(shù)
3.4 常見時間復(fù)雜度
- 常見時間復(fù)雜度從小到大依次為:
O(1)<O(log2n)<O(n)<O(nlog2n)<O(n2)<O(n3)<O(nk)<n(2n)
- 隨著問題規(guī)模 n 不斷增大岖赋,算法的執(zhí)行效率越低疚脐。
3.5 平均時間復(fù)雜度和最壞時間復(fù)雜度
- 平均時間復(fù)雜度:所有可能的輸入實例均以等概率出現(xiàn)的情況下,該算法的運行時間
- 最壞時間復(fù)雜度:一般討論時間復(fù)雜度均是最壞情況下的時間復(fù)雜度。
4. 算法的空間復(fù)雜度
- 空間復(fù)雜度(Space Complexity): 該算法所耗費的存儲空間可岂,它也是問題規(guī)模 n 的函數(shù),是對一個算法在運行過程中臨時占用存儲空間大小的量度。
- 從用戶使用體驗上看柑营,更看重程序的執(zhí)行速度站刑,一些緩存產(chǎn)品和算法 本質(zhì)就是空間換時間
5.冒泡排序
- 基本思想: 依次比較相鄰元素的值中贝,若發(fā)現(xiàn)逆序則交換
- 優(yōu)化:如果一趟比較下來沒有進(jìn)行過交換,就說明序列有序
- 圖解
- 代碼實現(xiàn):
public class BubbleSort {
public static void main(String[] args) {
int arr[] = {3,9,-1,10,20};
// int arr[] = {-1, 3, 9, 10, 20};
//為了容易理解晶丘,把冒泡排序的過程展示一下
System.out.println("排序前");
System.out.println(Arrays.toString(arr));
bubbleSort(arr);
System.out.println("排序后");
System.out.println(Arrays.toString(arr));
/*
//第二趟排序黍氮,就是將第二大的數(shù)排在倒數(shù)第二位
for(int j = 0; j < arr.length - 1 - 1;j++){
if(arr[j] > arr[j+1]){
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
System.out.println("第二趟排序后的數(shù)組");
System.out.println(Arrays.toString(arr));
//第三趟排序,就是將第三大的數(shù)排在倒數(shù)第三位
for(int j = 0; j < arr.length - 1 - 2;j++){
if(arr[j] > arr[j+1]){
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
System.out.println("第三趟排序后的數(shù)組");
System.out.println(Arrays.toString(arr));
//第四趟排序浅浮,就是將第四大的數(shù)排在倒數(shù)第四位
for(int j = 0; j < arr.length - 1 - 3;j++){
if(arr[j] > arr[j+1]){
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
System.out.println("第四趟排序后的數(shù)組");
System.out.println(Arrays.toString(arr));*/
}
//將冒泡排序算法沫浆,封裝成一個方法
public static void bubbleSort(int[] arr){
//冒泡排序的時間復(fù)雜度O(n^2)
int temp = 0; //臨時變量,交換時用
boolean flag = false; //標(biāo)識變量滚秩,表示是否進(jìn)行過交換
for(int i = 0; i < arr.length - 1;i++){
//第一躺排序专执,就是將最大的數(shù)排在最后
for (int j = 0; j < arr.length - 1 - i; j++) {
//如果前面的數(shù)比后面的數(shù)大,則交換
if(arr[j] > arr[j+1]){
flag = true;
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
// System.out.printf("第%d趟排序后的數(shù)組\n",i+1);
// System.out.println(Arrays.toString(arr));
if(!flag){ //在一趟排序中郁油,一次交換都沒有發(fā)生
break;
}else{
flag = false; //重置flag本股,進(jìn)行下次判斷,
}
}
}
}
6. 選擇排序
選擇排序的基本思想:從欲排序的數(shù)據(jù)中桐腌,找到一個最小的拄显,然后與指定位置的值進(jìn)行交換
思路分析圖:
-
思路分析:
選擇排序一共有
數(shù)組大小-1
輪排序-
每 1 輪排序,又是一個循環(huán)案站,循環(huán)的規(guī)則
2.1 先假定當(dāng)前這個數(shù)是
最小數(shù)
2.2 然后與后面的數(shù)比較躬审,如果發(fā)現(xiàn)有更小的數(shù),就重新確定最小數(shù)蟆盐,并得到下標(biāo)
2.3 當(dāng)遍歷到數(shù)組的最后時承边,就得到本輪的最小數(shù)和下標(biāo)
2.4 交換
代碼實現(xiàn)
public class SelectSort {
public static void main(String[] args) {
int[] arr = {101,34,119,1};
System.out.println("排序前");
System.out.println(Arrays.toString(arr));
selectSort(arr);
System.out.println("排序后");
System.out.println("排序后");
System.out.println(Arrays.toString(arr));
}
//選擇排序
public static void selectSort(int[] arr){
//在推到的過程,可以使用循環(huán)
//選擇排序的時間復(fù)雜度是O(n^2)
for(int i = 0; i < arr.length - 1;i++) {
//使用逐步推導(dǎo)的方式石挂,講解選擇排序
//原始數(shù)組:101博助,34,119痹愚,1
//算法 先簡單——》后復(fù)雜 富岳,可以把一個復(fù)雜的算法罗心,拆分成簡單的問題 -》 逐步解決
//第一輪
int minIndex = i;
int min = arr[i];
for (int j = i + 1; j < arr.length; j++) {
if (min > arr[j]) { //說明假定的最小值,并不是最小值
min = arr[j]; //重置min
minIndex = j; //重置minIndex
}
}
//將最小值城瞎,放在 arr[0], 即交換
if (minIndex != i) {
arr[minIndex] = arr[i];
arr[i] = min;
}
// System.out.println("第"+i+1+"輪后,");
// System.out.println(Arrays.toString(arr));
}
/* //第2輪
minIndex = 1;
min = arr[1];
for(int j = 1 + 1; j < arr.length; j++){
if(min > arr[j]){ //說明假定的最小值疾瓮,并不是最小值
min = arr[j]; //重置min
minIndex = j; //重置minIndex
}
}
//將最小值脖镀,放在 arr[1], 即交換
if(minIndex!=1){
arr[minIndex] = arr[1];
arr[1] = min;
}
System.out.println("第2輪后,");
System.out.println(Arrays.toString(arr));
//第3輪
minIndex = 2;
min = arr[2];
for(int j = 2 + 1; j < arr.length; j++){
if(min > arr[j]){ //說明假定的最小值狼电,并不是最小值
min = arr[j]; //重置min
minIndex = j; //重置minIndex
}
}
//將最小值蜒灰,放在 arr[1], 即交換
if(minIndex!=2){
arr[minIndex] = arr[2];
arr[2] = min;
}
System.out.println("第3輪后,");
System.out.println(Arrays.toString(arr));*/
}
}
7.插入排序
插入排序的基本思想: 把n個待排序的元素看成一個有序表和一個無序表肩碟,開始 有序表只包含一個元素强窖,無序表中有 n-1 個元素,排序過程中削祈,每次從無序表中取出第一個元素翅溺,將它插入到有序表中的適當(dāng)位置。
-
思路圖解:
image-20220324230244941.png
- 代碼實現(xiàn)
public class InsertSort {
public static void main(String[] args) {
int[] arr = {101,34,119,1};
insertSort(arr);
}
//插入排序
public static void insertSort(int[] arr){
int insertval = 0;
int insertIndex = 0;
//使用for循環(huán)髓抑,將代碼簡化
for (int i = 1; i < arr.length; i++) {
//定義待插入的數(shù)
insertval = arr[i];
insertIndex = i-1; //即arr[i]的前面這個數(shù)的下標(biāo)
//給 insertVal 找到插入的位置
//說明
//1.insertIndex >= 0 保證在給 insertVal 找插入位置咙崎,不越界
//2. insertIndex < arr[insertIndex] 待插入的數(shù),還沒有找到插入的位置(小于inserIndex位置)
//3. 就需要將 arr[insertIndex] 后移
while(insertIndex >= 0 && insertval < arr[insertIndex]){
arr[insertIndex + 1] = arr[insertIndex]; //值向后移
insertIndex--; //索引減一
}
//當(dāng)退出 while 循環(huán)時吨拍,說明插入的位置找到 insertIndex;
if(insertIndex + 1 != i){
arr[insertIndex + 1] = insertval;
}
System.out.println("第"+i+"輪插入后:");
System.out.println(Arrays.toString(arr));
}
/* //使用逐步推到的方式來講解褪猛,便于理解
//第一輪 {101,34,119,1}; => {34,101,119,1}
//定義待插入的數(shù)
int insertval = arr[1];
int insertIndex = 1-1; //即arr[1]的前面這個數(shù)的下標(biāo)
//給 insertVal 找到插入的位置
//說明
//1.insertIndex >= 0 保證在給 insertVal 找插入位置,不越界
//2. insertIndex < arr[insertIndex] 待插入的數(shù)羹饰,還沒有找到插入的位置
//3. 就需要將 arr[insertIndex] 后移
while(insertIndex >= 0 && insertval < arr[insertIndex]){
arr[insertIndex + 1] = arr[insertIndex]; //
insertIndex--;
}
//當(dāng)退出 while 循環(huán)時伊滋,說明插入的位置找到 insertIndex;
arr[insertIndex + 1] = insertval;
System.out.println("第1輪插入后:");
System.out.println(Arrays.toString(arr));
//第2輪
insertval = arr[2];
insertIndex = 2-1; //即arr[1]的前面這個數(shù)的下標(biāo)
while(insertIndex >= 0 && insertval < arr[insertIndex]){
arr[insertIndex + 1] = arr[insertIndex]; //
insertIndex--;
}
//當(dāng)退出 while 循環(huán)時,說明插入的位置找到 insertIndex;
arr[insertIndex + 1] = insertval;
System.out.println("第2輪插入后:");
System.out.println(Arrays.toString(arr));
//第3輪
insertval = arr[3];
insertIndex = 3-1; //即arr[1]的前面這個數(shù)的下標(biāo)
while(insertIndex >= 0 && insertval < arr[insertIndex]){
arr[insertIndex + 1] = arr[insertIndex]; //
insertIndex--;
}
//當(dāng)退出 while 循環(huán)時队秩,說明插入的位置找到 insertIndex;
arr[insertIndex + 1] = insertval;
System.out.println("第3輪插入后:");
System.out.println(Arrays.toString(arr));*/
}
}
8. 希爾排序(縮小增量排序)
- 基本思想:把記錄按下標(biāo)的一定增量分組笑旺,對每組使用直接插入排序算法排序;隨著增量逐漸減少刹碾,每組包含的關(guān)鍵詞越來越多燥撞。當(dāng)增量減至1,整個文件恰被分成一組迷帜,算法便終止
- 希爾排序的示意圖
交換法代碼實現(xiàn):
//使用逐步推導(dǎo)的方式來編寫希爾排序
//希爾排序時物舒,對有序序列在插入時采用交換法
public static void shellSort(int[] arr){
//使用循環(huán)處理
int temp = 0;
int count = 0;
for(int gap = arr.length/2; gap > 0;gap/=2){
for(int i = gap; i < arr.length;i++){
//遍歷各組中的素有元素(共5組,每組有2個元素)戏锹,步長5
for (int j = i - gap; j >= 0; j-=gap) {
//如果當(dāng)前元素大于加上步長后的那個元素冠胯,交換‘
if(arr[j] > arr[j+gap]){
temp = arr[j];
arr[j] = arr[j+gap];
arr[j+gap] = temp;
}
}
}
System.out.println("希爾排序第"+(++count)+"輪后:"+ Arrays.toString(arr));
}
移位法代碼實現(xiàn):
//對交換式的希爾排序進(jìn)行優(yōu)化 -》 移位法
public static void shellSort2(int[] arr){
//增量 gap,并逐步的縮小增量
for(int gap = arr.length / 2; gap > 0; gap /= 2 ){
//從 gap 個元素锦针,逐個對其的組進(jìn)行直接插入排序
for(int i = gap; i < arr.length; i++){
int j = i;
int temp = arr[j];
if(arr[j] < arr[j - gap]){
while( j - gap >= 0 && temp < arr[j - gap]){
//移動
arr[j] = arr[j - gap];
j -= gap;
}
//當(dāng)退出while后荠察,就給 temp 找到插入的位置
arr[j] = temp;
}
}
System.out.println(Arrays.toString(arr));
}
}
9. 快速排序
基本思想:通過一趟排序?qū)⒁判虻臄?shù)據(jù)分割成獨立的兩部分置蜀,其中一部分的所有數(shù)據(jù)都比另外一部分的所有數(shù)據(jù)都要小,然后再按此方法對著兩部分?jǐn)?shù)據(jù)分別進(jìn)行快速排序悉盆,整個排序過程可以遞歸進(jìn)行盯荤,一次達(dá)到整個數(shù)據(jù)變成有序序列。
示意圖
- 代碼實現(xiàn)
- 代碼1
public class quickSort {
public static void main(String[] args) {
int[] arr = {-9,78,0,23,-567,70};
quickSort(arr,0,arr.length-1);
System.out.println("arr="+ Arrays.toString(arr));
}
public static void quickSort(int[] arr,int left,int right){
int l = left; //左下標(biāo)
int r = right; //右下標(biāo)
int temp = 0; //臨時變量焕盟,交換時使用
//pivot 中軸值
int pivot = arr[(left + right)/2];
//while 循環(huán)的目的秋秤,是讓比pivot 值小的放到左邊
//比 pivot 值放到右邊
while(l < r){ //
//在pivot 的左邊一直找,找到大于等于 pivot 值脚翘,才退出
while( arr[l] < pivot){
l += 1;
}
//在pivot 的右邊一直找灼卢,找到小于等于 pivot 值,才退出
while(arr[r] > pivot){
r -= 1;
}
//如果l >= r 說明 pivot 的左右兩邊的值来农,已經(jīng)按照
// 左邊全部是小于等于pivot 的值鞋真,右邊全部大于等于pivot的值
if(l >= r){
break;
}
//交換
temp = arr[l];
arr[l] = arr[r];
arr[r] = temp;
//如果交換完后,發(fā)現(xiàn)這個 arr[l] == pivot 值相等 r--沃于, 前移
if(arr[l] == pivot){
r -= 1;
}
//如果交換完后涩咖,發(fā)現(xiàn)這個 arr[r] == pivot 值相等 l++, 后移
if(arr[l] == pivot){
l += 1;
}
}
//如果 l == r,bixu l++,r--,否則出現(xiàn)棧溢出
if(l == r){
l += 1;
r -= 1;
}
//向左遞歸
if(left < r){
quickSort(arr,left,r);
}
//向右遞歸
if(right > l){
quickSort(arr,l,right);
}
}
}
- 代碼2
public class QuickSort {
private static void swap(int[] data, int i, int j) {
int temp = data[i];
data[i] = data[j];
data[j] = temp;
}
private static void subSort(int[] data, int start, int end) {
if (start < end) {
int base = data[start];
int low = start;
int high = end + 1;
while (true) {
while (low < end && data[++low] - base <= 0)
;
while (high > start && data[--high] - base >= 0)
;
if (low < high) {
swap(data, low, high);
} else {
break;
}
}
swap(data, start, high);
subSort(data, start, high - 1);//遞歸調(diào)用
subSort(data, high + 1, end);
}
}
public static void quickSort(int[] data){
subSort(data,0,data.length-1);
}
public static void main(String[] args) {
int[] data = { 9, -16, 30, 23, -30, -49, 25, 21, 30 };
System.out.println("排序之前:\n" + java.util.Arrays.toString(data));
quickSort(data);
System.out.println("排序之后:\n" + java.util.Arrays.toString(data));
}
}
10. 歸并排序
- 基本思想:采用經(jīng)典的 分治策略
- 代碼實現(xiàn):
public class MergetSort {
public static void main(String[] args) {
int arr[] = {8,4,5,7,1,3,6,2}; //n -> merge -> n-1
int temp[] = new int[arr.length]; //歸并排序需要一個額外空間
mergeSort(arr,0,arr.length-1,temp);
System.out.println("歸并排序后:"+ Arrays.toString(arr));
}
/**
* 分+合的方法
* @param arr
* @param left
* @param right
* @param temp
*/
public static void mergeSort(int[] arr,int left,int right,int[] temp){
if(left < right){
int mid = (left + right)/2; //中間索引
//向左遞歸進(jìn)行分解
mergeSort(arr,left,mid,temp);
//向右遞歸進(jìn)行分解
mergeSort(arr,mid+1,right,temp);
//到合并時
merge(arr,left,mid,right,temp);
}
}
/**
* 合并的方法
* @param arr 排序的原始數(shù)組
* @param left 左邊有序序列的初始索引
* @param mid 中間索引
* @param right 右邊索引
* @param temp 中轉(zhuǎn)的數(shù)組
*/
public static void merge(int arr[],int left,int mid,int right,int[] temp){
// System.out.println("xxxx");
int i = left; //初始化i揽涮,表示 左邊有序序列的初始索引
int j = mid+1; //初始化j抠藕,右邊有序序列的初始索引
int t = 0; // 指向temp 數(shù)組的當(dāng)前索引
//(1)
//先把左右兩邊(有序)的數(shù)據(jù)按照規(guī)則填充到 temp 數(shù)組
//直到左右兩邊的有序序列,有一邊處理完畢為止
while(i <= mid && j <= right){ //繼續(xù)
//如果左邊的有序序列的當(dāng)前元素蒋困,小于等于右邊有序序列的當(dāng)前元素
//將左邊的當(dāng)前元素盾似,拷貝到 temp 數(shù)組
//然后 t 后移, i 后移
if(arr[i] <= arr[j]){
temp[t] = arr[i];
t += 1;
i += 1;
} else{ //反之:右邊的有序序列當(dāng)前元素雪标,大于 左邊的零院,并填充到 temp 數(shù)組中
temp[t] = arr[j];
t += 1;
j += 1;
}
}
//(2)
//把有剩余數(shù)據(jù)的一邊的數(shù)據(jù)依次全部填充到 temp
while( i <= mid){ //左邊的有序序列還有剩余元素,就全部填充到 temp
temp[t] = arr[i];
t += 1;
i += 1;
}
while( j <= right){ //右邊的有序序列還有剩余元素村刨,就全部填充到 temp
temp[t] = arr[j];
t += 1;
j += 1;
}
//(3)
//將 temp 數(shù)組的元素拷貝到 arr
//注意告抄,并不是每次都拷貝所有
t = 0;
int tempLeft = left;
System.out.println("tempLeft="+tempLeft+"right="+right);
while(tempLeft <= right){ //第一次合并 tempLeft = 0,right = 1 第二次:tempLeft=2,right=3 第三次嵌牺,tL=0,right=3
arr[tempLeft] = temp[t];
t += 1;
tempLeft += 1;
}
}
}
11.基數(shù)排序
基數(shù)排序(radix sort) 屬于 “分配式排序” 又稱 “桶子法”
基數(shù)排序是屬于穩(wěn)定性的排序打洼,是桶排序的擴展
基本思想: 將所有待比較數(shù)值同一為同樣的數(shù)位長度,數(shù)位較短的數(shù)前面補零逆粹。然后募疮,從最低位開始,依次進(jìn)行排序僻弹,這樣從最低位排序一直到最高位排序完成以后阿浓,數(shù)列就變成一個有序序列。
示意圖:
- 代碼實現(xiàn):
public class RadixSort {
public static void main(String[] args) {
int arr[] = {53,3,542,748,14,214};
radixSort(arr);
}
//基數(shù)排序方法
public static void radixSort(int[] arr){
//根據(jù)推導(dǎo)過程
//1. 得到數(shù)組中最大數(shù)的位數(shù)
int max = arr[0]; //假設(shè)第一個數(shù)就是最大數(shù)
for (int i = 0; i < arr.length; i++) {
if(arr[i] > max){
max = arr[i];
}
}
//得到最大數(shù)是幾位數(shù)
int maxLength = (max + "").length();
int[][] bucket = new int[10][arr.length];
int[] bucketElementCounts = new int[10];
for (int i = 0,n=1; i < maxLength;i++,n*=10){
//針對每個元素的對應(yīng)位進(jìn)行排序處理蹋绽,第一次是個位芭毙,第二次是十位筋蓖,第三次是百位
for (int j = 0; j < arr.length; j++) {
//取出每個元素的個位
int digitOfElement = arr[j] /n % 10;
//放入對應(yīng)的桶中
bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];
bucketElementCounts[digitOfElement]++; //記錄每個桶中的數(shù)據(jù)
}
//按照這個桶的順序(一維數(shù)組的下標(biāo)依次取出數(shù)據(jù),放入原來數(shù)組)
int index = 0;
//遍歷每一桶退敦,并將桶中的數(shù)粘咖,放入原數(shù)組
for(int k = 0; k < bucketElementCounts.length;k++){
//如果桶中有數(shù)據(jù),才放入原數(shù)組
if(bucketElementCounts[k] != 0){
//循環(huán)該桶即第k個桶(第k個一維數(shù)組)侈百,放入
for(int l = 0; l < bucketElementCounts[k];l++){
//取出元素放到 arr
arr[index++] = bucket[k][l];
}
}
//第i+1輪處理后涂炎,需要將每個 bucketElementCounts[k] = 0;
bucketElementCounts[k] = 0;
}
System.out.println("第"+(i+1)+"輪對個位的排序處理 arr="+ Arrays.toString(arr));
}
/* //第1輪(針對每個元素的個位進(jìn)行排序處理)
//定義一個二維數(shù)組,表示10個桶设哗,每個桶就是一個一維數(shù)組
//說明
//1. 二維數(shù)組包含 10 個一維數(shù)組
//2. 為了防止在放入數(shù)的時候,數(shù)據(jù)溢出两蟀,則每個一維數(shù)組(桶)网梢,大小定為arr.length
//3. 明確:技術(shù)排序是使用空間換時間的經(jīng)典算法
int[][] bucket = new int[10][arr.length];
//為了記錄每個桶中,實際存放多少個數(shù)據(jù)赂毯,我們定義一個一維數(shù)組战虏,來記錄各個桶每次放入數(shù)據(jù)的個數(shù)
//可以這樣理解
//比如:bucketElementCounts[0] ,記錄的是bucket[0] 桶的放入數(shù)據(jù)的個數(shù),
int[] bucketElementCounts = new int[10];
for (int j = 0; j < arr.length; j++) {
//取出每個元素的個位
int digitOfElement = arr[j] % 10;
//放入對應(yīng)的桶中
bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];
bucketElementCounts[digitOfElement]++; //記錄每個桶中的數(shù)據(jù)
}
//按照這個桶的順序(一維數(shù)組的下標(biāo)依次取出數(shù)據(jù)党涕,放入原來數(shù)組)
int index = 0;
//遍歷每一桶烦感,并將桶中的數(shù),放入原數(shù)組
for(int k = 0; k < bucketElementCounts.length;k++){
//如果桶中有數(shù)據(jù)膛堤,才放入原數(shù)組
if(bucketElementCounts[k] != 0){
//循環(huán)該桶即第k個桶(第k個一維數(shù)組)手趣,放入
for(int l = 0; l < bucketElementCounts[k];l++){
//取出元素放到 arr
arr[index++] = bucket[k][l];
}
}
//第2輪處理后,需要將每個 bucketElementCounts[k] = 0;
bucketElementCounts[k] = 0;
}
System.out.println("第1輪對個位的排序處理 arr="+ Arrays.toString(arr));
//第二輪
for (int j = 0; j < arr.length; j++) {
//取出每個元素的個位
int digitOfElement = arr[j] /10 % 10;
//放入對應(yīng)的桶中
bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];
bucketElementCounts[digitOfElement]++; //記錄每個桶中的數(shù)據(jù)
}
//按照這個桶的順序(一維數(shù)組的下標(biāo)依次取出數(shù)據(jù)肥荔,放入原來數(shù)組)
index = 0;
//遍歷每一桶绿渣,并將桶中的數(shù),放入原數(shù)組
for(int k = 0; k < bucketElementCounts.length;k++){
//如果桶中有數(shù)據(jù)燕耿,才放入原數(shù)組
if(bucketElementCounts[k] != 0){
//循環(huán)該桶即第k個桶(第k個一維數(shù)組)中符,放入
for(int l = 0; l < bucketElementCounts[k];l++){
//取出元素放到 arr
arr[index++] = bucket[k][l];
}
}
//第1輪處理后,需要將每個 bucketElementCounts[k] = 0;
bucketElementCounts[k] = 0;
}
System.out.println("第2輪對十位的排序處理 arr="+ Arrays.toString(arr));
//第3輪
for (int j = 0; j < arr.length; j++) {
//取出每個元素的個位
int digitOfElement = arr[j] /100 % 10;
//放入對應(yīng)的桶中
bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];
bucketElementCounts[digitOfElement]++; //記錄每個桶中的數(shù)據(jù)
}
//按照這個桶的順序(一維數(shù)組的下標(biāo)依次取出數(shù)據(jù)誉帅,放入原來數(shù)組)
index = 0;
//遍歷每一桶淀散,并將桶中的數(shù),放入原數(shù)組
for(int k = 0; k < bucketElementCounts.length;k++){
//如果桶中有數(shù)據(jù)蚜锨,才放入原數(shù)組
if(bucketElementCounts[k] != 0){
//循環(huán)該桶即第k個桶(第k個一維數(shù)組)档插,放入
for(int l = 0; l < bucketElementCounts[k];l++){
//取出元素放到 arr
arr[index++] = bucket[k][l];
}
}
//第3輪處理后,需要將每個 bucketElementCounts[k] = 0;
bucketElementCounts[k] = 0;
}
System.out.println("第3輪對百位的排序處理 arr="+ Arrays.toString(arr));*/
}
}
- 基數(shù)排序的說明:是經(jīng)典空間換時間的方式踏志,占用內(nèi)存很大阀捅,當(dāng)對海量數(shù)據(jù)排序時,容易造成
OutOfMemeoryError
- 有負(fù)數(shù)的數(shù)組针余,我們不考慮基數(shù)排序
12. 常用排序算法的比較
13. 相關(guān)術(shù)語解釋
- 穩(wěn)定:如果a原本在b的前面饲鄙,而a=b,排序之后凄诞,a仍然在b的前面
- 不穩(wěn)定:如果a原本在b的前面,而a=b,排序之后忍级,a有可能會出現(xiàn)在b的后面
- 內(nèi)排序:所有排序操作都在內(nèi)存中完成
- 外排序:由于數(shù)據(jù)太大帆谍,因此把數(shù)據(jù)放在磁盤中,而排序通過磁盤和內(nèi)存的數(shù)據(jù)傳輸才能進(jìn)行
- In-place: 不占用額外內(nèi)存
- Out-place:占用額外內(nèi)存