包括:
插入排序:直接插入排序,折半插入排序,希爾排序
交換排序:冒泡排序,快速排序
選擇排序:簡單選擇排序
其它排序:歸併排序,堆排序,桶排序
查詢“排序算法動圖”更好理解
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("}");
}
}