八大排序基本算法实现
/**
* Created by zrq
* time:2019-3-12
*/
import java.util.Arrays;
public class Sort {
public static void main(String[] args) {
int[] sort=new int[]{34,8,64,51,32,21};
// int[] sortLarge=new int[sort.length*100];
// Random r=new Random(1000);
// for(int i=0;i< sortLarge.length;i++){
// sortLarge[i]= r.nextInt(1000);
// }
// long start=System.currentTimeMillis();//时间测试
// bubbleIndexSort(sortLarge)
// System.out.println(System.currentTimeMillis()-start);
// insertSort(sort);//1.简单插入排序
// binaryInsertSort(sort);//二分插入排序
// shellInsertSort(sort);//2.希尔排序
// selectSort(sort);//3.简单选择排序
// twoSelectSort(sort);//二元选择排序
// heapSort(sort);//4.堆排序
// bubbleSort(sort);//5.冒泡排序
// bubbleIndexSort(sort);//冒泡排序-改进:先存索引再交换
// bubbleLowAndHigh(sort);//冒泡双向排序
// quickSort(sort);//6.快速排序
// mergeSort(sort,sort);//7.归并排序
radixSort(sort);//8.基数排序
}
//=====================================================
// 基数排序:
/**
* 名称:基数排序
* 用法:radixSort(sort)
* 思想:基数排序(radix sort)属于"分配式排序"(distribution sort),又称"桶子法"(bucket sort)或bin sort,
* 顾名思义,它是透过键值的部份资讯,将要排序的元素分配至某些"桶"中,藉以达到排序的作用
* 时间复杂度:O (nlog(r)m)
* 参考:https://segmentfault.com/a/1190000013986116
* @param sort
*/
private static void radixSort(int[] sort){
System.out.println("基数排序过程:");
int max=sort[0];
for(int i=1;i<sort.length;i++){//找出最大的数
if(max<sort[i]){
max=sort[i];
}
}
//需要遍历的次数由数组最大值的位数来决定
for (int i = 1; max / i > 0; i = i * 10) {
int[][] buckets = new int[sort.length][10];
//获取每一位数字(个、十、百、千位...分配到桶子里)
for (int j = 0; j < sort.length; j++) {
int num = (sort[j] / i) % 10;
//将其放入桶子里
buckets[j][num] = sort[j];
}
//回收桶子里的元素
int k = 0;
//有10个桶子
for (int j = 0; j < 10; j++) {
//对每个桶子里的元素进行回收
for (int l = 0; l < sort.length ; l++) {
//如果桶子里面有元素就回收(数据初始化会为0)
if (buckets[l][j] != 0) {
sort[k++] = buckets[l][j];
}
}
}
print(sort);
}
}
//=====================================================
// 归并排序:
/**
* 名称:归并排序
* 用法:mergeSort(a,b)
* 思想:两个有序列表合并排序
* @param a
* @param b
* @return
*/
private static int[] mergeSort(int[] a,int[] b){
System.out.println("归并排序过程:");
Arrays.sort(a);Arrays.sort(b);
int[] result=new int[a.length+b.length];
int aindex=0,bindex=0,index=0;
while(aindex<a.length&&bindex<b.length){
if(a[aindex]<=b[bindex]) {
result[index] = a[aindex];
aindex++;
}else {
result[index] = b[bindex];
bindex++;
}
index++;
print(result);
}
while(aindex<a.length){
result[index]=a[aindex];
aindex++;
}
while(bindex<b.length){
result[index]=b[bindex];
bindex++;
}
print(result);
return result;
}
//=====================================================
// 交换排序:
/**
* 名称:交换排序-快速排序
* 用法:quickSort(sort)
* 思想:基准数左边一定比它小,右边一定比它大(升序)
* @param sort
*/
private static void quickSort(int[] sort){
System.out.println("快速排序过程:");
int low=0,high=sort.length-1;
quickOnce(sort,low,high);
}
/**
* 名称:快速排序的一次过程
* 用途:辅助快速排序quickOnce(待排序数组,开始位置索引,结束位置索引)
* 思想:选一个基准数保证一次快排过程后左边的都是小于基准数的元素,右边的都是大于基准数的数(若使用非递归则需要栈辅助实现)
* 顺序很重要,要先从右边开始找(保证最后一定有i=j),j先减保证所遍历的都大于基准数,i则为刚好小于基准数,
* 当到达中间那个数时,由于j所指元素大于基准j会先减1使指针指向中间的数,保证i=j,而i所指一定小于基准;
* (
* 假设基准为10,i=3为index(3)=5,j=5为index(5)=11,交换后按顺序还是j先走
* 中间数大于基准的情况:索引为4的是index(4)=12(j更新为4,i是3)
* index(4)大于基准数则j--(j=3),此时i=j=3不满足循环到达最后的交换基准数阶段,且此时i是一定小于基准的,因为上一轮i就是代表小于基准的索引,否则就有i,j交换,交换后也就满足了
* 中间数小于基准的情况:索引为4的是index(4)=6(j更新为4,i是3)
* index(4)小于基准数则j由于上一次已减,此时j不变j=4;这时轮到i,因为index(4)=6<10所以i++(i=4),这时i=j=4所以循环结束,进行交换
* 综上不管中间的是大于还是小于,最后一定是i=j的情况,而i指针的元素一定会小于基准数,所以可以和基准数交换
* )
* 时间复杂度:O(nlog2n)
* @param sort 待排序数组
* @param left 开始位置索引
* @param right 结束位置索引
*/
private static void quickOnce(int[] sort,int left,int right){
int i,j,t,temp;
if(left>right)return;
temp=sort[left]; //temp中存的就是基准数
i=left;j=right;
while(i!=j){
while(sort[j]>=temp && i<j)//从右往左找不大于基准的元素跳出循环
j--;
//再找右边的
while(sort[i]<=temp && i<j)//从左往右找不小于基准的元素跳出循环
i++;
//交换两个数在数组中的位置
if(i<j){
t=sort[i];sort[i]=sort[j];sort[j]=t;
}
}
//最终将基准数归位
sort[left]=sort[i];sort[i]=temp;
print(sort);
quickOnce(sort, left,i-1);//继续处理左边的,这里是一个递归的过程
quickOnce(sort, i+1,right);//继续处理右边的 ,这里是一个递归的过程
}
/**
* 名称:交换排序-冒泡排序
* 用法:bubbleSort(sort)
* 思想:自上而下对相邻的两个数依次进行比较和调整,让较大的数往下沉,较小的往上冒。
* 即:每当两相邻的数比较后发现它们的排序与排序要求相反时,就将它们互换
* 时间复杂度:O(n^2)
* @param sort
*/
private static void bubbleSort(int[] sort){
System.out.println("冒泡排序过程:");
int temp;
for(int i=0;i<sort.length;i++){//外层保证每一轮当前的位置是最小的
for(int j=i+1;j<sort.length;j++){//内层负责每一轮与剩下的数的遍历
if(sort[i]>sort[j]){
temp=sort[i];//保证每一轮当前数的位置比其余的数大(也就是当前数不大就交换比较的数的内容)
sort[i]=sort[j];
sort[j]=temp;
}
}
printSerial(sort,i);
}
}
/**
* 名称:冒泡索引排序
* 用法: bubbleIndexSort(sort)
* 思想:冒泡排序改进,每次记录最小元素的位置,将最小的与当前位置交换
* @param sort
*/
private static void bubbleIndexSort(int[] sort){
System.out.println("冒泡索引排序过程:");
int index,temp;
for(int i=0;i<sort.length;i++){
index=i;
for(int j=i;j<sort.length;j++){
if(sort[index]>sort[j]){
index=j;
}
}
if(index==i) {
printSerial(sort,i);
continue;
}
temp=sort[i];
sort[i]=sort[index];
sort[index]=temp;
printSerial(sort,i);
}
// //用位置指针记录位置,每次比较后交换,保证最后一位最大
// int i= sort.length -1; //初始时,最后位置保持不变
// while ( i> 0) {
// int pos= 0; //每趟开始时,无记录交换
// for (int j= 0; j< i; j++)
// if (sort[j]> sort[j+1]) {
// pos= j; //记录交换的位置
// int tmp = sort[j]; sort[j]=sort[j+1];sort[j+1]=tmp;
// }
// i= pos; //为下一趟排序作准备
// print(sort);
// }
}
/**
* 名称:冒泡双向排序
* 用法:bubbleLowAndHigh(sort)
* 思想:用正反方向的遍历找到最大与最小者,在一次外层循环的过程中排序好待排序的最大与最小两个值
* @param sort
*/
private static void bubbleLowAndHigh(int[] sort){
System.out.println("冒泡双向排序过程:");
int low = 0;
int high= sort.length -1; //设置变量的初始值
int tmp,j;
while (low < high) {
for (j= low; j< high; ++j) //正向冒泡,找到最大者
if (sort[j]> sort[j+1]) {
tmp = sort[j]; sort[j]=sort[j+1];sort[j+1]=tmp;
}
--high; //修改high值, 前移一位
for ( j=high; j>low; --j) //反向冒泡,找到最小者
if (sort[j]<sort[j-1]) {
tmp = sort[j]; sort[j]=sort[j-1];sort[j-1]=tmp;
}
++low; //修改low值,后移一位
print(sort);
}
}
//=====================================================
// 选择排序:
/**
* 名称:选择排序-堆排序
* 用法:heapSort(sort)
* 思想:根据大根堆根节点为最大元素与末尾交换,交换后重新构建大根堆,直到根节点为末尾元素则结束
* 在排序的过程中,将array[0,...,n-1]看成是一颗完全二叉树的顺序存储结构,
* 利用完全二叉树中双亲节点和孩子结点之间的内在关系,在当前无序区中选择关键字最大(最小)的元素
* 时间复杂度:建堆:o(n),每次调整o(log n),故最好、最坏、平均情况下:o(n*logn);
* 链接:Java实现堆排序(大根堆)https://www.cnblogs.com/CherishFX/p/4643940.html
* @param sort
*/
private static void heapSort(int[] sort) {
buildInitHeap(sort);
for (int i = sort.length - 1; i >= 0; i--) {
int t = sort[0];
sort[0] = sort[i];
sort[i] = t;
heapAdjust(sort, i, 0);
}
print(sort);
}
/**
* 堆调整:
* 判断当前节点与左右孩子节点那个最大
* 保留最大的节点索引继续判断左右孩子那个最大直到不超过范围的节点或当前节点就是最大就返回
* @param sort
* @param len
* @param index
*/
private static void heapAdjust(int[] sort, int len, int index) {
int left = index * 2 + 1;//左孩子节点
int right = index * 2 + 2;//右孩子节点
int large = index;//最大节点,初始为当前节点
if (left < len && sort[left] > sort[large]) {//左孩子比当前节点大,设为最大节点
large = left;
}
if (right < len && sort[right] > sort[large]) {//右孩子比最大节点大,设为最大节点
large = right;
}
if (large != index) {//最大节点不是 当前节点,做个交换,并且递归往树深层继续比较
int t = sort[index];
sort[index] = sort[large];
sort[large] = t;
System.out.print("\t");
printSerial(sort,len);
heapAdjust(sort, len, large);
}
}
/**
* 名称:构建大根堆
* 用途:辅助堆排序
* 思想:从数组最后一位开始直到数组第一位判断该节点的父节点是不是满足大根堆条件,不满足则交换,
* 最后数组中所有节点(i)的父节点((i-1)/2)都比该节点大
*
* 堆的定义:n个关键字序列array[0,...,n-1],当且仅当满足下列要求:(0 <= i <= (n-1)/2)
* ① array[i] <= array[2*i + 1] 且 array[i] <= array[2*i + 2]; 称为小根堆;
* ② array[i] >= array[2*i + 1] 且 array[i] >= array[2*i + 2]; 称为大根堆;
* 任意一节点指针 i:父节点:i==0 ? null : (i-1)/2
* 左孩子:2*i + 1,右孩子:2*i + 2
* @param sort
*/
private static void buildInitHeap(int[] sort) {
int lastParent = (sort.length - 1) / 2;//找到最后一个父节点就行,最后一个父节点满足(i-1)/2
for (int i = lastParent; i >= 0; i--) {//从最后一个父节点开始往上调整
heapAdjust(sort, sort.length, i);
}
}
//初始建堆,堆调整方法,放弃
private static void buildMaxHeap(int[] sort,int n){
int temp;
for(int i=n;i>=0;i--){
if(sort[(i-1)/2]<sort[i]){//父节点比当前元素小,两者交换
temp=sort[i];
sort[i]=sort[(i-1)/2];
sort[(i-1)/2]=temp;
}
System.out.print("\t");
printSerial(sort,i);
}
}
/*************************** 堆排序结束 *********************/
/**
* 名称:二元选择排序
* 用法:twoSelectSort(sort)
* 思想:<span>简单选择排序改进</span>,每趟循环确定两个元素(当前趟最大和最小记录)的位置,从而减少排序所需的循环次数。
* 改进后对n个数据进行排序,最多只需进行[n/2]趟循环即可
* 时间复杂度:O(n^2)
* @param sort
*/
private static void twoSelectSort(int[] sort){
System.out.println("二元选择排序:");
for(int i=0;i<=sort.length/2;i++){//外层做1-n/2的遍历,内层根据i做n/2-max(n-i)的遍历
int min=i,max=i,j=i+1;//min,max记录当前最小最大值的位置索引
while(j<sort.length-i){
if(sort[j]>sort[max]){//大于最大值记录当前索引
max=j;
j++;
continue;
}
if(sort[j]<sort[min]){//小于最小值记录当前索引
min=j;
}
j++;
}
int tmin=sort[min],tmax=sort[max];
tmin=sort[i];sort[i]=sort[min];sort[min]=tmin;//交换最小值
tmax=sort[sort.length-i-1];sort[sort.length-i-1]=sort[max];sort[max]=tmax;//交换最大值
printSerial(sort,i);
}
}
/**
* 名称:选择排序-简单选择排序
* 用法:selectSort(sort)
* 思想:在要排序的一组数中,选出最小(或者最大)的一个数与第1个位置的数交换;
* 然后在剩下的数当中再找最小(或者最大)的与第2个位置的数交换,依次类推,
* 直到第n-1个元素(倒数第二个数)和第n个元素(最后一个数)比较为止。
* 时间复杂度:O(n^2)
* @param sort
*/
private static void selectSort(int[] sort){
System.out.println("简单选择排序:");
for(int i=0;i<sort.length;i++){//外层选择一次遍历选位置
int min=sort[i],index=i;
for(int j=i+1;j<sort.length;j++){//内层判断当前最小的元素并记录下最小的位置索引
// min也可以不要、直接记录索引位置:if(sort[j]<sort[i]) index=j;
if(sort[j]<min){
min=sort[j];
index=j;
}
}
int t=sort[i];//当前位置的元素与最小位置处的数据交换
sort[i]=sort[index];
sort[index]=t;
printSerial(sort,i);
}
}
//=====================================================
// 插入排序:每趟将一个元素,按照其关键字的大小插入到它前面已经排序的子序列中,依此重复,直到插入全部元素。
/**
* 名称:插入排序-希尔排序
* 用法:shellInsertSort(sort);
* 思想:先将整个待排序的记录序列分割成为若干子序列(所有距离为d1的倍数的记录放在同一个组中,把无序数组分割为若干个子序列)
* 分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行依次直接插入排序
* 时间复杂度:最好情况:O(n),最坏情况:O(nlog2n)
* @param sort
*/
private static void shellInsertSort(int[] sort){
System.out.println("希尔排序过程:");
int d=sort.length/2;
while(d>=1){
shellSubSort(sort,d);
d/=2;
System.out.print("单次结果:");
print(sort);
}
}
/**
* 名称:希尔子排序
* 用途:辅助希尔排序完成一次增量排序过程,间隔为d的数组元素<span>直接插入排序</span>
* @param sort
* @param d
*/
private static void shellSubSort(int[] sort,int d){
System.out.println("\t希尔子排序过程:");
for(int i=d;i<sort.length;i++){
int t=sort[i];
int j;
for(j=i-d;j>=0&&sort[i]<sort[i-d];j=j-d){//索引从0开始索引大于等于0
sort[j+d]=sort[j];
}
sort[j+d]=t;
System.out.print("\t");
printSerial(sort,i);
}
}
/**
* 名称:插入排序-二分插入排序
* 用法:binaryInsertSort(sort);
* 思想:直接插入排序的一个变种,区别是:在有序区中查找新元素插入位置时,为了减少元素比较次数提高效率,采用二分查找算法进行插入位置的确定
* 时间复杂度:最好情况:O(log2n),最坏情况:O(n^2)
* @param sort
*/
private static void binaryInsertSort(int[] sort){
System.out.println("二分插入排序过程:");
for(int i=1;i<sort.length;i++){
int low=0,high=i-1;
int t=sort[i];//保存当前位置
while(low<=high){//折半查找位置,注意中间位置时候的情况,中间位置不考虑则会漏掉中间元素的比较
int mid=(high+low)/2;//取中间索引
if(sort[mid]>t){//插入值小于中点值:中间元素大于比较元素,比较元素在mid之前
high=mid-1;//向左缩小区间
}else{
low=mid+1;//向右缩小区间,当low=high时
}
}//最后获得的low就是要插入的位置
for(int j=i-1;j>=low;j--){//移动比较元素要插入的位置的有序数组直到当前位置
sort[j+1]=sort[j];
}
sort[low]=t;
printSerial(sort,i);
}
}
/**
* 名称:插入排序-直接插入排序
* 用法:insertSort(sort);
* 思想:将前面排序的设为有序数组,后面无序的遍历后一个个由有序部分由后往前比较直到到达合适位置进行移动
* 时间复杂度:O(n^2)
* @param sort
*/
private static void insertSort(int[] sort) {
System.out.println("直接插入排序过程:");
for(int i=1;i<sort.length;i++){//外层循环遍历列表
if(sort[i]<sort[i-1]){//判断当前元素是否小于前一个元素,小于进入比较,不小于原地不动继续下一个
int t=sort[i];//保存当前要比较的值
int j;
for(j=i-1;j>=0&&t<sort[j];j--){//要比较的元素还是小于有序队列前一个,移动有序队列,并保证j前一个位置的索引不超过数组范围
sort[j+1]=sort[j];//j为有序队列比较的前一个,j+1为当前移动到的位置,也就是要存的位置
}
sort[j+1]=t;//保存前当前比较元素
}
printSerial(sort,i);
}
}
//======================================================================================
// 打印方法
/**
* 打印序号数组
* @param sort
* @param i
*/
private static void printSerial(int[] sort,int i){
System.out.print(i+" :");
print(sort);
}
/**
* 循环打印数组元素
* @param sort
*/
private static void print(int[] sort){
for(int x:sort){
System.out.print(x+" ");
}
System.out.println();
}
}