排序 java_十种常见排序算法-JAVA版

[toc]

## 概述

数据结构可视化可以参考[Data Structure Visualizations](https://www.cs.usfca.edu/~galles/visualization/Algorithms.html)

### 分类

十种常见的排序算法主要可分为两大类:比较类排序和非比较类排序

- 比较类:通过对数组中的元素进行比较来实现排序,这类算法的时间复杂度下界为$\Omega(nlogn)$;

- 非比较类:利用非比较的方法获得有关输入数组中的排序信息,则可突破$\Omega(nlogn)$的复杂度下界。

### 复杂度

|排序算法|平均时间复杂度|最坏时间复杂度|最好时间复杂度|空间复杂度|稳定性|

|------  |:------------:|:------------:|:------------:|:--------:|:----:|

|选择排序|$O(n^2)$|$O(n^2)$|$O(n^2)$|$O(1)$|不稳定|

|冒泡排序|$O(n^2)$|$O(n^2)$|$O(n)$|$O(1)$|稳定|

|插入排序|$O(n^2)$|$O(n^2)$|$O(n)$|$O(1)$|稳定|

|希尔排序|$O(n^{1.3})$|$O(n^2)$|$O(n)$|$O(1)$|不稳定|

|归并排序|$O(nlog_2n)$|$O(nlog_2n)$|$O(nlog_2n)$|$O(n)$|稳定|

|快速排序|$O(nlog_2n)$|$O(n^2)$|$O(nlog_2n)$|$O(log_2n)$|不稳定|

|堆排序|$O(nlog_2n)$|$O(nlog_2n)$|$O(nlog_2n)$|$O(1)$|不稳定|

| | |

|计数排序|$O(n+k)$|$O(n+k)$|$O(n+k)$|$O(n+k)$|稳定|

|桶排序|$O(n+k)$|$O(n^2)$|$O(n)$|$O(n+k)$|稳定|

|基数排序|$O(n*k)$|$O(n*k)$|$O(n*k)$|$O(n+k)$|稳定|

以下排序以数组`{4, 27, 7, 34, 29, 17, 0, 6, 10, 31, 25}`为示例

## 比较排序

### 1. 选择排序

#### 1.1 算法描述

选择排序是一种最简单的排序算法,其命名原因就是不断地选择数组中的最小(大)值。首先,找到数组中最小的元素,然后将它和数组的第一个元素交换位置(如果第一个元素最小就和自己交换)。其次,在剩下的元素中找到最小的元素,将其与数组的第二个元素交换位置,如此直到将整个数组都排好序。

#### 1.2 代码实现

```java

//选择排序

public static int[] SelectionSort(int[] arr)

{

System.out.println("选择排序");

int minIdx;

for(int i=0;i

{

minIdx = i;

for(int j=i+1;j

{

if(arr[j]

{

minIdx = j;

}

}

int temp = arr[i];

arr[i] = arr[minIdx];

arr[minIdx] = temp;

System.out.printf("第%d趟结果为:%s\n", i+1, Arrays.toString(arr));

}

return arr;

}

```

#### 1.3 排序过程

```

第1趟结果为:[0, 23, 7, 34, 129, 18, 4, 116, 11, 32, 25]

第2趟结果为:[0, 4, 7, 34, 129, 18, 23, 116, 11, 32, 25]

第3趟结果为:[0, 4, 7, 34, 129, 18, 23, 116, 11, 32, 25]

第4趟结果为:[0, 4, 7, 11, 129, 18, 23, 116, 34, 32, 25]

第5趟结果为:[0, 4, 7, 11, 18, 129, 23, 116, 34, 32, 25]

第6趟结果为:[0, 4, 7, 11, 18, 23, 129, 116, 34, 32, 25]

第7趟结果为:[0, 4, 7, 11, 18, 23, 25, 116, 34, 32, 129]

第8趟结果为:[0, 4, 7, 11, 18, 23, 25, 32, 34, 116, 129]

第9趟结果为:[0, 4, 7, 11, 18, 23, 25, 32, 34, 116, 129]

第10趟结果为:[0, 4, 7, 11, 18, 23, 25, 32, 34, 116, 129]

```

#### 1.4 算法特点

选择排序的总交换次数为N。它有两个鲜明的特点:

- 运行时间和输入无头

- 数据移动是最少

### 2. 冒泡排序

#### 2.1 算法描述

冒泡排序的基本思想就是比较相邻的元素,根据大小交换位置。算法过程如下:

- 比较相邻元素,若前一个比后一个大,则交换两元素;

- 比较每一对相邻元素,一趟排序过后,最后一个元素即为最大元素;

- 重复这样的过程直到排序完成

#### 2.2 代码实现

```java

//冒泡排序

public static int[] BubbleSort(int[] arr)

{

System.out.println("冒泡排序");

for(int i=0;i

{

for(int j=0;j

{

if(arr[j]>arr[j+1])

{

int temp = arr[j];

arr[j] = arr[j+1];

arr[j+1] = temp;

}

}

System.out.printf("第%d趟结果为:%s\n", i+1, Arrays.toString(arr));

}

return arr;

}

```

#### 2.3 排序过程

```

第1趟结果为:[4, 7, 23, 34, 18, 0, 116, 11, 32, 25, 129]

第2趟结果为:[4, 7, 23, 18, 0, 34, 11, 32, 25, 116, 129]

第3趟结果为:[4, 7, 18, 0, 23, 11, 32, 25, 34, 116, 129]

第4趟结果为:[4, 7, 0, 18, 11, 23, 25, 32, 34, 116, 129]

第5趟结果为:[4, 0, 7, 11, 18, 23, 25, 32, 34, 116, 129]

第6趟结果为:[0, 4, 7, 11, 18, 23, 25, 32, 34, 116, 129]

第7趟结果为:[0, 4, 7, 11, 18, 23, 25, 32, 34, 116, 129]

第8趟结果为:[0, 4, 7, 11, 18, 23, 25, 32, 34, 116, 129]

第9趟结果为:[0, 4, 7, 11, 18, 23, 25, 32, 34, 116, 129]

第10趟结果为:[0, 4, 7, 11, 18, 23, 25, 32, 34, 116, 129]

```

### 3. 插入排序

#### 3.1 算法描述

插入排序是一种简单的排序算法。通过类比整理扑克牌可以很好地理解插入排序的过程。插入排序把数组分为两个部分,左端为有序部分,右端为待插入部分。每次选择右端的第一个元素,插入到左端的相应位置,并将左端比该值大的元素依次右移,直到数组全部被排好序。

#### 3.2 代码实现

```java

//插入排序

public static int[] InsertionSort(int[] arr)

{

System.out.println("插入排序");

int cur, j;

for(int i=1;i

{

j = i;

cur = arr[i];

while(j-1>=0 && cur

{

arr[j] = arr[j-1];

arr[j-1] = cur;

j--;

}

System.out.printf("第%d趟结果为:%s\n", i, Arrays.toString(arr));

}

return arr;

}

```

#### 3.3 排序过程

```

第1趟结果为:[4, 23, 7, 34, 129, 18, 0, 116, 11, 32, 25]

第2趟结果为:[4, 7, 23, 34, 129, 18, 0, 116, 11, 32, 25]

第3趟结果为:[4, 7, 23, 34, 129, 18, 0, 116, 11, 32, 25]

第4趟结果为:[4, 7, 23, 34, 129, 18, 0, 116, 11, 32, 25]

第5趟结果为:[4, 7, 18, 23, 34, 129, 0, 116, 11, 32, 25]

第6趟结果为:[0, 4, 7, 18, 23, 34, 129, 116, 11, 32, 25]

第7趟结果为:[0, 4, 7, 18, 23, 34, 116, 129, 11, 32, 25]

第8趟结果为:[0, 4, 7, 11, 18, 23, 34, 116, 129, 32, 25]

第9趟结果为:[0, 4, 7, 11, 18, 23, 32, 34, 116, 129, 25]

第10趟结果为:[0, 4, 7, 11, 18, 23, 25, 32, 34, 116, 129]

```

#### 3.4 算法特点

其所需时间取决于输入元素的初始顺序,适合小规模的数组。

### 4. 希尔排序

#### 4.1 算法描述

希尔排序是一种基于插入排序的快速的排序算法。插入排序对于大规模乱序数组速度很慢,这主要是因为其在插入的过程中,只交换相邻两个元素。而希尔排序则交换不相邻的元素以对数组的局部进行排序,最终用插入排序将局部有序的数组排序。希尔排序是第一个突破$O(n^2)$的排序算法。

希尔排序的思想是使数组中任意间隔为h的元素都是有序的,这样的数组称为h有序数组。在橙书《算法》中,希尔排序的h有序数组使用序列$1/2(3^k-1)$,从$N/3$开始递减至1。如下的实现由于数组只有10个所以用`1, 4, 13, 40, 121, 364, 1093, ... `这样的递增序列无法较为清楚地展示希尔排序的过程,所以选择使用$N/2$的方法。

#### 4.2 代码实现

```java

//希尔排序

public static int[] ShellSort(int[] arr)

{

System.out.println("希尔排序");

int len = arr.length;

int cnt = 1;

int cur, j;

for(int gap=len/2;gap>0;gap=gap/2)

{

//注意此处i的终止条件,i和简单插入排序一样要跑到最后的

for(int i=gap;i

{

j = i;

cur = arr[j];

while(j-gap>=0 && cur

{

arr[j] = arr[j-gap];

arr[j-gap] = cur;

j -= gap;

}

}

System.out.printf("第%d趟增量为%d,结果为:%s\n", cnt, gap, Arrays.toString(arr));

cnt++;

}

return arr;

}

```

#### 4.3 排序过程

```

第1趟增量为5,结果为:[4, 0, 7, 11, 32, 18, 23, 116, 34, 129, 25]

第2趟增量为2,结果为:[4, 0, 7, 11, 23, 18, 25, 116, 32, 129, 34]

第3趟增量为1,结果为:[0, 4, 7, 11, 18, 23, 25, 32, 34, 116, 129]

```

#### 4.4 算法特点

希尔排序的代码量很小,且不需要额外的内存空间,对于中等大小的数组其运行时间可接受。后续介绍的归并、快排和堆排序更加高效,但除了对于很大的$N$,这些高级的排序算法可能只比希尔排序快两倍,而且更复杂。所以当需要解决一个排序问题而没有api(如嵌入式系统中)时,就可用希尔排序。

### 5. 归并排序

#### 5.1 算法描述

归并排序是一种建立在归并操作上的典型的基于分治法的排序算法。算法递归地将数组分成两半,分别排序,然后将结果归并起来。

- 原地归并

首先将复制所有元素复制到辅助数组中,然后再归并回原始数组。

>方法在归并时进行了4个条件判断:左半边用尽(取右半边的元素)、右半边用尽(取左半边的元素)、右半边的当前元素小于左半边的当前元素(取右半边的元素)、右半边的当前元素大于等于左半边的当前元素(取左半边的元素)。

>——引自橙书

```java

//原地归并

private static void merge(int[] arr, int low, int mid, int high)

{

int i = low, j = mid + 1;

int len = high - low + 1;

System.arraycopy(arr, low, mergeHelperArr, low, len);

//        for(int k=low;k<=high;k++)

//        {

//            temp[k] = ArrToMerge[k];

//        }

for(int k=low;k<=high;k++)

{

//i++ 先赋值,再加1

/*语句arr[k] = mergeHelperArr[j++];

相当于如下两句:

arr[k] = mergeHelperArr[j];

j++;

*/

if(i>mid) arr[k] = mergeHelperArr[j++];

else if(j>high) arr[k] = mergeHelperArr[i++];

else if(arr[i]

else if(arr[i]>arr[j]) arr[k] = mergeHelperArr[j++];

}

}

```

#### 5.2 代码实现

```java

//归并排序,参考橙书p170

//Top-Down 递归

private static int[] mergeHelperArr; //辅助数组

public static int[] MergeSort(int[] arr)

{

System.out.println("归并排序");

mergeHelperArr = new int[arr.length];

return mergeHelper(arr, 0, arr.length-1);

}

private static int[] mergeHelper(int[] arr, int low, int high)

{

if(high<=low)

{

return arr;

}

//亦为二分查找的mid计算方法,此法可避免(low+high)/2的溢出问题,如low+high>Integer.MAX_VALUE时

int mid = low + (high - low)/2;

mergeHelper(arr, 0, mid); //左半边排序

mergeHelper(arr, mid+1, high); //右半边排序

merge(arr, low, mid, high); //归并代码见原地归并方法

return arr;

}

```

#### 5.3 排序过程

由于递归调用无法直接明白地输出排序过程,可视化可以参考Data Structure Visualizations中比较排序之[归并](https://www.cs.usfca.edu/~galles/visualization/ComparisonSort.html)。

#### 5.4 算法特点

归并排序能够保证将任意长度为$N$的数组排序所需时间与$NlogN$成正比,主要缺点是需要额外空间与$N$成正比。

### 6. 快速排序

#### 6.1 算法描述

与归并一样,快排也是分治法的一种应用。其排序的分治过程分三个步骤:

- **分解**:在数组中选择一个元素作为基准(pivot),将数组分为两个子数组,一个比基准小,另一个比基准大;

- **解决**:通过递归调用快速排序,对两个子数组排序;

- **合并**:由于两个数组是就地排序的,所以合并不需要操作,整个数组已经排序。

#### 6.2 代码实现

```java

//快速排序,参考算法导论p85,86

public static int[] QuickSort(int[] arr){

System.out.println("快速排序");

return QSort(arr, 0, arr.length-1);

}

//快速排序递归调用

private static int[] QSort(int[] arr, int left, int right)

{

if(left>=right) return arr;

int v = partition(arr, left, right); //pivot = arr[j]

QSort(arr, left, v-1);

QSort(arr, v+1, right);

return arr;

}

//数组划分,partition过程

private static int partition(int[] arr, int left, int right)

{

int i = left-1, j;

int pivot = arr[right];

for(j=left;j<=right-1;j++)

{

if(arr[j]<=pivot)

{

i++;

swap(arr, i, j);

}

}

swap(arr, i+1, right);

System.out.printf("pivot为%d,分割结果为:%s\n", pivot, Arrays.toString(arr));

return i+1;

}

```

#### 6.3 排序过程

```

pivot为25,分割结果为:[4, 7, 17, 0, 11, 25, 34, 116, 129, 31, 27]

pivot为11,分割结果为:[4, 7, 0, 11, 17, 25, 34, 116, 129, 31, 27]

pivot为0,分割结果为:[0, 7, 4, 11, 17, 25, 34, 116, 129, 31, 27]

pivot为4,分割结果为:[0, 4, 7, 11, 17, 25, 34, 116, 129, 31, 27]

pivot为27,分割结果为:[0, 4, 7, 11, 17, 25, 27, 116, 129, 31, 34]

pivot为34,分割结果为:[0, 4, 7, 11, 17, 25, 27, 31, 34, 116, 129]

pivot为129,分割结果为:[0, 4, 7, 11, 17, 25, 27, 31, 34, 116, 129]

```

#### 6.4 算法特点

以上数组划分的基准选择的是数组或子数组的最后一个元素,这种方法虽然简单明确,但是并不是一种好的划分方法。因为这种划分方式在输入数组是预排序的或反序的时候,将使快速排序的时间复杂度退化到$O(N^2)$。下面介绍两种较好的pivot选择方法:

- 随机选取

随机选取pivot是比较安全方法,快排的平均复杂度计算一般用随机选取的划分方法。

- 三数中值分割(Median-of-Three Partitioning)

这方法一般选择数组左端、右端及中心位置三个元素的中值作为pivot。如示例数组`{4, 27, 7, 34, 29, 17, 0, 6, 10, 31, 25}`中,左端元素为4,右端元素为25,中心元素为17,三数的中值为17,于是pivot选择17。 使用三数中值法消除了预排序输入的情况,减少了快排的运行时间。如下是一种三数中值分割的实现。

```java

//三数中值分割

//需要修改一种简单排序算法的参数,用于在left+3>right的时候使用

public static int Midian3(int[] arr, int left, int right)

{

int mid = left + (right-left) / 2;

if(arr[left]>arr[mid]) swap(arr, left, mid);

if(arr[left]>arr[right]) swap(arr, left, right);

if(arr[mid]>arr[right]) swap(arr, mid, right);

swap(arr, mid, right-1);

return arr[right-1];

}

```

### 7. 堆排序

#### 7.1 算法描述

堆排序是是一种基于堆这种数据结构的排序方法。堆可以被视为一棵完全二叉树,除最后一层外,堆的每一层都是被填满的。堆具有子节点小于或大于父节点的特点,即堆的最小或最大节点为根节点。堆分为小顶堆和大顶堆。在堆排序中,升序使用大顶堆,降序使用小顶堆。

堆排序算法的过程如下:

- **建堆:**将数组构建为大顶堆;

- **交换元素:**最大元素在堆顶,即`arr[0]`,将`arr[0]`与`arr[n-1]`交换,得到新的数组`{arr[0]...arr[n-2]<=arr[n-1]`;

- **保持堆性质:**交换元素后,堆的状态可能不符合堆的性质,需要调用`maxHeapify`调整为新的堆

- **重复此过程:**使堆的大小从n-1降到2

#### 7.2 代码实现

```java

//堆排序,算法导论p74

//升序用大顶堆,降序用小顶堆

//TODO:画出堆的状态

public static int[] HeapSort(int[] arr)

{

System.out.println("堆排序");

buildMaxHeap(arr);

int len = arr.length;

for(int i=len-1;i>0;i--)

{

System.out.printf("%s\n", Arrays.toString(arr));

swap(arr, 0, i);

len--;

maxHeapify(arr, 0, len);

}

return arr;

}

//建堆

private static void buildMaxHeap(int[] arr)

{

int len = arr.length;

for(int i=arr.length/2;i>=0;i--)

{

maxHeapify(arr, i, len);

}

}

//保持堆的性质

private static void maxHeapify(int[] arr, int i, int len)

{

int left = 2 * i + 1;

int right = 2 * i + 2;

int largest = i;

if(leftarr[largest])

{

largest = left;

}

if(rightarr[largest])

{

largest = right;

}

if(largest!=i)

{

swap(arr, i, largest);

maxHeapify(arr, largest, len);

}

}

```

#### 7.3 排序过程

```

当前堆的状态为:

129

116 18 34 32

7 0 4

11 23 25

当前数组状态为[129, 116, 18, 34, 32, 7, 0, 4, 11, 23, 25]

当前堆的状态为:

116

34 18 25 32

7 0 4

11 23

当前数组状态为[116, 34, 18, 25, 32, 7, 0, 4, 11, 23, 129]

当前堆的状态为:

34

32 18 25 23

7 0 4

11

当前数组状态为[34, 32, 18, 25, 23, 7, 0, 4, 11, 116, 129]

当前堆的状态为:

32

25 18 11 23

7 0 4

当前数组状态为[32, 25, 18, 11, 23, 7, 0, 4, 34, 116, 129]

当前堆的状态为:

25

23 18 11 4

7 0

当前数组状态为[25, 23, 18, 11, 4, 7, 0, 32, 34, 116, 129]

当前堆的状态为:

23

11 18 0 4

7

当前数组状态为[23, 11, 18, 0, 4, 7, 25, 32, 34, 116, 129]

当前堆的状态为:

18

11 7 0 4

当前数组状态为[18, 11, 7, 0, 4, 23, 25, 32, 34, 116, 129]

当前堆的状态为:

11

4 7 0

当前数组状态为[11, 4, 7, 0, 18, 23, 25, 32, 34, 116, 129]

当前堆的状态为:

7

4 0

当前数组状态为[7, 4, 0, 11, 18, 23, 25, 32, 34, 116, 129]

当前堆的状态为:

4

0

当前数组状态为[4, 0, 7, 11, 18, 23, 25, 32, 34, 116, 129]

当前堆的状态为:

0

```

#### 7.4 算法特点

堆排序是一种原地排序算法,所以其空间复杂度为$O(1)$。

## 非比较排序

### 8. 计数排序

#### 8.1 算法描述

计数排序需要满元素中的每一个都是介于0到k之间的整数。其基本思想为对每一个元素确定小于该元素的元素个数,这样就可以直接把该元素放到最终输出数组中的位置上。如,有5个元素小于元素i,则i的最终位置为5。

#### 8.2 代码实现

```java

//计数排序

public static int[] CountingSort(int[] arr)

{

System.out.println("计数排序");

int maxVal = getMaxValue(arr);

int[] bucket = new int[maxVal+1];

for(int iter : arr)

{

bucket[iter]++;

}

System.out.printf("中间数组为%s\n", Arrays.toString(bucket));

int j = 0;

for(int i=0;i<=maxVal;i++)

{

while(bucket[i]>0)

{

arr[j] = i;

bucket[i]--;

j++;

}

}

return arr;

}

```

#### 8.3 排序过程

```

中间数组为[1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]

```

### 9. 桶排序

#### 9.1 算法描述

当数组符合均匀分布时,应用桶排序可使排序过程以线性期望时间运行。桶排序的基本思想是:

- 建立n个桶,n可为数组大小,桶的跨度为$dis=(maxVal-minVal)/(n-1)$,其中最后一个桶为最大值本身;

- 遍历数组,把元素放入各个桶中;

- 对桶内元素排序

- 遍历各桶,输出元素

#### 9.2 代码实现

```java

//桶排序,参考程序员小灰

public static int[] BucketSort(int[] arrInt)

{

System.out.println("桶排序");

double[] arr = new double[arrInt.length];

for(int i=0;i

arr[i] = arrInt[i];

//1.计算max,min得到差

double maxVal = arr[0], minVal = arr[0];

for(double iter : arr)

{

if(iter>maxVal) maxVal = iter;

if(iter

}

double dis = maxVal - minVal;

//2.建桶

int bucketNum = arr.length;

ArrayList> bucketList =

new ArrayList>(bucketNum);

for(int i=0;i

{

bucketList.add(new LinkedList());

}

//3.遍历数组,放入桶中

for(double iter : arr)

{

int pos = (int)((iter-minVal) * (bucketNum-1) / dis);

bucketList.get(pos).add(iter);

System.out.print("各个桶内元素为");

System.out.println(bucketList);

}

//4.对桶内进行排序

for(LinkedList iter : bucketList)

{

Collections.sort(iter);

}

//5.依次输出

int[] arrReturn = new int[arr.length];

int idx = 0;

for(LinkedList iter : bucketList)

{

for(double ele : iter)

{

arrReturn[idx] = (int)ele;

idx++;

}

}

return arrReturn;

}

```

#### 9.3 排序过程

```

各个桶内元素为[[4.0], [], [], [], [], [], [], [], [], [], []]

各个桶内元素为[[4.0], [23.0], [], [], [], [], [], [], [], [], []]

各个桶内元素为[[4.0, 7.0], [23.0], [], [], [], [], [], [], [], [], []]

各个桶内元素为[[4.0, 7.0], [23.0], [34.0], [], [], [], [], [], [], [], []]

各个桶内元素为[[4.0, 7.0], [23.0], [34.0], [], [], [], [], [], [], [], [129.0]]

各个桶内元素为[[4.0, 7.0], [23.0, 18.0], [34.0], [], [], [], [], [], [], [], [129.0]]

各个桶内元素为[[4.0, 7.0, 0.0], [23.0, 18.0], [34.0], [], [], [], [], [], [], [], [129.0]]

各个桶内元素为[[4.0, 7.0, 0.0], [23.0, 18.0], [34.0], [], [], [], [], [], [116.0], [], [129.0]]

各个桶内元素为[[4.0, 7.0, 0.0, 11.0], [23.0, 18.0], [34.0], [], [], [], [], [], [116.0], [], [129.0]]

各个桶内元素为[[4.0, 7.0, 0.0, 11.0], [23.0, 18.0], [34.0, 32.0], [], [], [], [], [], [116.0], [], [129.0]]

各个桶内元素为[[4.0, 7.0, 0.0, 11.0], [23.0, 18.0, 25.0], [34.0, 32.0], [], [], [], [], [], [116.0], [], [129.0]]

```

### 10.基数排序

#### 10.1 算法描述

基数排序是一种用在老式穿卡机上的算法。这种算法先按照最低有效位数字进行排序,再对高一位的有效位数字进行排序,直到最高位。

- 取得数组中的最大值,算出其数位;

- 从对低位开始,根据每一数位进行排序;

#### 10.2 代码实现

```java

//基数排序

public static int[] RadixSort(int[] arr)

{

System.out.println("基数排序");

int radixLen = getRadixLen(getMaxValue(arr));

int radix = 1;

for(int i=1;i<=radixLen;i++)

{

countingSort(arr, radix);

System.out.printf("当前数位为%d\n", radix);

System.out.printf("排序结果为%s\n", Arrays.toString(arr));

radix *= 10;

}

return arr;

}

//在各个数位之间用counting sort

//radix为排序的基数

private static void countingSort(int[] arr, int radix)

{

int[] bucket = new int[10];

int[] temp = new int[arr.length];

for(int iter : arr)

{

bucket[(iter/radix)%10]++;

}

for(int i=1;i<10;i++)

{

bucket[i] += bucket[i-1];

}

for(int i=arr.length-1;i>=0;i--)

{

temp[bucket[(arr[i]/radix)%10]-1] = arr[i];

bucket[(arr[i]/radix)%10]--;

}

//arraycopy的方向为 src==>dst,参数顺序为 src, 复制开始idx, dst, 复制开始idx, 复制长度

System.arraycopy(temp, 0, arr, 0, arr.length);

}

//获得max的位数

private static int getRadixLen(int maxVal)

{

if(maxVal==0) return 1;

int radixLen = 0;

while(maxVal!=0)

{

maxVal /= 10;

radixLen++;

}

return radixLen;

}

```

#### 10.3 排序过程

```

当前数位为1

排序结果为[0, 11, 32, 23, 4, 34, 25, 116, 7, 18, 129]

当前数位为10

排序结果为[0, 4, 7, 11, 116, 18, 23, 25, 129, 32, 34]

当前数位为100

排序结果为[0, 4, 7, 11, 18, 23, 25, 32, 34, 116, 129]

```

- 一些公用函数

```java

//helper

private static void swap(int[] arr, int i, int j)

{

int temp = arr[i];

arr[i] = arr[j];

arr[j] = temp;

}

private static int getMaxValue(int[] arr)

{

int maxVal = 0;

for(int iter : arr)

{

if(iter>maxVal) maxVal = iter;

}

return maxVal;

}

/*

被static修饰的成员变量和成员方法独立于该类的任何对象。也就是说,它不依赖类特定的实例,被类的所有实例共享。

只要这个类被加载,Java虚拟机就能根据类名在运行时数据区的方法区内定找到他们。

因此,static对象可以在它的任何对象创建之前访问,无需引用任何对象

用public修饰的static成员变量和成员方法本质是全局变量和全局方法,当声明它类的对象市,

不生成static变量的副本,而是类的所有实例共享同一个static变量

static变量前可以有private修饰,表示这个变量可以在类的静态代码块中,或者类的其他静态成员方法中使用(当然也可以在非静态成员方法中使用),

但是不能在其他类中通过类名来直接引用,这一点很重要。实际上你需要搞明白,private是访问权限限定,static表示不要实例化就可以使用。

static前面加上其它访问权限关键字的效果也以此类推

*/

```

## 总结

排序算法是算法学习中最基本的问题,而且也是程序员面试中的高频问题。我自己目前在秋招过程中已经被到问到过3次快排了。这篇blog也算是总结一下自己之前的面试。

全部代码已上传到github上我的项目[Dr-Cube/DataStructuresAlgorithm](https://github.com/Dr-Cube/DataStructuresAlgorithm)。写的时候不知道,写完才发现可以用泛型。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值