數據結構與算法---常用三大排序算法


1:冒泡排序


冒泡排序是的算法思路是將最小數值放在下標為0的位置,將最大值放在mao.length-1的位置

外層for循環開始計算層數,即mao.length-1為目標計划循環次數,當外層for完成一次后,總長度就會-1,也就是說最大值已經出來了並且放在了最后一位,那么在之后的循環中就不算這一項了,以此類推。

內層for循環從下標0開始,和下一角標比較,這里用第一項和第二項代表,如果第一項>第二項,那么兩個就互換,否側不換,然后第二項和第三項比較。直到完成這一次循環。

// 簡單排序--冒泡排序
		int[] mao = { 120, 110, 100, 90, 80, 70, 45 };
		// 外循環只是循環幾次
		for (int i = mao.length - 1; i > 1; i--) {
			// 內循環每循環一次就會有拍好一位,如最大值
			System.out.println("第" + (mao.length - i) + "次循環");
			for (int j = 0; j < i; j++) {
				if (mao[j] > mao[j + 1]) {
					// 如果第一個比第二大,那就換位子
					int temp = mao[j];
					mao[j] = mao[j + 1];
					mao[j + 1] = temp;
				}
				for (int k = 0; k < mao.length; k++) {
					System.out.print(mao[k] + " , ");
				}
				System.out.println();
			}

		}
第1次循環
110 , 120 , 100 , 90 , 80 , 70 , 45 , 
110 , 100 , 120 , 90 , 80 , 70 , 45 , 
110 , 100 , 90 , 120 , 80 , 70 , 45 , 
110 , 100 , 90 , 80 , 120 , 70 , 45 , 
110 , 100 , 90 , 80 , 70 , 120 , 45 , 
110 , 100 , 90 , 80 , 70 , 45 , 120 , 
第2次循環
100 , 110 , 90 , 80 , 70 , 45 , 120 , 
100 , 90 , 110 , 80 , 70 , 45 , 120 , 
100 , 90 , 80 , 110 , 70 , 45 , 120 , 
100 , 90 , 80 , 70 , 110 , 45 , 120 , 
100 , 90 , 80 , 70 , 45 , 110 , 120 , 
第3次循環
90 , 100 , 80 , 70 , 45 , 110 , 120 , 
90 , 80 , 100 , 70 , 45 , 110 , 120 , 
90 , 80 , 70 , 100 , 45 , 110 , 120 , 
90 , 80 , 70 , 45 , 100 , 110 , 120 , 
第4次循環
80 , 90 , 70 , 45 , 100 , 110 , 120 , 
80 , 70 , 90 , 45 , 100 , 110 , 120 , 
80 , 70 , 45 , 90 , 100 , 110 , 120 , 
第5次循環
70 , 80 , 45 , 90 , 100 , 110 , 120 , 
70 , 45 , 80 , 90 , 100 , 110 , 120 , 
第6次循環
45 , 70 , 80 , 90 , 100 , 110 , 120 , 

算法不變性

這里可以看出當比較到最后一次時,只剩下前兩個比較了,也就是說在最右邊一旦完成比較后就不會再進行任何比較操作,當然每個排序的不變性是不一樣的。

冒泡排序的效率

在上面的例子結果中可以看出,7條數據的情況下,第一趟循環比較了6次,第二次比較了5次...,最后一次比較一次,公式為
(N-1)+(N-2)+(N-3)+...+1=N*(N-1)/2====

比較算法結果:

學過高數的人都知道,數學中有叫趨向於某某某的說法,咱們的算法比較結果就是趨向於N²/2
。因為在N很大的情況下,N-1與N可以都看做為N,就算-1,-2也影響不大。

交換算法結果

由於在比較時不一定要交換,那么交換次數肯定是小於比較次數,有點的是從頭到位都需要交換(逆向排序),則有的一次都不用換(已經排序好的),所以,這里排序算法取平均N²/4.

大O表示法

由於常數不算在表示法中,所以結果為O(N²)時間。

在平常的代碼中,如果那你看到雙重for循環時,基本就可以判斷運行時間效果為O(N²)了。

2:選擇排序


// 選擇排序
		System.out.println("選擇排序");

		int[] selectInt = { 120, 110, 100, 90, 80, 70, 45 };
		/**
		 * 第一步: 原理是都和第一項(下標0)比較,小的則替換
		 * 
		 * 外層變量設置為outer,內層變量為inner,最小數據存放下標min。
		 * selectInt.length-1一共7個數比6次就可以了。selectInt.length內部是從下標1開始的,這里就不要再-1了,
		 * 否側少比較一次
		 */
		int min;
		for (int outer = 0; outer < selectInt.length - 1; outer++) {
			min = outer;
			// 內循環每循環一次就會有拍好一位,如最大值
			System.out.println("第" + (outer + 1) + "次循環");
			for (int inner = outer + 1; inner < selectInt.length; inner++) {
				// 如果后六個數中,有小於第一項(下標0)的,就調換下標
				if (selectInt[inner] < selectInt[min]) {
					min = inner;
				}
			}
			int temp = selectInt[outer];
			selectInt[outer] = selectInt[min];
			selectInt[min] = temp;

			for (int k = 0; k < selectInt.length; k++) {
				System.out.print(selectInt[k] + " , ");
			}
			System.out.println();
		}
		/**
		 * 第1次循環 45 , 110 , 100 , 90 , 80 , 70 , 120 ,
		 * 
		 */
第1次循環
45 , 110 , 100 , 90 , 80 , 70 , 120 , 
第2次循環
45 , 70 , 100 , 90 , 80 , 110 , 120 , 
第3次循環
45 , 70 , 80 , 90 , 100 , 110 , 120 , 
第4次循環
45 , 70 , 80 , 90 , 100 , 110 , 120 , 
第5次循環
45 , 70 , 80 , 90 , 100 , 110 , 120 , 
第6次循環
45 , 70 , 80 , 90 , 100 , 110 , 120 , 

選擇排序算法不變性

下標小於等於outer的位置的數據總是有序的,一旦放在前面后,就不會再動。

選擇排序算法效率

上面結果可以說明,雖然也是比較了和冒泡一樣多的次數,但是交換缺少了很多。所以時間為N²/2

比較次數和交換次數

比較次數:N²/2
交換次數:最多N-1,最少0次,取平均N/2

大O表示法

O(N²)

以上可看出選擇排序是比冒泡排序快點的。

3:插入排序

概念:有一個已經有序的數據序列,要求在這個已經排好的數據序列中插入一個數,但要求插入后此數據序列仍然有序,這個時候就要用到一種新的排序方法——插入排序法,插入排序的基本操作就是將一個數據插入到已經排好序的有序數據中,從而得到一個新的、個數加一的有序數據,算法適用於少量數據的排序,時間復雜度為O(n^2)

/**
		 * 從第二項開始,第一項默認為有序 
		 * 1:把第二項數據暫存,和第一項比較,如果第一項>第二項則調換,
		 * 2:把第三項數據暫存,和第二項比較,如果第二項>第三項則調換, 這時調換后的第二項還要和第一項比較,然后再判斷調換,從當前下標開始向左遍歷凡是大於temp的數據,下標
		 * 都均向右移動一位(調換)。
		 * 以此類推 ........
		 *
		 * 
		 * 很多人估計不理解為什么從第二項開始,而不是從第一項,
		 * 這里我稍微做一下解釋,插入排序就是將一個數據插入到已經排好序的有序數據中,從而得到一個新的、個數加一的有序數據,算法適用於少量數據的排序,
		 * 我們對於一個數組,不知道哪里是排序好的,可能是前三條,也可能不是有序的,我們這時就要假設一段已經排好序的數組,我們直接取前三項的話,
		 * 不一定是排序好的, 我們取前一項的話,就一個數據肯定是排序好的,所以就從第二項開始,默認第一項已經排序好了。這就是由來。
		 */
		int insertElems[] = { 23, 4, 5, 34, 56, 21 };
		int inner, outer;
		for (outer = 1; outer < insertElems.length; outer++) {
			int temp = insertElems[outer];// 把第二項暫存
			inner = outer;
			// (第一項>第二項)
			while (inner > 0 && insertElems[inner - 1] > temp) {
				insertElems[inner] = insertElems[inner - 1];
				inner--;
			}
			insertElems[inner] = temp;
			for (int k = 0; k < insertElems.length; k++) {
				System.out.print(insertElems[k] + " , ");
			}
			System.out.println();

		}
4 , 23 , 5 , 34 , 56 , 21 , 
4 , 5 , 23 , 34 , 56 , 21 , 
4 , 5 , 23 , 34 , 56 , 21 , 
4 , 5 , 23 , 34 , 56 , 21 , 
4 , 5 , 21 , 23 , 34 , 56 , 

插入排序的不變性

在每趟結束時,在temp位置插入后,比outer下標小(左邊)的數據項都是有序的。

結論:插入排序比冒泡快一倍,優於選擇排序


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM