1. 程式人生 > >八大排序演算法及時間空間複雜度分析,java版

八大排序演算法及時間空間複雜度分析,java版

//放在一起感覺又臭又長,所以每排序我單獨放出來了,歡迎大家平均交流指出不足

import java.lang.reflect.Array;

import java.util.*;
public class EightKindOfSort {
/*選擇排序    (不穩定演算法)
 * 基本思想:兩個for迴圈巢狀,內部for迴圈用來找到最大(小)的元素,外部迴圈用來放置找到的元素
 * 複雜度:需要遍歷陣列才能找到峰值元素,所以複雜度與原始序列是否有序無關,最好最壞和平均情況的時間複雜度都為O(n^2);
 * 需要一個臨時變數用來交換陣列內資料位置,所以空間複雜度為O(1)
 */
public static void xuanZeSort(int []a)
{

for(int i=0;i<a.length-1;i++)
{
for(int j=i+1;j<a.length;j++)
{
if(a[i]>a[j])
{
int temp=a[i];
a[i]=a[j];
a[j]=temp;
}
}
}
for(int j:a)  //j是a遍歷出的每個元素,不是下標
{
System.out.print (j+" ");


}

}
/*快速排序(不穩定演算法)
 * 基本思想:選擇陣列任一資料作為k,依次從前向後 找到第一個比k大的元素,然後與k交換。
 *   從後向前找到第一個比k小的元素,然後與k交換,直到遍歷一次,k恰好放到正確的位置
 *   然後在分別對k左和k右兩部分進行快速排序
 *  外層是while迴圈,條件start<end  為一次遍歷
 *  迴圈體兩個while,一個從左到右找到第一個比k大的元素交換,條件是(start<end&&a[start]<=base)
 *  另一個從右到左找到第一個比k小的元素交換
 *  一次遍歷之後把陣列劃分成兩部分,然後一次遞迴;
 *  
 *  複雜度:簡單來說,複雜度就是對資料的操作次數
 *  在最好和平均情況下,資料從中間劃分成兩部分,一個大小為n的陣列需要劃分Log2n次,即遞迴log2n次,
 *  一次對n級別個數據進行操作,所以時間複雜度為O(n*log2n)
 * 在最壞的情況下,每次都選到陣列中的最大或者最小的元素,每次劃分成n-1和1兩部分,這樣就需要遞迴n-1次,
 * 一次對n級別個數據進行操作,所以最壞的時間複雜度為O(n*2)
 *  快速排序的空間複雜度可以理解為遞迴的深度,而遞迴的實現依靠棧。
 *   已經說明平均需要遞迴log2n次,所以平均空間複雜度為O(log2n)
 * 
 */

/*public static void quickSort(int []a,int start ,int end)
{
if(start>end)
return;
else
{
int mid=quick(a,start,end);
quickSort(a,start,mid-1);
quickSort(a,mid+1,end);
}
}*/
public static void  quickSort(int []a,int start ,int end)
{
int start0=start;
int end0=end;
int base =a[end];
while(start<end)
{

while(start<end&&a[start]<=base)
start++;
if(start<end)
{
int temp=a[start];
a[start]=a[end];
a[end]=temp;
end--;
}
while(start<end&&a[end]>=base)
end--;
if(start<end)
{
int temp=a[start];
a[start]=a[end];
a[end]=temp;
start++;
}
}
//return end;
if(start0>=end0)
{
return  ;
}
else
{
int mid =end;
quickSort(a,start0,mid-1);
quickSort(a,mid+1,end0);

}

}
/*
* 氣泡排序:(穩定演算法)
* 基本思想:氣泡排序屬於比較簡單的排序,以非遞減為例,依次遍歷陣列,發現a[i]>a[i+1}的情況,swap(a[i],a[i+1])
* 直到沒有逆序的資料,完成排序
* 可以用兩個for迴圈巢狀實現,外層控制遍歷次數,內層用來實現交換

* 也可以用一個boolean型別變數來控制是否有交換髮生,如果沒有交換,表明已經正序,可以直接輸出

* 複雜度 分析:
* 很明顯,氣泡排序最好情況是陣列已經有序的情況,此時只需要遍歷一次資料,沒有交換髮生,結束排序,時間複雜度為O(n)
* 那最壞情況下的冒泡就是逆序,此時你需要遍歷n-1次資料,(資料為 3  2   1,一次遍歷為2 1 3 ,二次遍歷 1 2 3 結束  ),
* 此時的時間複雜度為O(n^2) 
* 平均情況下也為O(n^2) 需要注意的是平均情況並不是與最壞情況下的時間複雜度相等,
* 平均的時間複雜度=sum(Pi*f(n));Pi為每種情況出現的概率,計算起來有些困難,在這裡直接用前輩的結果
* 空間複雜度:只需要一個temp臨時變數來交換資料,所以O(1)


* Ps:氣泡排序在陣列基本有序,只有零星逆序的情況下效率極高
*/
public static void bubbleSort1(int[] a){
for (int k = 1; k < a.length ; k++) {
for (int i = 0; i < a.length - k; ++i) {
if (a[i] > a[i + 1]) {
int temp = a[i];
a[i] = a[i + 1];
a[i + 1] = temp;
}
}
}


}

public static void bubbleSort2(int[] a) {
boolean bol = true;
while (bol) {
bol = false;
for (int i = 0; i < a.length - 1; ++i) {
if (a[i] > a[i + 1]) {
int temp = a[i];
a[i] = a[i + 1];
a[i + 1] = temp;
bol = true;
}
}
}


}
/*
* 直接插入排序(穩定演算法)
* 基本思想:從第一個數開始,認定陣列的前i個數有序,依次遍歷陣列,
* 把後面的資料插入到合適的位置,使陣列繼續保持有序
* 用兩個for迴圈實現,外層i用來控制陣列的資料量,內層用來找到a[i]需要插入的位置
* 如果temp大於a[j]則把a[j]向後移動
* 複雜度分析:
* 時間複雜度:最好情況是陣列有序,依次把資料放到前一個數的後面   O(n)
* 最壞情況是陣列逆序,遍歷n次陣列,每次都需要把n個數據向後移動  O(n)
* 平均情況      O(n)
* 空間複雜度:需要一個臨時變數temp來儲存即將插入的數 據   所以   O(1)

*/
public static void insertSort(int[] a) {
for (int i = 1; i < a.length; ++i) {
int temp = a[i];
int j = i - 1;
for (j = i - 1; j >= 0; --j) {
if (temp < a[j]) {
a[j + 1] = a[j];
} else {
break;
}


}
a[j + 1] = temp;
}


}
/*
* shell排序(希爾排序)
* 基本思想:希爾排序選取一個增量h,也就是把整個陣列分成h份,對每一份進行排序。
* 然後減少增量h,重複上述過程。
* 一般我們選取的遞增序列為:3*h+1   即1,4,13,40,.....
* 實現:用一個while語句求出對應陣列我們所需要的最大h
* 然後在用一個外層while迴圈控制h,每迴圈一次h=h/3;直至h自減至1;
* 內層是直接插入排序演算法,兩個for迴圈巢狀,外層for迴圈用來控制i  - a.length的自增
* 內層for迴圈用來找到i需要插入的位置。
* 複雜度分析:
* 時間複雜度:希爾排序最好情況是陣列正序,此時外層for迴圈執行一次+最外層while迴圈<n次;內層for迴圈不執行,  O(n)
* 最壞情況是陣列逆序,外層for迴圈+while<n次,內層for每次都需要把n個數據向後移動一位;   O(n^2)
* 平均情況:  O(n^1.3)  站在巨人的肩膀上看問題
* 空間複雜度:
* 需要一個temp用來臨時交換資料,一個h來儲存增量           O(1)


*/
public static void shellSort(int []a){
int len=a.length;
int h=1;
while (h<len/3)
h=h*3+1;
while(h>0){
for(int i=h;i<len;++i){
for(int j=i;j>=h;j=j-h){
if(a[j]<a[j-h]){
int temp=a[j];
a[j]=a[j-h];
a[j-h]=temp;
}
}
}
h=h/3;
}
}
/*
* (二分)歸併排序(穩定演算法)
* 基本思想:將陣列遞迴分成越來越小的數集,直到每個數集只有一個數
* 然後將資料遞迴排序,使其合併成與原來資料一樣的有序序列
* 時間複雜度分析:遞迴分解資料,需要遞迴logN次,每次都需要對n個數據掃描一次,最好最壞平均都一樣,所以O(nlogn)
* 空間複雜度分析:歸併排序需要一個臨時temp[]來儲存歸併的結果,所以   O(n) 

* 基本實現:需要兩個函式,一個用來遞迴劃分陣列,比較簡單,依次二分即可
* 一個用來把劃分好的陣列遞迴排序併合並:兩個陣列從第一個元素開始比較,小的資料入,直到一個數組中資料為空,
* 然後把另一個數組中的剩餘資料寫入,最後用temp陣列替代初始的陣列
*/

public static  void merge(int a[],int start,int mid,int end){
int[] temp=new int[end-start+1];
int i=start;
int j=mid+1;
int k=0;
while (i <= mid && j <= end) {
if (a[i] < a[j]) {
temp[k++] = a[i++];
} else {
temp[k++] = a[j++];
}
while (i <= mid) {
temp[k++] = a[i++];
}
while (j <= end) {
temp[k++] = a[j++];
}
for(int m=0;m<temp.length;++m){
a[start+m]=temp[m];
}

}


}
/**
* @param a
* @param start  陣列索引
* @param end陣列索引,<.length
*/
public static void mergeSort(int a[],int start,int end){
int mid=(end+start)/2;
//int mid=start+(end-start)/2;
//System.out.println(mid);
if(start<end){
mergeSort(a,start,mid);
mergeSort(a,mid+1,end);
merge(a,start,mid,end);
}
}