包括:
插入排序:直接插入排序,折半插入排序,希尔排序
交换排序:冒泡排序,快速排序
选择排序:简单选择排序
其它排序:归并排序,堆排序,桶排序
查询“排序算法动图”更好理解
import java.util.*;
public class Main
{
public static void main(String[] args)
{
//获取数字
Scanner s1=new Scanner(System.in);
System.out.println("请输入随机数列的数的个数,程序自动构建随机数列(0-10000范围内):");
int n=s1.nextInt();
int[] a=randomArray(n);
//插入排序
{
System.out.println("插入排序");
//直接插入排序用时和结果
//straightInsertionSort(a);
//折半插入排序用时和结果
//binarySort(a);
//希尔排序用时和结果
shellSort(a);
}
//交换排序
{
System.out.println("交换排序");
//冒泡排序用时和结果
//bubbleSort(a);
//快速排序用时和结果
quickSort(a);
}
//选择排序
{
System.out.println("选择排序");
//简单选择排序
//selectSort(a);
}
//其它排序方式
{
System.out.println("其它排序方式");
//归并排序
mergeSort(a);
//堆排序
heapSort(a);
//桶排序
bucketSort(a);
}
}
//构建给定位数随机数
public static int[] randomArray(int n)
{
int[] a=new int[n];
for(int i=0;i<a.length;i++)
{
a[i]=(int)(Math.random()*10000);
}
//System.out.println("随机数列"+Arrays.toString(a));
return a;
}
//直接插入排序
public static void straightInsertionSort(int[] a)
{
int[] b=Arrays.copyOf(a, a.length);//重新建一个数组,否则如果改了传进来的数组,后面的排序用的数组就是排好的了
long t1,t2;//引入时间
t1 = System.currentTimeMillis();//计时器
//功能段
{
int i=0,j=0;
int c=0;//临时存放元素
for (i=1;i<b.length;i++)
{
if(b[i]<b[i-1])//小于才要动,大于是不用动的,
{
c=b[i];
for(j=i-1;(j>=0)&&(c<b[j]);j--)//我的b[0]放了东西的,没有放哨兵,所以可能会有j<0导致数组下标越界,为放这种情况就j<0就跳出,而且这个判断要放前面来短路后面的c<b[j],否则j=-1时调用了判断语句也可能超限。我觉得我这样写其实不是太好。
{
b[j+1]=b[j];
}
b[j+1]=c;
}
}
}
t2=System.currentTimeMillis();
System.out.println("直接插入排序用时"+(t2-t1)+"ms");//输出用时
//System.out.println("排序结果"+Arrays.toString(b));
return ;
}
//折半插入排序
public static void binarySort(int[] a)
{
int[] b=Arrays.copyOf(a, a.length);//重新建一个数组,否则如果改了传进来的数组,后面的排序用的数组就是排好的了
long t1,t2;//引入时间
t1 = System.currentTimeMillis();//计时器
//功能段
{
int i=0,j=0;
int c=0;//临时存放元素
int low,high,mid;
for (i=1;i<b.length;i++)
{
c=b[i];
low=0;
high=i-1;
//折半查找
while(low<=high)
{
mid=(low+high)/2;
if(b[mid]>c)
{
high=mid-1;
}
else low=mid+1;
}
//和直接插入一样后移元素
//由于跳出情况是high<low,这个时候high+1就是该放的位置
for(j=i-1;j>=high+1;j--)
b[j+1]=b[j];
b[high+1]=c;
}
}
t2=System.currentTimeMillis();
System.out.println("折半插入排序用时"+(t2-t1)+"ms");//输出用时
//System.out.println("排序结果"+Arrays.toString(b));
return ;
}
//希尔排序:难
public static void shellSort(int[] a)
{
int[] b=Arrays.copyOf(a, a.length);//重新建一个数组,否则如果改了传进来的数组,后面的排序用的数组就是排好的了
long t1,t2;//引入时间
t1 = System.currentTimeMillis();//计时器
//功能段
{
int i=0,j=0;
int c=0;//临时存放元素
//设置步长,每次循环内两个观察点的距离
int bc=0;
for (bc=b.length/2;bc>=1;bc=bc/2)//步长每个循环整除2
{
for(i=bc;i<b.length;i++)//你或许会奇怪为什么这里不是两个循环,一个就够了,这是因为在bc相同的情况下,每个元素的处理代码是一样的,也就是说只要扫到每一个数就行了(下面的处理代码是一样的),一个for循环即可
//i从bc开始算是因为,根据插入排序是和从第二个数开始循环和第一个数比
{
//
if(b[i]<b[i-bc])
{
c=b[i];
for(j=i-bc;(j>=0)&&(c<b[j]);j=j-bc)
{
b[j+bc]=b[j];
}
b[j+bc]=c;
}
}
}
}
t2=System.currentTimeMillis();
System.out.println("希尔插入排序用时"+(t2-t1)+"ms");//输出用时
//System.out.println("排序结果"+Arrays.toString(b));
return;
}
//冒泡排序
public static void bubbleSort(int[] a)
{
int[] b=Arrays.copyOf(a, a.length);//重新建一个数组,否则如果改了传进来的数组,后面的排序用的数组就是排好的了
long t1,t2;//引入时间
t1 = System.currentTimeMillis();//计时器
//功能段
{
int i=0,j=0;
int c;
for(i=0;i<b.length;i++)
{
for(j=b.length-1;j>i;j--)
{
if(b[j]>b[j-1])
{
c=b[j-1];
b[j-1]=b[j];
b[j]=c;
}
}
}
}
t2=System.currentTimeMillis();
System.out.println("冒泡排序用时"+(t2-t1)+"ms");//输出用时
//System.out.println("排序结果"+Arrays.toString(b));
return;
}
//快速排序:难
public static void quickSort(int[] a)
{
int[] b=Arrays.copyOf(a, a.length);//重新建一个数组,否则如果改了传进来的数组,后面的排序用的数组就是排好的了
long t1,t2;//引入时间
t1 = System.currentTimeMillis();//计时器
//功能段
{
QuickSort(b,0,b.length-1);
}
t2=System.currentTimeMillis();
System.out.println("快速排序用时"+(t2-t1)+"ms");//输出用时
//System.out.println("排序结果"+Arrays.toString(b));
return;
}
public static void QuickSort(int[] A,int low,int high)//递归调用自己
{
if(low<high)
{
int zhongXin=huaFen(A,low,high);
QuickSort(A,low,zhongXin-1);
QuickSort(A,zhongXin+1,high);
}
}
public static int huaFen(int[] A,int low,int high)//每一次的处理过程
{
int jiZun=A[low];
while(low<high)
{
while(low<high&&A[high]>=jiZun){high--;}
A[low]=A[high];
while(low<high&&A[low]<=jiZun){low++;}
A[high]=A[low];
}
A[low]=jiZun;
return low;
}
//简单选择排序
public static void selectSort(int[] a)
{
int[] b=Arrays.copyOf(a, a.length);//重新建一个数组,否则如果改了传进来的数组,后面的排序用的数组就是排好的了
long t1,t2;//引入时间
t1 = System.currentTimeMillis();//计时器
//功能段
{
//其实我感觉比冒泡还简单,每扫一遍把最小的数拿出来放前面
int i,j;
int min;
int c;//用于交换的
for (i=0;i<b.length;i++)
{
min=i;
for(j=i+1;j<b.length;j++)
{
if(b[j]<b[min]) {min=j;}
}
if(min!=i)
{
c=b[i];
b[i]=b[min];
b[min]=c;
}
}
}
t2=System.currentTimeMillis();
System.out.println("简单选择排序用时"+(t2-t1)+"ms");//输出用时
//System.out.println("排序结果"+Arrays.toString(b));
return;
}
//归并排序,也要迭代,但是最坏和最好情况一样,这一点比快排好
public static void mergeSort(int[] a)
{
int[] b=Arrays.copyOf(a, a.length);//重新建一个数组,否则如果改了传进来的数组,后面的排序用的数组就是排好的了
long t1,t2;//引入时间
t1 = System.currentTimeMillis();//计时器
//功能段
{
int[] c=new int[b.length];//构建辅助数组从头构建是因为,这样的话从头到尾只有一个辅助数组
MergeSort(b,0,b.length-1,c);
}
t2=System.currentTimeMillis();
System.out.println("归并排序用时"+(t2-t1)+"ms");//输出用时
//System.out.println("排序结果"+Arrays.toString(b));
return;
}
public static void MergeSort(int[] b,int low,int high,int[] c)//递归
{
if(low<high)//low=high的时候只有一个数就不用再向下归并了
{
int mid=(low+high)/2;
MergeSort(b,low,mid,c);
MergeSort(b,mid+1,high,c);
Merge(b,low,mid,high,c);
}
}
public static void Merge(int[] b,int low,int mid,int high,int[] c)//每一次的处理过程
{
//先把原数组的数复制到辅助数组上,因为等会要操作的是原数组
for(int k=low;k<=high;k++)
{
c[k]=b[k];
}
//其实下面这段你在一元多项式加法乘法里面就试过,不过没有下面这么简洁
int i,j;//分别控制第一个数列和第二个,由于我是二路归并也可以理解为前半后半
int k;//控制原数组新情况个元素位置
for(i=low,j=mid+1,k=i;i<=mid&&j<=high;k++)//谁小谁先进原数组,注意i和k别设成起始值为0
{
if(c[i]<=c[j])//比较c中左右两段的元素
{
b[k]=c[i];
i++;
}
else
{
b[k]=c[j];
j++;
}
}
//如果有没排进去的
while(i<=mid)//若第一个表没检测完,复制
{
b[k]=c[i];
k++;
i++;
}
while(j<=high)//若第二个表没检测完,复制
{
b[k]=c[j];
k++;
j++;
}
}
//堆排序
public static void heapSort(int[] a)
{
int[] b=Arrays.copyOf(a, a.length);//重新建一个数组,否则如果改了传进来的数组,后面的排序用的数组就是排好的了
long t1,t2;//引入时间
t1 = System.currentTimeMillis();//计时器
//功能段
{
//需要先构建一个为堆的类
MaxHeap test1 = new MaxHeap(b);
int bLength = b.length;
for (int i=0; i < bLength; i++) {
b[i]=test1.deleteMax();
}
}
t2=System.currentTimeMillis();
System.out.println("堆排序用时"+(t2-t1)+"ms");//输出用时
//System.out.println("排序结果"+Arrays.toString(b));
return;
}
//桶排序:4位数,这里只是一个简单的桶排序,实际情况里桶里面还会有排序
public static void bucketSort(int[] a)
{
int[] b=Arrays.copyOf(a, a.length);//重新建一个数组,否则如果改了传进来的数组,后面的排序用的数组就是排好的了
long t1,t2;//引入时间
t1 = System.currentTimeMillis();//计时器
//功能段
{
int[] c=new int[10000];//数组c相当于创建了10000个桶
int i;//数组的第i位的i
int j;//数组c的第j位的j
int k;//数组c的第j位的大小
int m;
//进桶
for(i=0;i<b.length;i++)
{
m=b[i];
c[m]++;
}
//导回数组b
i=0;
for(j=0;j<c.length;j++)
{
if(c[j]>0)
{
for(k=0;k<c[j];k++)
{
b[i]=j;
i++;
}
}
}
}
t2=System.currentTimeMillis();
System.out.println("桶排序用时"+(t2-t1)+"ms");//输出用时
//System.out.println("排序结果"+Arrays.toString(b));
return;
}
}
class MaxHeap {
int maxSize = 10000000;//堆的最大大小
int[] array = new int[maxSize+1];
int nowSize = 0;//目前堆使用的部分的大小
public MaxHeap() {
array[0] = Integer.MAX_VALUE;
}
public MaxHeap(int[] inputArray) {
int inputArrayLength = inputArray.length;
nowSize = inputArrayLength;
array[0] = Integer.MAX_VALUE;
for (int i = 1; i <= inputArrayLength; i++) {
array[i] = inputArray[i-1];
}
buildMaxHeap();
}
public boolean insert (int data) {
if (nowSize == maxSize) {
System.out.println("最大堆已满");
return false;
}
nowSize++;
int i = nowSize;
for (;array[i/2] < data; i = i/2) {// 完全二叉树 n/2是父节点
array[i] = array[i/2];// 和插入排序的移动方式类似
}
array[i] = data;
return true;
}
public int deleteMax() {
if (nowSize == 0) {
System.out.print("最大堆已为空");
return -100000;
} else {
int maxData = array[1];// 取出根节点最大值
array[1] = array[nowSize];
nowSize--;
buildFromTreeRoot(1);
return maxData;//这里可以返回maxData
}
}
public void buildMaxHeap() {
//先理子树,子树理完再理根,这样时间复杂度只用O(n)
for(int i = nowSize / 2; i > 0; i--) {
buildFromTreeRoot(i);
}
}
//将根节点与两边比较和移动的函数
public void buildFromTreeRoot(int p) {
int temp = array[p];
int Parent;
int Child;
for (Parent = p ; Parent * 2 <= nowSize ; Parent = Child) {
Child = Parent * 2;
/* 开始循环说了Parent * 2 <= nowSize,且Child = Parent * 2且Child != nowSize时,那么可以肯定Child+1<=nowSize。
* 那么就可以比较Child和Child++的大小了,反之如果且Child == nowSize,那么Child==nowSize那么自然后面的不用考虑。
*/
if ((Child != nowSize) && array[Child] < array[Child + 1]) {
Child++;//Child指向左右子节点中的最大者
}
if ( temp >= array[Child] ) {
break;
} else {
array[Parent] = array[Child];
}
}
//跳出时没有子结点,或者比子结点大可以跳出了
array[Parent] = temp;
}
/**
* 层序遍历
*/
public void printMaxHeap() {//打印数组相当于层序遍历
System.out.print("{");
int i;
for(i = 1; i <= nowSize; i++) {
if (i != 1) {
System.out.print(",");
}
System.out.print(array[i]);
}
System.out.print("}");
}
}