目录
一、冒泡排序
冒泡排序(Bubble Sort),是一种计算机科学领域的较简单的排序算法。
它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果顺序(如从大到小、首字母从Z到A)错误就把他们交换过来。走访元素的工作是重复地进行,直到没有相邻元素需要交换,也就是说该元素列已经排序完成。
这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端(升序或降序排列),就如同碳酸饮料中二氧化碳的气泡最终会上浮到顶端一样,故名“冒泡排序”。
冒泡排序算法的原理如下: [1]
-
比较相邻的元素。如果第一个比第二个大,就交换他们两个。 [1]
-
对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。 [1]
-
针对所有的元素重复以上的步骤,除了最后一个。 [1]
-
持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。 [1]
public static void BubbleSort(int[] arr){
int length = arr.length;
for (int i = 1; i < length; i++) {
for (int j = 0; j <= length - 1 - i; j++) {
if(arr[j]>arr[j+1]){
int temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
}
}
二、快速排序
快速排序(Quicksort),计算机科学词汇,适用领域Pascal,c++等语言,是对冒泡排序算法的一种改进。
设要排序的数组是A[0]……A[N-1],首先任意选取一个数据(通常选
用数组的第一个数)作为关键数据,然后将所有比它小的数都放到它左边,所有比它大的数都放到它右边,这个过程称为一趟快速排序。值得注意的是,快速排序不是一种稳定的排序算法,也就是说,多个相同的值的相对位置也许会在算法结束时产生变动。 [1]
一趟快速排序的算法是: [1]
1)设置两个变量i、j,排序开始的时候:i=0,j=N-1; [1]
2)以第一个数组元素作为关键数据,赋值给key,即key=A[0]; [1]
3)从j开始向前搜索,即由后开始向前搜索(j--),找到第一个小于key的值A[j],将A[j]和A[i]的值交换; [1]
4)从i开始向后搜索,即由前开始向后搜索(i++),找到第一个大于key的A[i],将A[i]和A[j]的值交换; [1]
5)重复第3、4步,直到i==j; (3,4步中,没找到符合条件的值,即3中A[j]不小于key,4中A[i]不大于key的时候改变j、i的值,使得j=j-1,i=i+1,直至找到为止。找到符合条件的值,进行交换的时候i, j指针位置不变。另外,i==j这一过程一定正好是i+或j-完成的时候,此时令循环结束)。 [1]
假设一开始序列{xi}是:5,3,7,6,4,1,0,2,9,10,8。
此时,ref=5,i=1,j=11,从后往前找,第一个比5小的数是x8=2,因此序列为:2,3,7,6,4,1,0,5,9,10,8。
此时i=1,j=8,从前往后找,第一个比5大的数是x3=7,因此序列为:2,3,5,6,4,1,0,7,9,10,8。
此时,i=3,j=8,从第8位往前找,第一个比5小的数是x7=0,因此:2,3,0,6,4,1,5,7,9,10,8。
此时,i=3,j=7,从第3位往后找,第一个比5大的数是x4=6,因此:2,3,0,5,4,1,6,7,9,10,8。
此时,i=4,j=7,从第7位往前找,第一个比5小的数是x6=1,因此:2,3,0,1,4,5,6,7,9,10,8。
此时,i=4,j=6,从第4位往后找,直到第6位才有比5大的数,这时,i=j=6,ref成为一条分界线,它之前的数都比它小,之后的数都比它大,对于前后两部分数,可以采用同样的方法来排序。 [3]
public static void QuickSort(int[] arr,int start,int end){
if(start>=end) return;
int i=start,j=end;
int base=arr[start];
//一趟排序
while (i<j){
//从右向左找到第一个比base小的数
while (i<j && arr[j]>=base){
j--;
}
if(i<j) {
swap(arr, i, j);
i++;
}
//从左向右找到第一个比base大的数
while (i<j && arr[i]<=base){
i++;
}
if(i<j){
swap(arr,i,j);
j--;
}
}
QuickSort(arr,start,i-1);
QuickSort(arr,i+1,end);//注意不能取i,不然会导致死循环
}
三、插入排序
插入排序,一般也被称为直接插入排序。对于少量元素的排序,它是一个有效的算法 [1] 。插入排序是一种最简单的排序方法,它的基本思想是将一个记录插入到已经排好序的有序表中,从而一个新的、记录数增1的有序表。在其实现过程使用双层循环,外层循环对除了第一个元素之外的所有元素,内层循环对当前元素前面有序表进行待插入位置查找,并进行移动 [2] 。
原理:
插入排序的工作方式像许多人排序一手扑克牌。开始时,我们的左手为空并且桌子上的牌面向下。然后,我们每次从桌子上拿走一张牌并将它插入左手中正确的位置。为了找到一张牌的正确位置,我们从右到左将它与已在手中的每张牌进行比较。拿在左手上的牌总是排序好的,原来这些牌是桌子上牌堆中顶部的牌 [1] 。
插入排序是指在待排序的元素中,假设前面n-1(其中n>=2)个数已经是排好顺序的,现将第n个数插到前面已经排好的序列中,然后找到合适自己的位置,使得插入第n个数的这个序列也是排好顺序的。按照此法对所有元素进行插入,直到整个序列排为有序的过程,称为插入排序 [3] 。
public static void insertion_sort(int[] arr){
// 插入排序(Insertion-Sort)的算法描述是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,
// 找到相应位置并插入。例如要将数组arr=[4,2,8,0,5,1]排序,可以将4看做是一个有序序列,将[2,8,0,5,1]看做一个无序序列。
// 无序序列中2比4小,于是将2插入到4的左边,此时有序序列变成了[2,4],无序序列变成了[8,0,5,1]。
// 无序序列中8比4大,于是将8插入到4的右边,有序序列变成了[2,4,8],无序序列变成了[0,5,1]。以此类推,最终数组按照从小到大排序。该算法的时间复杂度为O(n^2)。
int len=arr.length;
int left;//左边已排序数组的最后一个数的索引
int right;//右边未排序数组的第一个数的索引
int key;//待排序的第一个数
for (int i = 1; i < len; i++) {
left=i-1;
right=i;
key=arr[i];
while (left>=0 && key<arr[left]){
arr[left+1]=arr[left];
left--;
}
arr[left+1]=key;
}
}
四、希尔排序
希尔排序(Shell's Sort)是插入排序的一种又称“缩小增量排序”(Diminishing Increment Sort),是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。该方法因 D.L.Shell 于 1959 年提出而得名。
希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至 1 时,整个文件恰被分成一组,算法便终止。 [1]
原理:
先取一个小于n的整数d1作为第一个增量,把文件的全部记录分组。所有距离为d1的倍数的记录放在同一个组中。先在各组内进行直接插入排序;然后,取第二个增量d2 <d1
该方法实质上是一种分组插入方法
比较相隔较远距离(称为增量)的数,使得数移动时能跨过多个元素,则进行一次比较就可能消除多个元素交换。D.L.shell于1959年在以他名字命名的排序算法中实现了这一思想。算法先将要排序的一组数按某个增量d分成若干组,每组中记录的下标相差d.对每组中全部元素进行排序,然后再用一个较小的增量对它进行分组,在每组中再进行排序。当增量减到1时,整个要排序的数被分成一组,排序完成。
一般的初次取序列的一半为增量,以后每次减半,直到增量为1。
给定实例的shell排序的排序过程
假设待排序文件有10个记录,其关键字分别是:
49,38,65,97,76,13,27,49,55,04。
增量序列的取值依次为:
5,2,1
public static void shell_sort(int[] arr){
int len = arr.length;
//确定增量
for (int gap=len/2; gap > 0 ; gap/=2) {
for (int i = gap; i < len; i++) {
for (int j = i; j >=gap; j-=gap) {
if(arr[j]<arr[j-gap]){
swap(arr,j-gap,j);
}else {
break;
}
}
}
}
}
五、归并排序
归并排序是建立在归并操作上的一种有效,稳定的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
原理:
归并操作,也叫归并算法,指的是将两个顺序序列合并成一个顺序序列的方法。
如 设有数列{6,202,100,301,38,8,1}
初始状态:6,202,100,301,38,8,1
第一次归并后:{6,202},{100,301},{8,38},{1},比较次数:3;
第二次归并后:{6,100,202,301},{1,8,38},比较次数:4;
第三次归并后:{1,6,8,38,100,202,301},比较次数:4;
总的比较次数为:3+4+4=11;
逆序数为14;
public static void MergeSort(int[] arr,int start,int end){
if (start >= end) return;
int mid = (start + end) / 2;
//拆分数组
MergeSort(arr, start, mid);
MergeSort(arr, mid + 1, end);
//合并两个有序数组数组
MergeTwoInSort(arr, start, mid, end);
}
//合并两个有序数组
private static void MergeTwoInSort(int[] arr,int start,int mid,int end){
int left=start,right=mid+1;
int[] temp=new int[end-start+1];
int Idx=0;
while (left<=mid && right<=end){
if(arr[left]<=arr[right]){
temp[Idx++]=arr[left++];
}else {
temp[Idx++]=arr[right++];
}
}
//左边有剩余
while (left<=mid){
temp[Idx++]=arr[left++];
}
//右边有剩余
while (right<=end){
temp[Idx++]=arr[right++];
}
//把临时数组拷贝到arr中
Idx=0;
while (start<=end){
arr[start++]=temp[Idx++];
}
}
六、选择排序
选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理是:第一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后再从剩余的未排序元素中寻找到最小(大)元素,然后放到已排序的序列的末尾。以此类推,直到全部待排序的数据元素的个数为零。选择排序是不稳定的排序方法。
原理:
首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
public static void selectionSort(int[] arr){
int length = arr.length;
for (int i = 0; i <length; i++) {
int min=i;
for (int j = i+1; j < length; j++) {
if(arr[min]>arr[j]){
//记录最小值的索引
min=j;
}
}
//每一轮循环结束交换最小值放到i索引位置
swap(arr,min,i);
}
}
七、堆排序
首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
原理:
堆的数据结构中,堆中的最大值总是位于根节点(在优先队列中使用堆的话堆中的最小值位于根节点)。堆中定义以下几种操作:
-
最大堆调整(Max Heapify):将堆的末端子节点作调整,使得子节点永远小于父节点
-
创建最大堆(Build Max Heap):将堆中的所有数据重新排序
-
堆排序(HeapSort):移除位在第一个数据的根节点,并做最大堆调整的递归运算
public static void heapSort(int[] arr){
//完全二叉树 某个结点i的左孩子结点为 2*i+1
// 某个结点i的右孩子结点为 2*i+2
// 某个结点i的父亲结点为 (i-1)/2
//建大顶堆 从最后一个非叶子结点(arr.len-1-1)/2=>arr.len/2-1开始建堆
for(int i=arr.length/2-1;i>=0;i--){
Down(arr,i,arr.length-1);
}
//堆排序 交换数组最后一个元素和第一个元素 然后让第一个元素下沉再次调整成大顶堆 重复
for (int i = arr.length-1; i > 0; i--) {
//交换堆顶和最后一个结点的值
swap(arr,0,i);
//让堆顶元素下沉,重新调整到大顶堆结构
Down(arr,0,i-1);
}
}
//下沉方法
private static void Down(int[] arr, int i, int maxIdx) {
int max=i;
int left=2*i+1;
int right=2*i+2;
//左孩子比父节点大
if(left<=maxIdx && arr[left]>arr[i]){
max=left;//记录最大值为左孩子索引
}
if(right<=maxIdx && arr[right]>arr[max]){
max=right;//记录最大值为右孩子索引
}
if(max != i){
//最大不再是父节点,需要交换
swap(arr,max,i);
//递归调用down操作,父节点下沉后破坏大顶堆结构
Down(arr,max,maxIdx);
}
}
八、计数排序
计数排序是一个非基于比较的排序算法,该算法于1954年由 Harold H. Seward 提出。它的优势在于在对一定范围内的整数排序时,它的复杂度为Ο(n+k)(其中k是整数的范围),快于任何比较排序算法。 [1] 当然这是一种牺牲空间换取时间的做法,而且当O(k)>O(n*log(n))的时候其效率反而不如基于比较的排序(基于比较的排序的时间复杂度在理论上的下限是O(n*log(n)), 如归并排序,堆排序)
原理:
计数排序对输入的数据有附加的限制条件:
2、设输入的线性表的长度为n,|S|=k(表示集合S中元素的总数目为k),则k=O(n)。
在这两个条件下,计数排序的复杂性为O(n)。
计数排序的基本思想是对于给定的输入序列中的每一个元素x,确定该序列中值小于x的元素的个数(此处并非比较各元素的大小,而是通过对元素值的计数和计数值的累加来确定)。一旦有了这个信息,就可以将x直接存放到最终的输出序列的正确位置上。例如,如果输入序列中只有17个元素的值小于x的值,则x可以直接存放在输出序列的第18个位置上。当然,如果有多个元素具有相同的值时,我们不能将这些元素放在输出序列的同一个位置上,因此,上述方案还要作适当的修改。
public static void counting_sort(int[] arr){
//找到数组最大值和最小值
int max=arr[0];
int min=arr[0];
for (int i = 0; i < arr.length; i++) {
if(arr[i]>max) max=arr[i];
if(arr[i]<min) min=arr[i];
}
//创建计数数组
int[] count=new int[max-min+1];
//计数原数组
for (int i = 0; i < arr.length; i++) {
count[arr[i]-min]++;
}
//遍历计数数组,拷贝到原数组中
int k=0;
for (int i = 0; i < count.length; i++) {
while (count[i]>0){
arr[k++]=i+min;
count[i]--;
}
}
}
九、桶排序
桶排序 (Bucket sort)是计算排序的升级版,是一个排序算法,工作的原理是将数组分到有限数量的桶子里。每个桶子再个别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排序)。桶排序是鸽巢排序的一种归纳结果。当要被排序的数组内的数值是均匀分配的时候,桶排序使用线性时间(Θ(n))。但桶排序并不是 比较排序,他不受到 O(n log n) 下限的影响。
原理:
假定:输入是由一个随机过程产生的[0, 1)区间上均匀分布的实数。将区间[0, 1)划分为n个大小相等的子区间(桶),每桶大小1/n:[0, 1/n), [1/n, 2/n), [2/n, 3/n),…,[k/n, (k+1)/n ),…将n个输入元素分配到这些桶中,对桶中元素进行排序,然后依次连接桶输入0 ≤A[1..n] <1辅助数组B[0..n-1]是一指针数组,指向桶(链表)
public static void bucketSort(int[] arr){
//找到数组最大值和最小值
int max=arr[0];
int min=arr[0];
for (int i = 0; i < arr.length; i++) {
if(arr[i]>max) max=arr[i];
if(arr[i]<min) min=arr[i];
}
//确定桶的数量,并创建空桶
int count=(max-min)/arr.length+1;
ArrayList<ArrayList<Integer>> buckets = new ArrayList<>();
for (int i = 0; i < count; i++) {
buckets.add(new ArrayList<Integer>());
}
//把arr元素放到桶里
for (int i = 0; i < arr.length; i++) {
//计算元素放到哪个桶
int idx=(arr[i]-min)/arr.length;
buckets.get(idx).add(arr[i]);
}
//对每个桶进行排序
int k=0;
for (int i = 0; i < count; i++) {
//对每个桶进行排序
Collections.sort(buckets.get(i));
//依次输出桶数据到arr中
for (Integer num : buckets.get(i)) {
arr[k++]=num;
}
}
}
十、基数排序
基数排序(radix sort)属于“分配式排序”(distribution sort),又称“桶子法”(bucket sort)或bin sort,顾名思义,它是透过键值的部份资讯,将要排序的元素分配至某些“桶”中,藉以达到排序的作用,基数排序法是属于稳定性的排序,其时间复杂度为O (nlog(r)m),其中r为所采取的基数,而m为堆数,在某些时候,基数排序法的效率高于其它的稳定性排序法。
原理:
基数排序的发明可以追溯到1887年赫尔曼·何乐礼在打孔卡片制表机(Tabulation Machine)上的贡献。它是这样实现的:将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。
基数排序的方式可以采用LSD(Least significant digital)或MSD(Most significant digital),LSD的排序方式由键值的最右边开始,而MSD则相反,由键值的最左边开始。
public static void RadixSort(int[] arr){
//找到数组中的最大值
int max=arr[0];
for (int i = 1; i < arr.length; i++) {
if(arr[i]>max) max=arr[i];
}
System.out.println(max);
//依次根据个位,十位,百位...排序
for(int exp=1;max/exp>0;exp*=10){
//创建10个桶
int[] buckets=new int[10];
//创建temp数组
int[] temp=new int[arr.length];
//计算每个桶中数据的个数
for (int i : arr) {
buckets[i/exp%10]++;
}
//累积桶数据个数
for (int i = 1; i < 10; i++) {
buckets[i]+=buckets[i-1];
}
//根据位数大小排序 放到temp数组中 要保证顺序 按百位排序时是在按十位排行的基础上进行的
for (int i = arr.length-1; i >= 0; i--) {
temp[buckets[arr[i]/exp%10]-1]=arr[i];
buckets[arr[i]/exp%10]--;
}
/* //这种写法是不行的
for (int i = 0; i < arr.length; i++) {
temp[buckets[arr[i]/exp%10]-1]=arr[i];
buckets[arr[i]/exp%10]--;
}*/
//拷贝temp到arr中
System.out.println(Arrays.toString(temp));
for (int i = 0; i < temp.length; i++) {
arr[i]=temp[i];
}
}
}
十一、测试
1、十大排序算法
public class TenSort {
public static void BubbleSort(int[] arr){
int length = arr.length;
for (int i = 1; i < length; i++) {
for (int j = 0; j <= length - 1 - i; j++) {
if(arr[j]>arr[j+1]){
int temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
}
}
public static void QuickSort(int[] arr,int start,int end){
if(start>=end) return;
int i=start,j=end;
int base=arr[start];
//一趟排序
while (i<j){
//从右向左找到第一个比base小的数
while (i<j && arr[j]>=base){
j--;
}
if(i<j) {
swap(arr, i, j);
i++;
}
//从左向右找到第一个比base大的数
while (i<j && arr[i]<=base){
i++;
}
if(i<j){
swap(arr,i,j);
j--;
}
}
QuickSort(arr,start,i-1);
QuickSort(arr,i+1,end);//注意不能取i,不然会导致死循环
}
public static void selectionSort(int[] arr){
int length = arr.length;
for (int i = 0; i <length; i++) {
int min=i;
for (int j = i+1; j < length; j++) {
if(arr[min]>arr[j]){
//记录最小值的索引
min=j;
}
}
//每一轮循环结束交换最小值放到i索引位置
swap(arr,min,i);
}
}
public static void insertion_sort(int[] arr){
// 插入排序(Insertion-Sort)的算法描述是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,
// 找到相应位置并插入。例如要将数组arr=[4,2,8,0,5,1]排序,可以将4看做是一个有序序列,将[2,8,0,5,1]看做一个无序序列。
// 无序序列中2比4小,于是将2插入到4的左边,此时有序序列变成了[2,4],无序序列变成了[8,0,5,1]。
// 无序序列中8比4大,于是将8插入到4的右边,有序序列变成了[2,4,8],无序序列变成了[0,5,1]。以此类推,最终数组按照从小到大排序。该算法的时间复杂度为O(n^2)。
int len=arr.length;
int left;//左边已排序数组的最后一个数的索引
int right;//右边未排序数组的第一个数的索引
int key;//待排序的第一个数
for (int i = 1; i < len; i++) {
left=i-1;
right=i;
key=arr[i];
while (left>=0 && key<arr[left]){
arr[left+1]=arr[left];
left--;
}
arr[left+1]=key;
}
}
public static void shell_sort(int[] arr){
int len = arr.length;
//确定增量
for (int gap=len/2; gap > 0 ; gap/=2) {
for (int i = gap; i < len; i++) {
for (int j = i; j >=gap; j-=gap) {
if(arr[j]<arr[j-gap]){
swap(arr,j-gap,j);
}else {
break;
}
}
}
}
}
public static void MergeSort(int[] arr,int start,int end){
if (start >= end) return;
int mid = (start + end) / 2;
//拆分数组
MergeSort(arr, start, mid);
MergeSort(arr, mid + 1, end);
//合并两个有序数组数组
MergeTwoInSort(arr, start, mid, end);
}
public static void heapSort(int[] arr){
//完全二叉树 某个结点i的左孩子结点为 2*i+1
// 某个结点i的右孩子结点为 2*i+2
// 某个结点i的父亲结点为 (i-1)/2
//建堆 从最后一个非叶子结点(arr.len-1-1)/2=>arr.len/2-1开始建堆
for(int i=arr.length/2-1;i>=0;i--){
Down(arr,i,arr.length-1);
}
//堆排序 交换数组最后一个元素和第一个元素 然后让第一个元素下沉再次调整成大顶堆 重复
for (int i = arr.length-1; i > 0; i--) {
//交换堆顶和最后一个结点的值
swap(arr,0,i);
//让堆顶元素下沉,重新调整到大顶堆结构
Down(arr,0,i-1);
}
}
private static void Down(int[] arr, int i, int maxIdx) {
int max=i;
int left=2*i+1;
int right=2*i+2;
//左孩子比父节点大
if(left<=maxIdx && arr[left]>arr[i]){
max=left;//记录最大值为左孩子索引
}
if(right<=maxIdx && arr[right]>arr[max]){
max=right;//记录最大值为右孩子索引
}
if(max != i){
//最大不再是父节点,需要交换
swap(arr,max,i);
//递归调用down操作,父节点下沉后破坏大顶堆结构
Down(arr,max,maxIdx);
}
}
public static void counting_sort(int[] arr){
//找到数组最大值和最小值
int max=arr[0];
int min=arr[0];
for (int i = 0; i < arr.length; i++) {
if(arr[i]>max) max=arr[i];
if(arr[i]<min) min=arr[i];
}
//创建计数数组
int[] count=new int[max-min+1];
//计数原数组
for (int i = 0; i < arr.length; i++) {
count[arr[i]-min]++;
}
//遍历计数数组,拷贝到原数组中
int k=0;
for (int i = 0; i < count.length; i++) {
while (count[i]>0){
arr[k++]=i+min;
count[i]--;
}
}
}
public static void bucketSort(int[] arr){
//找到数组最大值和最小值
int max=arr[0];
int min=arr[0];
for (int i = 0; i < arr.length; i++) {
if(arr[i]>max) max=arr[i];
if(arr[i]<min) min=arr[i];
}
//确定桶的数量,并创建空桶
int count=(max-min)/arr.length+1;
ArrayList<ArrayList<Integer>> buckets = new ArrayList<>();
for (int i = 0; i < count; i++) {
buckets.add(new ArrayList<Integer>());
}
//把arr元素放到桶里
for (int i = 0; i < arr.length; i++) {
//计算元素放到哪个桶
int idx=(arr[i]-min)/arr.length;
buckets.get(idx).add(arr[i]);
}
//对每个桶进行排序
int k=0;
for (int i = 0; i < count; i++) {
//对每个桶进行排序
Collections.sort(buckets.get(i));
//依次输出桶数据到arr中
for (Integer num : buckets.get(i)) {
arr[k++]=num;
}
}
}
public static void RadixSort(int[] arr){
//找到数组中的最大值
int max=arr[0];
for (int i = 1; i < arr.length; i++) {
if(arr[i]>max) max=arr[i];
}
System.out.println(max);
//依次根据个位,十位,百位...排序
for(int exp=1;max/exp>0;exp*=10){
//创建10个桶
int[] buckets=new int[10];
//创建temp数组
int[] temp=new int[arr.length];
//计算每个桶中数据的个数
for (int i : arr) {
buckets[i/exp%10]++;
}
//累积桶数据个数
for (int i = 1; i < 10; i++) {
buckets[i]+=buckets[i-1];
}
//根据位数大小排序 放到temp数组中 要保证顺序 按百位排序时是在按十位排行的基础上进行的
for (int i = arr.length-1; i >= 0; i--) {
temp[buckets[arr[i]/exp%10]-1]=arr[i];
buckets[arr[i]/exp%10]--;
}
/* //这种写法是不行的
for (int i = 0; i < arr.length; i++) {
temp[buckets[arr[i]/exp%10]-1]=arr[i];
buckets[arr[i]/exp%10]--;
}*/
//拷贝temp到arr中
System.out.println(Arrays.toString(temp));
for (int i = 0; i < temp.length; i++) {
arr[i]=temp[i];
}
}
}
//合并两个有序数组
private static void MergeTwoInSort(int[] arr,int start,int mid,int end){
int left=start,right=mid+1;
int[] temp=new int[end-start+1];
int Idx=0;
while (left<=mid && right<=end){
if(arr[left]<=arr[right]){
temp[Idx++]=arr[left++];
}else {
temp[Idx++]=arr[right++];
}
}
//左边有剩余
while (left<=mid){
temp[Idx++]=arr[left++];
}
//右边有剩余
while (right<=end){
temp[Idx++]=arr[right++];
}
//把临时数组拷贝到arr中
Idx=0;
while (start<=end){
arr[start++]=temp[Idx++];
}
}
//交换数组中两个索引的值
private static void swap(int[] arr, int i, int j) {
int temp=arr[i];
arr[i]=arr[j];
arr[j]=temp;
}
}
2、测试
public class Test {
public static void main(String[] args) {
int[] arr = {3,5,1,-7,4,9,-6,8,10,4};
//冒泡排序
/* TenSort.BubbleSort(arr);
System.out.println(Arrays.toString(arr));*/
//快速排序
/* TenSort.QuickSort(arr,0,arr.length-1);
System.out.println(Arrays.toString(arr));*/
//选择排序
/* TenSort.selectionSort(arr);
System.out.println(Arrays.toString(arr));*/
//插入排序
/* TenSort.insertion_sort(arr);
System.out.println(Arrays.toString(arr));*/
//希尔排序
/* TenSort.shell_sort(arr);
System.out.println(Arrays.toString(arr));*/
//归并排序
/* TenSort.MergeSort(arr,0,arr.length-1);
System.out.println(Arrays.toString(arr));
*/
//堆排序
/* TenSort.heapSort(arr);
System.out.println(Arrays.toString(arr));*/
//计数排序 数组元素不能负数,最大差值太多则性能很差
//int[] arr2 = {3,5,1,7,4};
/* int[] arr2 = {103,115,107,117,104};
TenSort.counting_sort(arr2);
System.out.println(Arrays.toString(arr2));*/
//桶排序 计数排序的升级版
/* int[] arr2 = {103,115,107,117,104,0,66,88,109,3,7};
TenSort.counting_sort(arr2);
System.out.println(Arrays.toString(arr2));*/
//基数排序
int[] arr2 = {103,115,107,117,104,0,66,88,109,3,7};
TenSort.RadixSort(arr2);
System.out.println(Arrays.toString(arr2));
/* Arrays.sort(arr);
System.out.println(Arrays.toString(arr));*/
}
}