分类:
1)插入排序(直接插入排序、希尔排序)
2)交换排序(冒泡排序、快速排序)
3)选择排序(直接选择排序、堆排序)
4)归并排序
5)分配排序(基数排序)
所需辅助空间最多:归并排序
所需辅助空间最少:堆排序
平均速度最快:快速排序
不稳定:快速排序,希尔排序,堆排序。
先来看看8种排序之间的关系:
1、插入排序
基本思想:
在要排序的一组数中,假设前面(n-1)[n>=2] 个数已经是排好顺序的,
现在要把第n个数插到前面的有序数中,使得这n个数也是排好顺序的。
如此反复循环,直到全部排好顺序。
JAVA实现:
/**
* 插入排序
* 排序原理:
* 当数组长度为1时,直接返回数组,当数组长度大于1时, 每次取数组中的一
* 个数跟第一个数进行比较,如果大于它就插在右边,
* 小于它就插在左边,就跟打扑克一样,它前面的数组等于是已经排序了的。
* @param arrays
* @param len
* @return
*/
public static int[] chaRuSort(int[] arrays,int len) {
int middle = 0;
if (len == 0 || len == 1)
return arrays;
for (int i = 1; i < len;i++) {// 从第二个数据开始插入
if (arrays[i] <arrays[i - 1]) {// 如果需要排序的数字比前面的最大数据要小,则表示它需要放到前面去
middle =arrays[i];// 存储需要插入的数组
for (int j =i - 1; j >= 0; j--) {// 从右边开始比较,当它比某个数大时,则表示他需要插入这个位置,同时每次循环的数字都往后挪一位
if(arrays[j] < middle) {
arrays[j+ 1] = middle;
break;//以插入就可以跳出循环
}
arrays[j+ 1] = arrays[j];
if(j == 0)
arrays[0]= middle;// 但如果它一直没有跳出循环,则直接把它插入第一个位置,但必须要在第一个数字已换位置之后
}
}
}
return arrays;
}
2、希尔排序
基本思想:
算法先将要排序的一组数按某个增量d(n/2,n为要排序数的个数)分成若干组,
每组中记录的下标相差d.对每组中全部元素进行直接插入排序,然后再用一个较小
的增量(d/2)对它进行分组,在每组中再进行直接插入排序。当增量减到1时,进行直接插入排序后,排序完成。
JAVA实现:
/**
* 希尔排序 排序原理:
* 先把数组分为part份,
* 之后对每份都进行插入排序
* @param arrays
* @param len
* @param part,分多少份
* @return
*/
public static int[] xiErSort(int[]arrays, int len, int part) {
for (int h = len / part; h >0; h /= part) {
for (int i = h; i <len; i++) {
for (int j =i - h; j >= 0; j -= h) {
if(arrays[j] > arrays[j + h]) {
inttemp = arrays[j];
arrays[j]= arrays[j + h];
arrays[j+ h] = temp;
}
}
}
}
return arrays;
}
3、选择排序
基本思想
在要排序的一组数中,选出最小的一个数与第一个位置的数交换;
然后在剩下的数当中再找最小的与第二个位置的数交换,如此循环到倒数第二个数和最后一个数比较为止。
JAVA实现:
/**
* 直接选择排序
* 排序原理:
* 第一次从R[0]~R[n-1]中选取最小值,与R[0]交换,
* 第二次从R[1]~R[n-1]中选取最小值,与R[1]交换,
* …., 第i次从R[i-1]~R[n-1]中选取最小值,
* 与R[i-1]交换, …..,
* 第n-1次从R[n-2]~R[n-1]中选取最小值,与R[n-2]交换,
* 共通过n-1次,得到一个从小到大排列的有序序列。
* @param arrays
* @param len
* @return
*/
public static int[] xuanZeSort(int[]arrays, int len) {
int middle = 0, minIndex = 0;
for (int i = 0; i < len;i++) {
minIndex = i;
for (int j = i + 1; j< len; j++) {// 从i+1开始比较,如果比需要比较的arrays[middle]小,前面已经排序,当找到比它小的,其余的数就跟这个更小的比较,直到找到最小值。
if(arrays[minIndex] > arrays[j])
minIndex= j;// 把比较索引只想更小的那个数的索引。
}
if (i != minIndex) {//如果最小值下标进行了改变,则说明数组有比前面最大值更小的值,需要还位置。
middle =arrays[i];
arrays[i] =arrays[minIndex];
arrays[minIndex]= middle;
}
}
return arrays;
}
4、堆排序
基本思想:
堆排序是一种树形选择排序,是对直接选择排序的有效改进。堆的定义如下:具有n个元素的序列(h1,h2,…,hn),当且仅当满足(hi>=h2i,hi>=2i+1)或(hi<=h2i,hi<=2i+1)(i=1,2,…,n/2)时称之为堆。在这里只讨论满足前者条件的堆。由堆的定义可以看出,堆顶元素(即第一个元素)必为最大项(大顶堆)。
完全二叉树可以很直观地表示堆的结构。堆顶为根,其它为左子树、右子树。初始时把要排序的数的序列看作是一棵顺序存储的二叉树,调整它们的存储序,使之成为一个堆,这时堆的根节点的数最大。然后将根节点与堆的最后一个节点交换。然后对前面(n-1)个数重新调整使之成为堆。依此类推,直到只有两个节点的堆,并对它们作交换,最后得到有n个节点的有序序列。从算法描述来看,堆排序需要两个过程,一是建立堆,二是堆顶与堆的最后一个元素交换位置。所以堆排序有两个函数组成。一是建堆的渗透函数,二是反复调用渗透函数实现排序的函数。
交换,从堆中踢出最大数
剩余结点再建堆,再交换踢出最大数
依次类推:最后堆中剩余的最后两个结点交换,踢出一个,排序完成。
JAVA实现:
publicvoid heapSort(int[] a){
System.out.println("开始排序");
int arrayLength=a.length;
//循环建堆
for(int i=0;i<arrayLength-1;i++){
//建堆
buildMaxHeap(a,arrayLength-1-i);
//交换堆顶和最后一个元素
swap(a,0,arrayLength-1-i);
System.out.println(Arrays.toString(a));
}
}
private void swap(int[] data, int i, int j){
// TODO Auto-generated method stub
int tmp=data[i];
data[i]=data[j];
data[j]=tmp;
}
//对data数组从0到lastIndex建大顶堆
private void buildMaxHeap(int[] data, intlastIndex) {
// TODO Auto-generated method stub
//从lastIndex处节点(最后一个节点)的父节点开始
for(int i=(lastIndex-1)/2;i>=0;i--){
//k保存正在判断的节点
int k=i;
//如果当前k节点的子节点存在
while(k*2+1<=lastIndex){
//k节点的左子节点的索引
int biggerIndex=2*k+1;
//如果biggerIndex小于lastIndex,即biggerIndex+1代表的k节点的右子节点存在
if(biggerIndex<lastIndex){
//若果右子节点的值较大
if(data[biggerIndex]<data[biggerIndex+1]){
//biggerIndex总是记录较大子节点的索引
biggerIndex++;
}
}
//如果k节点的值小于其较大的子节点的值
if(data[k]<data[biggerIndex]){
//交换他们
swap(data,k,biggerIndex);
//将biggerIndex赋予k,开始while循环的下一次循环,重新保证k节点的值大于其左右子节点的值
k=biggerIndex;
}else{
break;
}
}
}
}
5、冒泡排序
基本思想:
在要排序的一组数中,对当前还未排好序的范围内的全部数,自上而下对相邻的两个数依次进行比较和调整,让较大的数往下沉,较小的往上冒。即:每当两相邻的数比较后发现它们的排序与排序要求相反时,就将它们互换。
JAVA实现
/**
* 冒泡排序
* 排序原理:从待排序序列的起始位置开始,从前往后依次比较各个位置和其后一位置的大小并执行。
* 如果当前位置的值大于其后一位置的值,就把他俩的值交换(完成一次全序列比较后,序列最后位
* 置的值即此序列最大值,所以其不需要再参与冒泡)。将序列的最后位置从待排序序列中移除。
* 若移除后的待排序序列不为空则继续执行,否则冒泡结束。
* @param arrays,将排序的数组
* @param len,数组长度
* @return,排序后的数组
*/
private static int[] maoPaoSort(int[]arrays, int len) {
int middle = 0;
for (int i = 0; i < len - 1;i++) {
for (int j = 0; j <len - i - 1; j++) {
if (arrays[i]> arrays[j]) {// 把前一个比较数据大于后面一个数据的放到后面,从小到大排序
middle= arrays[i];
arrays[i]= arrays[j];
arrays[j]= middle;
}
}
}
return arrays;
}
/**
* 冒泡排序优化:
* 排序原理:
* 当数据位置不在互换时,则表示前面的数据都大于后面的数据,排序已完成
* @param arrays,将排序的数组
* @param len,数组长度
* @return,排序后的数组
*/
public static int[]maoPaoYouHuaSort(int[] arrays, int len) {
int middle = 0;
boolean flag = true;
while (flag) {
flag = false;// 如果没有交换,则flag=false,则跳出循环
for (int i = 0; i <len - 1; i++) {
if (arrays[i]> arrays[i + 1]) {// 把前一个比较数据大于后面一个数据的放到后面,从小到大排序
middle= arrays[i];
arrays[i]= arrays[i + 1];
arrays[i+ 1] = middle;
flag= true;
}
}
len--;// 去除最后一个排序正确的
}
return arrays;
}
6、快速排序
基本思想:
选择一个基准元素,通常选择第一个元素或者最后一个元素,通过一趟扫描,将待排序列分成两部分,一部分比基准元素小,一部分大于等于基准元素,此时基准元素在其排好序后的正确位置,然后再用同样的方法递归地排序划分的两部分。
JAVA实现:
/**
*快速排序:
*排序原理:
*把一个需要排序的数组分成2份, 之后取数组的left值进行比较,在左边找到比数组下
*标left大的就换位置,在右边找到比数组下标left 小的就换位置,直到数组有一个值的左边都比它小,右
*边都比它大,也就是i和j相等的时候就完成第一次排序, 之后递归对它左右2边的数组进行排序,直到数组不再出
*现i<j,也就是数组只有一个元素才结束。
*@param arrays,将排序的数组
*@param left,数组的左边起始位置,0
*@param right,数组的右边起始位置,arrays.length-1
*@return,排序后的数组
*/
private static int[] kuaiSuSort(int[]arrays, int left, int right) {
int i = left, j = right, key =arrays[left];
while (i < j) {// 只有当i=j时,才完成第一次排序,
while (i < j&& arrays[j] >= key)
// 循环查询,从右边开始比较,比key大就就说明这个值本身就应该放在右边,不用换位置,之后继续往前查询,但查到右边第二个值比左边的小时,跳出循环。
j--;
if (i < j) {// 跳出了循环,如果i还是大于j,则表示查询到的小于或等于key的数字在key的右边,需要互换位置,把arrays[i]的值与arrays[j]互换。
arrays[i] =arrays[j];// arrays[i]的值已经保存在了key里面,只是这里没有赋值,到后面赋值。
i++;
}
while (i < j&& arrays[i] <= key)
// 循环查询,从左边开始比较,比key小就就说明这个值本身就应该放在左边,不用换位置,之后继续往前查询,但查到某个值比不小于key,跳出循环。
i++;
if (i < j) {// 跳出了循环,如果i还是大于j,则表示查询到的小于或等于key的数字在key的左边,需要互换位置,把arrays[i]的值与arrays[j]互换
arrays[j] =arrays[i];// arrays[j]的值已经保存在了arrays[i]里面,只是这里没有赋值,到后面赋值。
j--;
}
arrays[i] = key;
kuaiSuSort(arrays, left,i - 1);
kuaiSuSort(arrays, j + 1,right);
}
return arrays;
}
7、归并排序
基本排序:
归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。
JAVA实现:
/**
*归并排序 排序原理:把排序数组分成2份,
*新建一个临时数组,存放排序后的数组,且这两个数组已经排序了的,之后比较这两个数组的初始值,小的放入临时数组
*,之后把它和临时数组的索引+1,最后把还有数据的数组数字直接放在最后
*,前提条件,(两个数组已经排序),但在数组中都只有一个数字的时候才能说数组已经进行了排序。
*@param arrays
*@param ones,第一个数组的起始位置
*@param twos,第二个数组的起始位置,twos-1也即第一个数组的终止位置。
*@param twot,第二个数组的终止位置
*@return
*/
public static int[] guiBinSort(int[]arrays, int ones, int twos, int twot) {
int[] tem = newint[arrays.length];
int i = ones, j = twos, k = 0;
while (i < twos && j<= twot) {// 当i,j还在数组里面,则表示2个数组都还有值,否则就有一个数组里面已经没有数字
if (arrays[i] <=arrays[j]) {// 当arrays[i]小一些,就把它放入临时数组,临时数组和第一个数组的下标加1
tem[k] =arrays[i];
i++;
} else {
tem[k] =arrays[j];
j++;
}
k++;
}
while (i < twos) {// 表示第一个数组里面还有数字
tem[k] = arrays[i];
i++;
k++;
}
while (j <= twot) {// 表示第一个数组里面还有数字
tem[k] = arrays[j];
j++;
k++;
}
for (int g = 0; g < k; g++) {//把已排序的内容重新替换排序的数据
arrays[g] = tem[g];
}
return arrays;
}
/**
*只有单个元素时,数组才能说时有序的
*
*@param arrays
*@param len
*@return
*/
public static int[] guiBinSort(int[]arrays, int len) {
for (int i = 1; i < len; i++){
guiBinSort(arrays, 0, i,i);// 归并排序arrays[0-0]和arrays[1-1],这个就排序了
}
/*
* int i=1; while(i<len){
* guiBinSort(arrays,0,i,i);//归并排序arrays[0-0]和arrays[1-1],这个就排序了
* i++;//递加1就变成了归并归并排序arrays[0-1]和arrays[2-2],一直递归 }
*/
return arrays;
}
8、基数排序
基本思想:
将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列。
JAVA实现
/**
*基数排序
*实现原理:将所有待比较数值(正整数)统一为同样的数位长度,
*数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。
*这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。
*@param arrays
*@param d,最大有几位数
*@return
*/
public static int[] jiShuSort(int[]arrays, int d) {
int k = 0;
int n = 1;
int m = 1; // 控制键值排序依据在哪一位
int[][] temp = newint[10][arrays.length]; // 数组的第一维表示可能的余数0-9
int[] order = new int[10]; // 数组orderp[i]用来表示该位是i的数的个数
while (m <= d) {
for (int i = 0; i <arrays.length; i++) {
int lsd =((arrays[i] / n) % 10);
temp[lsd][order[lsd]]= arrays[i];
order[lsd]++;
}
for (int i = 0; i <10; i++) {
if (order[i] !=0)
for (int j= 0; j < order[i]; j++) {
arrays[k] = temp[i][j];
k++;
}
order[i] = 0;
}
n *= 10;
k = 0;
m++;
}
return arrays;
}
/**
*变种的基数排序,
*把排序数组中的所有数字分成d份 必须都是正数,
*每个数在什么位置就放入那个数组中,
*之后对每个数组进行排序,在合并
*@param arrays
*@param len
*@param d,最大数据有多少位
*@return
*/
public static int[] jiShuSort(int[]arrays, int len, int d) {
int n = 0;// 临时存储arrays[i]的值
int tem = 0;// 临时存储位数值
int k = 0; // 从新排序索引
int min = 0, max = 0;// 临时存储最大值和最小值
int[][] temp = new int[d][len];// 数组的第一维表示可能的余数0-9
for (int j = d; j >= 0; j--) {
for (int i = 0; i <len; i++) { // 把数值分配到指定的类中
n = arrays[i];
min = (int) Math.pow(10,j - 1);
max = (int)Math.pow(10, j);
if (min <= n&& n < max)
temp[j -1][i] = n;
}
}
for (int i = 0; i < d; i++) {
temp[i] =guiBinSort(temp[i], len);// 每一个数组再次进行排序
for (int j = 0; j <len; j++) {// 之后把排序之后的每个数组重新放入数组中
if(temp[i][j] != 0) {
arrays[k]= temp[i][j];
k++;
}
}
}
return arrays;
}