2.冒泡排序
/**
* 冒泡排序
* 思路:相邻两两比较,不断的将整个数组中最大的数找出来,确定在最后。
* @param test
* @return
*/
public static int[] bubblesort(int []test){
int []arr=Arrays.copyOf(test, test.length);
int len=arr.length;
//外层循环:每次冒泡都能找到当前最大的数,确定最大数的位置,所以总共需要循环len-1次
for(int i=1;iarr[j+1]){
int tmp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=tmp;
}
}
}
return arr;
}
/**
* 增强冒泡排序
* 思路:在冒泡排序的基础上,若某一次内层循环未发生交换,即表示整体有序,提前退出
* @param test
* @return
*/
public static int[] bubblesort_pro(int []test){
int []arr=Arrays.copyOf(test, test.length);
int len=arr.length;
boolean flag=false; //内层是否发生交换操作
//外层循环:每次冒泡都能找到当前最大的数,确定最大数的位置,所以总共需要循环len-1次
for(int i=1;iarr[j+1]){
int tmp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=tmp;
flag=true;
}
}
//判断是否需要进行下一次循环
if(flag==false){
break; //提前退出
}
else{
flag=false; //重置flag,进行下一次循环
}
}
return arr;
}
3.选择排序
/**
* 选择排序
* 思路:进行n-1次循环,第i次循环将数组中(除去已排序好的元素)最小的数找出来,放在第i位,
* @param test
* @return
*/
public static int[] selectsort(int []test){
int []arr=Arrays.copyOf(test, test.length);
int len=arr.length;
/*//实现方法一:频繁交换,只要arr[j]arr[j]){
int tmp=arr[i];
arr[i]=arr[j];
arr[j]=tmp;
}
}
}*/
//实现方法二:先遍历找到最小的数和索引。arr[i]
//eg.遍历一次得到arr[j]最小 则将arr[i]的arr[j]内容交换
for(int i=0;i
4.插入排序
/**
* 插入排序
* 思路:抽取一个元素从后向前与已排好序的元素进行比较,直到找到正确的插入位置
* 前面的元素是已排好序了,从后向前比较,这时若该元素小于之前的元素,则将元素位置调换,重复比较,直至找到正确的位置。
* @param test
* @return
*/
public static int[] insertsort(int []test){
int []arr=Arrays.copyOf(test, test.length);
int len=arr.length;
//对于数组中的每一个元素,从后向前与已排好序的元素进行比较,直到找到正确的插入位置
for(int i=1;i=0&&tmp
5.希尔排序
/**
* 希尔排序(改进版的插入排序)
* 希尔排序的基本思想是:先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,(预处理)
* 待整个序列中的记录“基本有序”时,再对全体记录进行依次直接插入排序。
*
* 为什么需要希尔排序?(为什么需要预处理?)
* eg.考虑该情况(*,*,*,*,*,*,*,*,*,1)
* 插入排序:最后一位1需要 向前比较9次
* 希尔排序:最后一位1在预处理时就会向前移动(9->4->2->0,比较3次),
* 真正插入排序时,不需要向前比较
* 结论:当序列长度较长时,“基本有序”可大大减少直接插入的效率,预处理很有必要
* @param test
* @return
*/
public static int[] shellsort(int []test){
int []arr=Arrays.copyOf(test, test.length);
int len=arr.length;
/*关于希尔排序increment(增量)的取法:增量increment的取法有各种方案。
最初shell提出取increment=n/2向下取整,increment=increment/2向下取整,直到increment=1。
但由于直到最后一步,在奇数位置的元素才会与偶数位置的元素进行比较,这样使用这个序列的效率会很低。
后来Knuth提出取increment=n/3向下取整+1.*/
int gap=len/2; //步长
//int gap=len/3+1;
while(gap>0){
//步长为gap的插入排序
//第一步:步长为5【0,5】【1,6】【2,7】【3,8】【4,9】分别有序===========>步长为5的插入排序
//第二步:步长为2【0,2,4,6,8】【1,3,5,7,9】分别有序================>步长为2的插入排序
//第三步:步长为1【0,1,2,3,4,5,6,7,8,9】有序====================>步长为1的插入排序(就是插入排序)
//第四步:步长为0,完成,退出
for(int i=gap;i=0&&tmp
6.归并排序
/**
* 归并排序
* 分治思想:先分再治
* @param test
* @return
*/
public static int[] mergesort(int []test){
int len=test.length;
if(len<2){
return test;
}
//分:用mid将数组分为left和right(二路归并)
int mid=len/2;
int []left=Arrays.copyOfRange(test, 0, mid); //数组左部分
int []right=Arrays.copyOfRange(test, mid, len); //数组右部分
return merge(mergesort(left),mergesort(right));
}
//治:将left[]数组和right[]数组进行合并排序
// left[]和right[]已经有序,此时问题等价为给定两个有序的数组,合并使得其整体有序
public static int[] merge(int[] left, int[] right) {
int l_len=left.length;
int r_len=right.length;
int []new_arr=new int [l_len+r_len];
int l_index=0;
int r_index=0;
int n_index=0;
//从left数组和right数组的首部开始遍历,哪个小就放入new_arr数组中,然后继续,
//直至某个数组到达尾部,跳出循环
while(l_index!=l_len&&r_index!=r_len){
if(left[l_index]
7.快速排序
/**
* 快速排序(递归)
* 算法步骤:从数列中挑出一个元素,称为 “基准”(pivot),选用数组的第一个元素;
* 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。
* 在这个分区(partition)操作退出之后,该基准就处于数列的中间位置。
* 递归地(recursive)把小于基准值元素的子数列【left,pivot-1】和大于基准值元素的子数列【pivot+1,right】排序;
* @param test
* @return
*/
public static int[] quicksort(int []test){
int []arr=Arrays.copyOf(test, test.length);
arr=quicksort(arr,0,test.length-1);
return arr;
}
//定义快速排序递归内容
public static int[] quicksort(int []arr,int left,int right){
if(leftpivot)
arr[i]=arr[pivot];//将大于基准值的数组元素放在基准值之后 (此时arr[pivot]的位置可写入)
}
}
arr[pivot]=tmp;
return pivot;
}
8.堆排序
/**
* 堆排序
* 最大堆:根节点总是大于,左右孩子节点
* 最大堆调整:使得堆满足最大堆所做的调整
* 堆排序步骤:
* 1.最大堆调整后,将堆顶元素与末尾元素进行交换,最后的叶子节点即为最大值,位置确定,该节点不再考虑
* 2.将剩余的结点重复进行1步骤,直至结束
* 我的理解:堆排序是特殊的选择排序
*
* 为了简化操作:可用数组映射为堆结构,索引为i的结点的子节点索引为2i+1,2i+2
* @param test
* @return
*/
public static int[] heapsort(int []test){
int []arr=Arrays.copyOf(test, test.length);
int len=arr.length;
while(len>1){
//从第一个非叶子节点出发(最后一个根出发)由后向前,进行堆调整
for(int i=len/2-1;i>=0;i--){
heapadjust(arr, i, len);
}
//在得到最大堆后,将堆顶元素与末尾元素进行交换,
int max=arr[0];
arr[0]=arr[len-1];
arr[len-1]=max;
//最大元素放在末尾,不再考虑和调整,len-1,
len--;
}
return arr;
}
//堆调整(根 ,左孩子,右孩子,保证根孩子是三个数中最大的)
public static int[] heapadjust(int []arr,int i,int len){
//如果存在左孩子且大于根,根与左孩子替换
if((2*i+1)arr[i]){
int r1=arr[i];
arr[i]=arr[2*i+1];
arr[2*i+1]=r1;
}
//如果存在右孩子且大于根,根与右孩子替换
if((2*i+2)arr[i]){
int r1=arr[i];
arr[i]=arr[2*i+2];
arr[2*i+2]=r1;
}
return arr;
}
9.记数排序+桶排序
/**
* 记数排序+桶排序
* 思想:空间换时间
* 问题:将班级中的人的分数由从高到低排列(分数:0-100,整数,人数:50人)?
* 分数0-100,可看成101个桶(数组表示),将分数放到对应的桶中,然后根据桶遍历输出。
* eg gradeBucket[100]=1:表示考100分的有1个人
* @return
*/
public static void bucketsort(){
//将50人的分数初始化
int []gradeTable=new int[50];
for(int i=0;i<50;i++){
gradeTable[i]=(int)(100*Math.random());
}
//将50个人的分数放到对应的桶中
int []gradeBucket=new int[101];
for(int grade:gradeTable){
gradeBucket[grade]++; //对应的桶记数+1
}
//按从大到小的顺序输出
for(int grade=100;grade>=0;grade--){
for(int count=0;count
10.基数排序
/**
* 基数排序
* 问题:排序的范围进一步扩大,那么桶排序的一维数组也会进一步扩大,如待排序的数是稀疏的,会造成内存的大量浪费,如何解决?
* 答:
* 特殊的桶排序:10个桶(0-9)
* 过程:个位进桶——>顺序出桶(个位有序)——>十位进桶——>十位出桶(十位+个位 有序)———>······——>最高位进桶,最高位出桶——>从最高位到最低位都有序,整体有序,输出
* 实现:1.高位不够,用0表示
* 2.定义一个二维数组 eg.int buckets[1][0]=61 个位为1,第一个进"1桶"
* (二维数组包含10个一维数组,每个一维数组长度为要排序数组的长度,以空间换时间)
* 3.定义一个一维数组 存放每个桶放置的元素个数 bucket_count[10]
*/
public static int[] radixsort(int []test){
int []arr=Arrays.copyOf(test, test.length);
int [][]buckets=new int[10][arr.length]; //二维数组,10个桶,每个桶里放属于该桶的
int []bucket_count=new int[10]; //记录每个桶内放的元素个数,便于入桶,出桶的操作
//获取数组中最大的位数
int max=arr[0];
for(int tmp:arr){
if(max十位->百位----->记录循环条件结束的时间
}
return arr;
}
如有错误,欢迎指正。
参考:
https://github.com/hustcc/JS-Sorting-Algorithm
https://www.cnblogs.com/onepixel/articles/7674659.html