數據結構—排序總結

排序根據是否使用外存分爲內排序和外排序,內排序只使用內存進行數據存儲,外排序由於數據量比較大需要藉助外存。

排序的穩定性:排序的穩定性是指排序之後相同的數據元素相對位置不變則爲穩定排序,否則爲不穩定排序。


插入排序

直接插入排序

思想:將一個記錄插入到已經排序的有序表中,從而得到一個新的、個數加1的有序表。這個過程在查找位置過程中進行記錄移動,而不用顯示的交換元素

時間複雜度:O(n^2)

空間複雜度:O(1)

穩定性:穩定排序

public void insertSort(int[] a){
		int j;
		for(int i= 1;i<a.length;i++){
			int tmp = a[i];
			for( j=i;j>0 && compare(a[j-1],tmp) ; j--)
				a[j] = a[j-1];
			a[j] = tmp;
		}
	}
    
private boolean compare(int a,int b){
        return a>b;
    }


折半插入排序

思想:直接插入排序的過程就是查找、比較,如果比較的次數相對少了其效率 也會有所提高,因爲需要在已經排好序的序列查找插入位置,所以使用折半查找降低查找次數

時間複雜度:O(n^2)

空間複雜度:O(1)

穩定性:穩定排序

public void bInsertSort(int[] a){
		int j;
		for(int i=1;i<a.length;i++){
			int tmp = a[i];
			int low = 0;
			int high = i-1;
			while(low < high){
				int mid = (low + high)/2;
				if(tmp < a[mid])
					high = mid -1;
				else
					low = mid + 1;
			}
			for(j=i;j>low;j--)
				a[j] = a[j-1];
			a[j] = tmp;
		}
	}

希爾排序

思想:在插入排序的基礎上,由於插入排序如果序列基本有序的話他的效率會很高,如果直接有序的話其時間複雜度是O(n);所以希爾排序將待排序列分割成若干個子序列,分別進行插入排序,待整個序列基本有序再進行一次整體排序。希爾排序的時間複雜度依賴於其增量序列。

時間複雜度:可達到O(n3/2)

空間複雜度:O(1)

穩定性:不穩定(跳躍性插入)

public void shellSort(int[] a){
		int j;
		int length = a.length;
		for(int skp=length/2;skp>0;skp/=2){
			for(int i=skp;i<length;i++){
				int tmp =a[i];
				for(j = i;j>=skp && compare(a[j-skp],tmp);j-=skp)
					a[j] = a[j-skp];
				a[j] = tmp;
			}
		}
	}
	private boolean compare(int a,int b){
		return a>b;
	}

我這裏使用的是希爾增量序列,他並不是最好的增量序列,比如當其個數是2的冪次方時候,每次其skp都是偶數位,如果偶數爲都是方最大值,基數爲方最小值,這時希爾排序並沒有其什麼作用,增量序列應該保證其相鄰增量互質,最小值爲1

交換排序

冒泡排序

思想:通過第一記錄與第二個記錄比較如果若爲逆序則交換其位置,然後比較第二個與第三個記錄,直到n-1項與第n項比較結束。這個過程是一次冒泡排序,選擇出最大值,然後繼續對前n-1個值進行比較位置,以此類推。整個排序過程需要n-1次冒泡排序。

時間複雜度:O(n^2)

空間複雜度:O(1)

穩定性:穩定排序

public void bubbleSort(int[] a){
		for(int i=0;i<a.length-1;i++)
			for(int j=0;j<a.length-i-1;j++)
				if(a[j] > a[j+1]){
					int tmp = a[j];
					a[j] = a[j+1];
					a[j+1] = tmp;
				}
	}

快速排序

思想:冒泡排序的改進,一趟排序將待排序序列分爲兩部分,其中一部分關鍵詞都大於另一部分關鍵詞,然後在對這兩部分序列繼續進行排序,以達到整個序列有序。其實現需要找到一個樞紐元素將序列與樞紐元素進行對比,小於樞紐元素放在一側,大於樞紐元素放在另一側。樞紐元素的選擇很重要,一般是選擇序列的第一個元素,但是着存在問題,當待排序列是正序或者逆序的時候所有元素都會傾向一側,這樣在整個遞歸過程中都是這樣,使其效率地下。可以選擇待排序列的第一元素、中間元素、最後一個元素進行比較然後選擇中間值,作爲樞紐元素。

時間複雜度:O(nlogn)

空間複雜度:O(1)

穩定性:不穩定排序

	public void quickSort(int[] a,int left ,int right){
		int dp;
		if(left < right){
			dp = partition(a, left, right);
			quickSort(a, left, dp-1);
			quickSort(a, dp+1, right);
		}
	}
	private  int partition(int[] a,int left, int right){
        int pivot = a[left];
        while(left < right){
            while(left < right && a[right] >= pivot)
                right--;
            if(left < right)
                a[left++] = a[right];
            while(left < right && a[left] < pivot)
                left++;
            if(left < right)
                a[right--] = a[left];
        }
        a[left] = pivot;
        return left;
    }

選擇排序

簡單選擇排序

思想:每一次從n-i個記錄中選取最小的元素,作爲有序序列中的第i個記錄

時間複雜度:O(n^2)

空間複雜度:O(1)

穩定性:不穩定排序

public void selectSort1(int[] a){
		for(int i=0;i<a.length;i++){
			int k = i;
			for(int j=i+1;j<a.length-1;j++){
				if(a[j] < a[k])
					k = j;
			}
			if(k != i){
				int tmp = a[i];
				a[i] = a[k];
				a[k] = tmp;
			}
		}
	}

堆排序

思想:堆排序的思想就是將待排序列看做完全二叉樹,堆分爲大堆和小堆,大根堆的意思就是每棵子樹的根節點大於其左右子樹的根節點,小根堆的與之相反。由待排序列建堆的過程就是篩選最大值的過程,查找出最大值後將該值存放在待排序數組的最後一個位置length-1,下一個最大值放在length-2位置。由於是完全二叉樹,所以左後一個非終端節點是n-1/2(因爲數組下標從0開始),用此值開始和左右子樹進行比較選取最大值後以此向上推。由於大根堆得出的序列是由小到大所以,如果是需要正序,則構造大根堆,如果需要逆序則構造小根堆。

時間複雜度:O(nlogn)

空間複雜度:O(1)

穩定性:不穩定排序

public void heapSort(int[] a){
		for(int i=0;i<a.length;i++){
			createMaxHeap(a,	a.length-i-1);
			swap(a,i,a.length-i-1);
		}
	}
	private void createMaxHeap(int[] a,int lastIndex){
		for(int i=(lastIndex-1)/2;i>=0;i--){
			int k = i;	//記錄當前節點
			while(2*k+1 <= lastIndex){	//判斷子節點是否存在
				int bigIndex = 2*k+1;		//記錄最大值節點,默認賦值爲左孩子節點
				if(bigIndex < lastIndex){	//判斷是否存在右孩子
					if(a[bigIndex +1] > a[bigIndex])		//找孩子節點中的最大值
						bigIndex++;
				}
				if(a[k] < a[bigIndex]){	//判斷孩子節點的最大值與根節點值進行比較
					swap(a,k,bigIndex);
					k = bigIndex;
				}else
					break;
			}
		}
	}
	private void swap(int[] a,int i,int j){
		int tmp = a[i];
		a[i] = a[j];
		a[j] = tmp;
	}

歸併排序

思想:將兩個或兩個以上的有序表合併成一個新的有序表。假如有n個記錄的待排序列,首先將其分爲n個子序列,然後兩兩合併得到n/2個子序列,然後再兩兩合併,重複執行。直到得到一個長度爲n的有序序列,這種排序方法稱爲2-路歸併排序;

時間複雜度:O(nlogn)

空間複雜度:O(n)

穩定性:穩定排序

	public static  void mergeSort(int[] a){
		sort(a,0,a.length-1);
		for (int i : a) {
			System.out.println(i);
		}
	}
	private static void sort(int[] a, int left,int right){
		if(left < right){
			int center = (left + right)/2;
			sort(a,left,center);
			sort(a,center+1,right);
			merge(a,left,center,right);
		}
	}
	private static void merge(int[] a,int left,int center,int right){
		int[] tmpArr = new int[a.length];
		int mid = center+1;
		int count = left;
		int tmp = left;
		while(left <=center && mid <= right){
			if(a[left] <= a[mid])
				tmpArr[count++] = a[left++];
			else
				tmpArr[count++] = a[mid++];
		}
		while(left<=center)
			tmpArr[count++] = a[left++];
		while(mid<=right)
			tmpArr[count++] = a[mid++];
		while(tmp <= right)
			a[tmp] = tmpArr[tmp++];
	}

桶式排序

思想:對於使用桶式排序,創建一個數組,數組大小爲其待排序元素的最小值和最大值只差,使用該數組下標存儲待排序列,然後使用公式a[i] = a[i] + a[i-1];計算各個記錄在序列中的位置。桶式排序需要保證待排序列其範圍與最大值最小值差不多如果,其最大值最小值差遠遠大於元素個數,那麼輔助空間花銷會很大。

時間複雜度:O(n+m) m爲範圍

空間複雜度:O(n)

穩定性:穩定排序

	public void bucketSort(int[] a,int max,int min ){
		//緩存數據
		int[] tmp = new int[a.length];
		//桶數組,他的大小應該爲所給數組中最大值減去最小值
		int [] buckets = new int[max - min];
		//將每個記錄掉入桶中
		for(int i=0;i<a.length;i++)
			buckets[a[i]-min]++;
		//計算桶中元素在有序序列中的位置
		for(int i=0;i<max-min;i++)
			buckets[i] = buckets[i] + buckets[i-1];
		tmp = Arrays.copyOf(a, a.length);
		for(int i=a.length-1;i>=0;i--)
			a[--buckets[tmp[i]-min]] = tmp[i];
	}

基數排序

思想:基數排序是一種藉助多關鍵字排序思想的排序。他將待排記錄分解成多個關鍵字,根據子關鍵字進行排序 。多關鍵字排序有兩種排序方式,最高位優先法(MSD)和最低位優先法(LSD),他是按照關鍵字的位數,比如123,分解成三個關鍵字百位、十位、分位。計算機更合適於LSD最低位優先法,關鍵字之間的排序還需要藉助另一種穩定的排序,如果待排序列是整數,那麼每一位都在[0-9]之間,因此可以使用桶式排序。

時間複雜度:依賴於子關鍵字排序算法

空間複雜度:依賴於字關健排序算法

穩定性:穩定排序


	//radix=10,d爲位數
	public void radixSort(int[] a,int radix ,int d){
		//創建緩存數據
		int[] tmp = new int[a.length];
		//buckets記錄排序元素的信息
		int[] buckets = new int[radix];
		//總共排序位數最多次
		for(int i=0,rate=1;i<d;i++){
			//重置count數組
			Arrays.fill(a, 0);
			//複製緩衝數據
			tmp = Arrays.copyOf(a, a.length);
			//將每一位放入桶中
			for(int j=0;j<a.length;j++){
				int subKey = (a[j]/rate)%radix;
				buckets[subKey]++;
			}
			//計算桶中元素在序列中的位置
			for(int j=1;j<radix;j++)
				buckets[j] = buckets[j] + buckets[j-1];
			
			//按子關鍵字對指定的數據排序
			for(int j=a.length-1;j>=0;j--){
				int subKey = (a[j]/rate)%radix;
				a[--buckets[subKey]] = tmp[j];
			}
			rate *=radix;
		}
	}

總結:

算法 時間複雜度 空間複雜度 穩定性
直接插入排序 O(n^2) O(1) 穩定
折半插入排序 O(n^2) O(1) 穩定
希爾排序 O(n3/2) O(1) 不穩定
冒泡排序 O(n^2) O(1) 穩定
快速排序 O( nlogn) O(1) 不穩定
簡單選擇排序 O(n^2) O(1) 不穩定
堆排序 O(nlogn) O(1) 不穩定
歸併排序 O(nlogn) O(n) 穩定
桶式排序 O(K+n) O(K+n) 穩定
基數排序               依賴於子關鍵字排序算法       依賴於子關鍵字排序算法         穩定

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章