1 冒泡排序
冒泡排序的算法比较简单,每一轮遍历数列,将数列中最大(或最小)的元素放到数列的头部(或尾部),这个最大(或最小)元素就是本轮的冒泡元素,冒泡元素的位置应该在数列的"头部+n"(或"尾部-n")的位置,n表示遍历的轮次。在下图的描述中,我们将数列中元素最大的元素在尾部"冒泡"。
在下面的程序实现中,我们将数组中每轮找到的最大元素"冒泡"在数组的尾部。具体算法实现如下面示例所示。
public class BubbleSort {
//打印数组
public static void printArray(int[] array) {
int length=array.length;
for(int i=0;i<length;i++) {
System.out.print(array[i]);
System.out.print(i!=length-1?",":"\n");
}
}
//冒泡排序
public static void sort(int[] array) {
for(int i=0;i<array.length;i++) {//外层循环,记录排序的轮次
/** 内存循环用于俩俩比较,从数组第一个元素比较到数组长度减i个轮次.
* 第一轮从1到length-0,第二轮从1到length-1,依次类推*/
for(int j=1;j<array.length-i;j++) {
//当前一元素大于后一个元素,交换二者的位置。实现大元素后移
if(array[j-1]>array[j]) {
int temp=array[j-1];
array[j-1]=array[j];
array[j]=temp;
}
}
System.out.print("第"+(i+1)+"轮结束后,数组的内容为:");
printArray(array);
}
}
public static void main(String[] args) {
int[] array= {1,4,3,-100,323,53,13,554,123,20,43,12};
sort(array);
}
}
示例运行结果:
第1轮结束后,数组的内容为:1,3,-100,4,53,13,323,123,20,43,12,554
第2轮结束后,数组的内容为:1,-100,3,4,13,53,123,20,43,12,323,554
第3轮结束后,数组的内容为:-100,1,3,4,13,53,20,43,12,123,323,554
第4轮结束后,数组的内容为:-100,1,3,4,13,20,43,12,53,123,323,554
第5轮结束后,数组的内容为:-100,1,3,4,13,20,12,43,53,123,323,554
第6轮结束后,数组的内容为:-100,1,3,4,13,12,20,43,53,123,323,554
第7轮结束后,数组的内容为:-100,1,3,4,12,13,20,43,53,123,323,554
第8轮结束后,数组的内容为:-100,1,3,4,12,13,20,43,53,123,323,554
第9轮结束后,数组的内容为:-100,1,3,4,12,13,20,43,53,123,323,554
第10轮结束后,数组的内容为:-100,1,3,4,12,13,20,43,53,123,323,554
第11轮结束后,数组的内容为:-100,1,3,4,12,13,20,43,53,123,323,554
第12轮结束后,数组的内容为:-100,1,3,4,12,13,20,43,53,123,323,554
2 插入排序
插入算法比较直观,它的思想是从前端构建有序数列,后端未排序的元素和向前端有序数列中的元素从后向前依次比较,如果比前面元素大,该元素位置不变,继续排序下一个未排序元素。如果比前面元素小,将该元素插入到比较元素之前,然后继续比较有序数列前一个元素,直到当前元素插入到有序数列合适位置。插入算法图解如下图所示。
采用插入算法排序时,我们的程序会将数组分为两段,前一段是经过排序的有序数列,我们记为a0、a1、a2……ai。未排序部分为ai+1、ai+2……。在排序的时,用ai+1和ai比较,如果ai+1大于ai,ai+1排序完成,进行ai+2的排序。如果小于ai,将ai+1插入到ai的前面,在去比较ai-1,直到ai+1找好合适位置。具体算法实现如下面示例所示。
public class InsertionSort {
//打印数组
public static void printArray(int[] array) {
int length=array.length;
for(int i=0;i<length;i++) {
System.out.print(array[i]);
System.out.print(i!=length-1?",":"\n");
}
}
//冒泡排序
public static void sort(int[] array) {
//未排序数列起始位置开始排序,i为未排序数列的起点索引
for(int i = 1;i<array.length;i++) {
/**
* 第一个未排序元素array[j]与有序数列最后一个元素<array[j-1]比较,
* 如果array[j]大,排序完成,外层循环继续。
* 如果array[j]小,交换array[j]和array[j-1]位置,在比较前一个有序元素。
*/
for(int j=i;j>0 && array[j]<array[j-1];j--) {
int temp=array[j];
array[j]=array[j-1];
array[j-1]=temp;
}
System.out.print("第 "+i+"轮插入排序后的结果:");
printArray(array);
}
}
public static void main(String[] args) {
int[] array= {99,4,3,-100,323,53,13,554,123,20,43,12};
sort(array);
}
}
示例运行结果:
第 1轮插入排序后的结果:4,99,3,-100,323,53,13,554,123,20,43,12
第 2轮插入排序后的结果:3,4,99,-100,323,53,13,554,123,20,43,12
第 3轮插入排序后的结果:-100,3,4,99,323,53,13,554,123,20,43,12
第 4轮插入排序后的结果:-100,3,4,99,323,53,13,554,123,20,43,12
第 5轮插入排序后的结果:-100,3,4,53,99,323,13,554,123,20,43,12
第 6轮插入排序后的结果:-100,3,4,13,53,99,323,554,123,20,43,12
第 7轮插入排序后的结果:-100,3,4,13,53,99,323,554,123,20,43,12
第 8轮插入排序后的结果:-100,3,4,13,53,99,123,323,554,20,43,12
第 9轮插入排序后的结果:-100,3,4,13,20,53,99,123,323,554,43,12
第 10轮插入排序后的结果:-100,3,4,13,20,43,53,99,123,323,554,12
第 11轮插入排序后的结果:-100,3,4,12,13,20,43,53,99,123,323,554
3 选择排序
选择排序需要定义额外的变量来保存元素信息。采用选择排序,我们需要定义一个变量minIndex用于保存数组中最小元素的索引。在第一轮遍历过程中,minIndex始终存储数组中数值最小元素的索引,在第一轮遍历结束后,将数组中第一个元素与minIndex位置的元素交换位置,第二轮遍历过程中,minIndex始终存储数组数值第二小的元素索引,遍历结束后将minIndex索引处的元素放置数组第二的位置上,以此类推完成所有排序。选择排序图解如下图所示。
使用选择排序需要使用额外的存储空间,在实现选择排序时,我们要保证每一轮将数组中元素的最小元素放置到数组的头部,依次进行排序。具体算法实现如下面示例所示。
public class SelectionSort {
//打印数组
public static void printArray(int[] array) {
int length=array.length;
for(int i=0;i<length;i++) {
System.out.print(array[i]);
System.out.print(i!=length-1?",":"\n");
}
}
public static void sort(int[] array) {
//排序的轮次,i记录未排序数列的起始索引位置
for(int i=0;i<array.length;i++) {
//minIndex记录未排序元素的最小值的索引,默认未排序数列第一元素为最小
int minIndex=i;
//从未排序数列第二个元素开始于当前最小元素做遍历比较
for(int j=i+1;j<array.length;j++) {
//如果有元素比当前最小元素小,修改minIndex保存的索引值
if(array[j]<array[minIndex]) {
minIndex=j;
}
}
//将每一轮最小元素放置到未排序数列的首位
int temp=array[minIndex];
array[minIndex]=array[i];
array[i]=temp;
System.out.print("第"+(i+1)+"次排序后的结果:");
printArray(array);
}
}
public static void main(String[] args) {
int[] array= {99,4,3,-100,323,53,13,554,123,20,43,12};
sort(array);
}
}
示例运行结果:
第1次排序后的结果:-100,4,3,99,323,53,13,554,123,20,43,12
第2次排序后的结果:-100,3,4,99,323,53,13,554,123,20,43,12
第3次排序后的结果:-100,3,4,99,323,53,13,554,123,20,43,12
第4次排序后的结果:-100,3,4,12,323,53,13,554,123,20,43,99
第5次排序后的结果:-100,3,4,12,13,53,323,554,123,20,43,99
第6次排序后的结果:-100,3,4,12,13,20,323,554,123,53,43,99
第7次排序后的结果:-100,3,4,12,13,20,43,554,123,53,323,99
第8次排序后的结果:-100,3,4,12,13,20,43,53,123,554,323,99
第9次排序后的结果:-100,3,4,12,13,20,43,53,99,554,323,123
第10次排序后的结果:-100,3,4,12,13,20,43,53,99,123,323,554
第11次排序后的结果:-100,3,4,12,13,20,43,53,99,123,323,554
第12次排序后的结果:-100,3,4,12,13,20,43,53,99,123,323,554
4 基数排序
前面几种排序算法总体上来说都是比较排序,即在排序的过程中需要进行比较,而基数排序是不需要比较的。基数排序是先从元素的低位开始排序,相同数值的收集在一起,然后再往高位依次进行排序收集,直至最高位被排序完毕,收集后的元素序列就是一个有序数列。下面一系列图片中描述了基数排序的过程。
当个位排序完成后,收集的时候从0到9数值位依次收集数据,收集后的数列供十位排序使用。
在十位排序的时候,部分元素在十位上没有数值,这些元素的十位数值都按0处理。
由于数列中最大的数只有三位,所以当百位排序完成后,基数排序完成。收集后的数列就是一个从小到大的有序数列。代码实现如下所示:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
public class RadixSort {
//打印数组
public static void printArray(int[] array) {
int length=array.length;
for(int i=0;i<length;i++) {
System.out.print(array[i]);
System.out.print(i!=length-1?",":"\n");
}
}
//按基数排序,注意这里只能排序正整数。
public static void sort(int[] array) {
//找到数组中最大数max,并计算它的位数maxDig
int max=array[0];
for(int maxTemp:array) {
if(maxTemp>max) max=maxTemp;
}
int maxDig=String.valueOf(max).length();
//创建从0~9的基数数据集
Map<Integer,List<Integer>> map=new HashMap<>();
for(int i=0;i<10;i++) {
map.put(i, new ArrayList<Integer>());
}
//从低位baseDig开始到高位maxDig进行指定位的基数排序
int baseDig=1;
while(baseDig<=maxDig) {
System.out.println("####开始从低位的第"+baseDig
+"位开始基数排序####");
//第一阶段,遍历目标数组,将数组元素放入指定的基数数集中
for(int temp:array) {
String strVal=String.valueOf(temp);
//获取元素baseDig位的数字,放入相对的基数数据集中。
//如果不存在,将该元素放入0基数数据集中。
Integer key=null;
if(strVal.length()<baseDig) {
key=0;
}else {
//获取指定位上的字符,并转化成目标基数key。
char bitNum=strVal.charAt(strVal.length()-baseDig);
key=Integer.valueOf(String.valueOf(bitNum));
}
map.get(key).add(temp);
}
int j=0;
System.out.println("从低位起,第"+baseDig
+"位基数排序后,各基数数据集的结果:");
//第二阶段,将0~9基数数据集中的数据,放回到数组中,
//表示当前baseDig位的基数排序完成
for(int i=0;i<10;i++) {
if(map.get(i).size()>=1) {
System.out.println(i+":"+map.get(i));
}
Iterator<Integer> it=map.get(i).iterator();
while(it.hasNext()) {
array[j++]=it.next();
}
map.get(i).clear();
}
System.out.print("第"+baseDig
+"位基数排序完成后,目标数组的结果:");
printArray(array);
baseDig++;
}
}
public static void main(String[] args) {
//要排序的目标数组
int[] array= {1,4,3,100,323,53,13,559230,123,20,43,12};
sort(array);
}
}
示例运行结果:
####开始从低位的第1位开始基数排序#####
从低位起,第1位基数排序后,各基数数据集的结果:
0:[100, 559230, 20]
1:[1]
2:[12]
3:[3, 323, 53, 13, 123, 43]
4:[4]
第1位基数排序完成后,目标数组的结果:100,559230,20,1,12,3,323,53,13,123,43,4
####开始从低位的第2位开始基数排序#####
从低位起,第2位基数排序后,各基数数据集的结果:
0:[100, 1, 3, 4]
1:[12, 13]
2:[20, 323, 123]
3:[559230]
4:[43]
5:[53]
第2位基数排序完成后,目标数组的结果:100,1,3,4,12,13,20,323,123,559230,43,53
####开始从低位的第3位开始基数排序#####
从低位起,第3位基数排序后,各基数数据集的结果:
0:[1, 3, 4, 12, 13, 20, 43, 53]
1:[100, 123]
2:[559230]
3:[323]
第3位基数排序完成后,目标数组的结果:1,3,4,12,13,20,43,53,100,123,559230,323
####开始从低位的第4位开始基数排序#####
从低位起,第4位基数排序后,各基数数据集的结果:
0:[1, 3, 4, 12, 13, 20, 43, 53, 100, 123, 323]
9:[559230]
第4位基数排序完成后,目标数组的结果:1,3,4,12,13,20,43,53,100,123,323,559230
####开始从低位的第5位开始基数排序#####
从低位起,第5位基数排序后,各基数数据集的结果:
0:[1, 3, 4, 12, 13, 20, 43, 53, 100, 123, 323]
5:[559230]
第5位基数排序完成后,目标数组的结果:1,3,4,12,13,20,43,53,100,123,323,559230
####开始从低位的第6位开始基数排序#####
从低位起,第6位基数排序后,各基数数据集的结果:
0:[1, 3, 4, 12, 13, 20, 43, 53, 100, 123, 323]
5:[559230]
第6位基数排序完成后,目标数组的结果:1,3,4,12,13,20,43,53,100,123,323,559230