1. 程式人生 > >Java之常見的幾種排序演算法-插入、選擇、冒泡、快排、堆排等 .

Java之常見的幾種排序演算法-插入、選擇、冒泡、快排、堆排等 .

本文就是介紹一些常見的排序演算法。排序是一個非常常見的應用場景,很多時候,我們需要根據自己需要排序的資料型別,來自定義排序演算法,但是,在這裡,我們只介紹這些基礎排序演算法,包括:插入排序、選擇排序、氣泡排序、快速排序(重點)、堆排序、歸併排序等等。看下圖:


給定陣列:int data[] = {9,2,7,19,100,97,63,208,55,78}

一、直接插入排序(內部排序、O(n2)穩定)

原理:從待排序的數中選出一個來,插入到前面的合適位置。

package com.xtfggef.algo.sort;

public class InsertSort {

	static int data[] = { 9, 2, 7, 19, 100, 97, 63, 208, 55, 78 };

	public static void insertSort() {
		int tmp, j = 0;
		for (int k = 0; k < data.length; k++) {//-----1-----
			tmp = data[k];
			j = k - 1;
			while (j >= 0 && tmp < data[j]) {//-----2-----
				data[j + 1] = data[j];
				j--;
			}
			data[j + 1] = tmp;//------3-------
		}
	}

	public static void main(String[] args) {
		print();
		System.out.println();
		insertSort();
		System.out.println();
		print();
	}

	static void print() {
		for (int i = 0; i < data.length; i++) {
			System.out.print(data[i] + " ");
		}
	}

}

我簡單的講解一下過程:思路上從待排序的資料中選出一個,插入到前面合適的位置,耗時點在插入方面,合適的位置意味著我們需要進行比較找出哪是合適的位置,舉個例子:對於9,2,7,19,100,97,63,208,55,78這組數,第一個數9前面沒有,不做操作,當第一個數完後,剩下的數就是待排序的數,我們將要從除去9開始的書中選出一個插入到前面合適的位置,拿到2後,放在tmp上,進行註釋中的2處的程式碼,2處的程式碼就是通過迴圈找出這個合適的位置,發現比tmp大的數,立即將該數向後移動一位(這樣做的目的是:前面需要空出一位來進行插入),最後通過註釋3處的程式碼將數插入。

本排序適合:基本有序的資料

二、選擇排序(O(n2)、不穩定)
與直接插入排序正好相反,選擇排序是從待排序的數中選出最小的放在已經排好的後面,這個演算法選數耗時。

package com.xtfggef.algo.sort;

public class SelectSort {

	static int data[] = { 9, 2, 7, 19, 100, 97, 63, 208, 55, 78 };

	public static void selectSort() {
		int i, j, k, tmp = 0;
		for (i = 0; i < data.length - 1; i++) {
			k = i;
			for (j = i + 1; j < data.length; j++)
				if (data[j] < data[k])
					k = j;
			if (k != i) {
				tmp = data[i];
				data[i] = data[k];
				data[k] = tmp;
			}
		}
	}
	public static void main(String[] args) {
		print();
		System.out.println();
		selectSort();
		System.out.println();
		print();
	}

	static void print() {
		for (int i = 0; i < data.length; i++) {
			System.out.print(data[i] + " ");
		}
	}

}

通過迴圈,找出最小的數的下標,賦值於k,即k永遠保持待排序資料中最小的數的下標,最後和當前位置i互換資料即可。

三、快速排序(O(nlogn)、不穩定)

快速排序簡稱快排,是一種比較快的排序,適合基本無序的資料,為什麼這麼說呢?下面我說下快排的思路:

設定兩個指標:i和j,分別指向第一個和最後一個,i像後移動,j向前移動,選第一個數為標準(一般這樣做,當然快排的關鍵就是這個“標準”的選取),從後面開始,找到第一個比標準小的數,互換位置,然後再從前面,找到第一個比標準大的數,互換位置,第一趟的結果就是標準左邊的都小於標準,右邊的都大於標準(但不一定有序),分成兩撥後,繼續遞迴的使用上述方法,最終有序!程式碼如下:

package com.xtfggef.algo.sort;

public class QuickSortTest {

	static class QuickSort {

		public int data[];

		private int partition(int array[], int low, int high) {
			int key = array[low];
			while (low < high) {
				while (low < high && array[high] >= key)
					high--;
				array[low] = array[high];
				while (low < high && array[low] <= key)
					low++;
				array[high] = array[low];
			}
			array[low] = key;
			return low;
		}

		public int[] sort(int low, int high) {
			if (low < high) {
				int result = partition(data, low, high);
				sort(low, result - 1);
				sort(result + 1, high);
			}
			return data;
		}
	}

	static void print(int data[]) {
		for (int i = 0; i < data.length; i++) {
			System.out.print(data[i] + " ");
		}
	}

	public static void main(String[] args) {
		int data[] = { 20, 3, 10, 9, 186, 99, 200, 96, 3000 };
		print(data);
		System.out.println();
		QuickSort qs = new QuickSort();
		qs.data = data;
		qs.sort(0, data.length - 1);
		print(data);
	}
}


看看上面的圖,基本就明白了。

四、氣泡排序(穩定、基本有序可達O(n),最壞情況為O(n2))

氣泡排序是一種很簡單,不論是理解還是時間起來都比較容易的一種排序演算法,思路簡單:小的數一點一點向前起泡,最終有序。

package com.xtfggef.algo.sort;

public class BubbleSort {

	static int data[] = { 9, 2, 7, 19, 100, 97, 63, 208, 55, 78 };

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

	public static void main(String[] args) {
		print();
		System.out.println();
		bubbleSort();
		System.out.println();
		print();
	}

	static void print() {
		for (int i = 0; i < data.length; i++) {
			System.out.print(data[i] + " ");
		}
	}

}

五、堆排序

我們這裡不詳細介紹概念,堆的話,大家只要記得堆是一個完全二叉樹(什麼是完全二叉樹,請不懂的讀者去查資料),堆排序分為兩種堆,大頂堆和小頂堆,大頂堆的意思就是堆頂元素是整個堆中最大的,小頂堆的意思就是堆頂元素是整個堆中最小的,滿足:任何一非葉節點的關鍵字不大於或者不小於其左右孩子節點的關鍵字。堆排序是一個相對難理解的過程,下面我會較為清楚、詳細的講解一下堆排序。堆排序分為三個過程:

建堆:從一個數組順序讀取元素,建立一個堆(完全二叉樹)

初始化:將堆進行調整,使得堆頂為最大(最大堆)或者最小(最小堆)的元素

維護:將堆頂元素出堆後,需要將堆的最後一個節點補充到堆頂,因為這樣破壞了堆的秩序,所以需要進行維護。下面我們圖示一下:

一般情況,建堆和初始化同步進行,


最後為如下所示,即為建堆、初始化成功。


我們可以觀察下這個最大堆,看出堆頂是整個堆中最大的元素,而且除葉子節點外每個節點都大於其子節點。下面的過程就是當我們輸出堆頂元素後,對堆進行維護。


過程是這樣:將堆頂元素出堆後,用最後一個元素補充堆頂元素,這樣破壞了之前的秩序,需要重新維護堆,在堆頂元素的左右節點中選出較小的和堆頂互換,然後一直遞迴下去,所以每次出一個元素,需要一次維護,堆排序適合解決topK問題,能將複雜度降到nlogK。下面是程式碼:

package com.xtfggef.algo.sort;

public class HeapSort {
	private static int[] sort = new int[] { 1, 0, 10, 20, 3, 5, 6, 4, 9, 8, 12,
			17, 34, 11 };

	public static void main(String[] args) {
		buildMaxHeapify(sort);
		heapSort(sort);
		print(sort);
	}

	private static void buildMaxHeapify(int[] data) {
		// 沒有子節點的才需要建立最大堆,從最後一個的父節點開始
		int startIndex = getParentIndex(data.length - 1);
		// 從尾端開始建立最大堆,每次都是正確的堆
		for (int i = startIndex; i >= 0; i--) {
			maxHeapify(data, data.length, i);
		}
	}

	/**
	 * 建立最大堆
	 * 
	 * @param data
	 * @param heapSize
	 *            需要建立最大堆的大小,一般在sort的時候用到,因為最多值放在末尾,末尾就不再歸入最大堆了
	 * @param index
	 *            當前需要建立最大堆的位置
	 */
	private static void maxHeapify(int[] data, int heapSize, int index) {
		// 當前點與左右子節點比較
		int left = getChildLeftIndex(index);
		int right = getChildRightIndex(index);

		int largest = index;
		if (left < heapSize && data[index] < data[left]) {
			largest = left;
		}
		if (right < heapSize && data[largest] < data[right]) {
			largest = right;
		}
		// 得到最大值後可能需要交換,如果交換了,其子節點可能就不是最大堆了,需要重新調整
		if (largest != index) {
			int temp = data[index];
			data[index] = data[largest];
			data[largest] = temp;
			maxHeapify(data, heapSize, largest);
		}
	}

	/**
	 * 排序,最大值放在末尾,data雖然是最大堆,在排序後就成了遞增的
	 * 
	 * @param data
	 */
	private static void heapSort(int[] data) {
		// 末尾與頭交換,交換後調整最大堆
		for (int i = data.length - 1; i > 0; i--) {
			int temp = data[0];
			data[0] = data[i];
			data[i] = temp;
			maxHeapify(data, i, 0);
		}
	}

	/**
	 * 父節點位置
	 * 
	 * @param current
	 * @return
	 */
	private static int getParentIndex(int current) {
		return (current - 1) >> 1;
	}

	/**
	 * 左子節點position 注意括號,加法優先順序更高
	 * 
	 * @param current
	 * @return
	 */
	private static int getChildLeftIndex(int current) {
		return (current << 1) + 1;
	}

	/**
	 * 右子節點position
	 * 
	 * @param current
	 * @return
	 */
	private static int getChildRightIndex(int current) {
		return (current << 1) + 2;
	}

	private static void print(int[] data) {
		int pre = -2;
		for (int i = 0; i < data.length; i++) {
			if (pre < (int) getLog(i + 1)) {
				pre = (int) getLog(i + 1);
				System.out.println();
			}
			System.out.print(data[i] + " |");
		}
	}

	/**
	 * 以2為底的對數
	 * 
	 * @param param
	 * @return
	 */
	private static double getLog(double param) {
		return Math.log(param) / Math.log(2);
	}
}

慢慢理解一下,還是容易明白的!

六、歸併排序

歸併排序是建立在歸併操作上的一種有效的排序演算法。該演算法是採用分治法(Divide and Conquer)的一個非常典型的應用。
首先考慮下如何將將二個有序數列合併。這個非常簡單,只要從比較二個數列的第一個數,誰小就先取誰,取了後就在對應數列中刪除這個數。然後再進行比較,如果有數列為空,那直接將另一個數列的資料依次取出即可。

package com.xtfggef.algo.sort;

public class SortTest {

	// 將有序陣列a[]和b[]合併到c[]中
	static void MemeryArray(int a[], int n, int b[], int m, int c[]) {
		int i, j, k;

		i = j = k = 0;
		while (i < n && j < m) {
			if (a[i] < b[j])
				c[k++] = a[i++];
			else
				c[k++] = b[j++];
		}

		while (i < n)
			c[k++] = a[i++];

		while (j < m)
			c[k++] = b[j++];
	}
	
	public static void main(String[] args) {
		int a[] = { 2, 7, 8, 10, 299 };
		int b[] = { 5, 9, 14, 20, 66, 88, 92 };
		int c[] = new int[a.length + b.length];
		MemeryArray(a, 5, b, 7, c);
		print(c);
	}

	private static void print(int[] c) {
		for (int i = 0; i < c.length; i++) {
			System.out.print(c[i] + " ");
		}
	}
}

可以看出合併有序數列的效率是比較高的,可以達到O(n)。解決了上面的合併有序數列問題,再來看歸併排序,其的基本思路就是將陣列分成二組A,B,如果這二組組內的資料都是有序的,那麼就可以很方便的將這二組資料進行排序。如何讓這二組組內資料有序了?可以將A,B組各自再分成二組。依次類推,當分出來的小組只有一個數據時,可以認為這個小組組內已經達到了有序,然後再合併相鄰的二個小組就可以了。這樣通過先遞迴的分解數列,再合併數列就完成了歸併排序。下面是歸併排序程式碼:

package com.xtfggef.algo.sort;

public class MergeSort {

	private static void mergeSort(int[] data, int start, int end) {
		if (end > start) {
			int pos = (start + end) / 2;
			mergeSort(data, start, pos);
			mergeSort(data, pos + 1, end);
			merge(data, start, pos, end);
		}
	}

	private static void merge(int[] data, int start, int pos, int end) {
		int len1 = pos - start + 1;
		int len2 = end - pos;
		int A[] = new int[len1 + 1];
		int B[] = new int[len2 + 1];
		for (int i = 0; i < len1; i++) {
			A[i] = data[i + start - 1];
		}
		A[len1] = Integer.MAX_VALUE;
		for (int i = 0; i < len2; i++) {
			B[i] = data[i + pos];
		}
		B[len2] = Integer.MAX_VALUE;
		int m = 0, n = 0;
		for (int i = start - 1; i < end; i++) {
			if (A[m] > B[n]) {
				data[i] = B[n];
				n++;
			} else {
				data[i] = A[m];
				m++;
			}
		}
	}

	private static void print(int[] data) {
		for (int i = 0; i < data.length; i++) {
			System.out.print(data[i] + " ");
		}
	}

	public static void main(String args[]) {
		int data[] = { 8, 16, 99, 732, 10, 1, 29, 66 };
		print(data);
		System.out.println();
		mergeSort(data, 1, data.length);
		print(data);
	}
}

七、希爾排序(不穩定、O(nlogn))

d1 = n/2,d2 = d1/2 ...

舉例一下:{9,8,7,6,5,4,3,2,1,0} 10個數,現分為5組(9,4),(8,3),(7,2),(6,1),(5,0),然後分別對每組進行直接插入排序得到:

(4,9),(3,8),(2,7),(1,6),(0,5),再將這5組分為2組(4,3,2,1,0),(9,8,7,6,5)分別對這兩組進行直插排序,得:(0,1,2,3,4),(5,6,7,8,9)最終有序。

package com.xtfggef.algo.sort;

public class ShellSort {

	static void shellsort(int[] a, int n) {
		int i, j, temp;
		int gap = 0;
		while (gap <= n) {
			gap = gap * 3 + 1;
		}
		while (gap > 0) {
			for (i = gap; i < n; i++) {
				j = i - gap;
				temp = a[i];
				while ((j >= 0) && (a[j] > temp)) {
					a[j + gap] = a[j];
					j = j - gap;
				}
				a[j + gap] = temp;
			}
			gap = (gap - 1) / 3;
		}
	}

	static void print(int data[]) {
		for (int i = 0; i < data.length; i++) {
			System.out.print(data[i] + " ");
		}
	}

	public static void main(String[] args) {
		int data[] = { 2, 68, 7, 19, 1, 28, 66, 200 };
		print(data);
		System.out.println();
		shellsort(data, data.length);
		print(data);
	}
}

八、其他排序

相關推薦

Java常見排序演算法-插入選擇冒泡 .

本文就是介紹一些常見的排序演算法。排序是一個非常常見的應用場景,很多時候,我們需要根據自己需要排序的資料型別,來自定義排序演算法,但是,在這裡,我們只介紹這些基礎排序演算法,包括:插入排序、選擇排序、氣泡排序、快速排序(重點)、堆排序、歸併排序等等。看下圖: 給定陣

Java常見排序演算法-插入選擇冒泡

Java面試寶典系列之基礎排序演算法 作者:egg 郵箱:[email protected] 本文就是介紹一些常見的排序演算法。排序是一個非常常見的應用場景,很多時候,我們需要根據自己需要排序的資料型別,來自

Java常見排序演算法-插入選擇冒泡

本文就是介紹一些常見的排序演算法。排序是一個非常常見的應用場景,很多時候,我們需要根據自己需要排序的資料型別,來自定義排序演算法,但是,在這裡,我們只介紹這些基礎排序演算法,包括:插入排序、選擇排序、氣泡排序、快速排序(重點)、堆排序、歸併排序等等。看下圖: 給定陣

Java 常見排序演算法-插入選擇冒泡

本文就是介紹一些常見的排序演算法。排序是一個非常常見的應用場景,很多時候,我們需要根據自己需要排序的資料型別,來自定義排序演算法,但是,在這裡,我們只介紹這些基礎排序演算法,包括:插入排序、選擇排序、氣泡排序、快速排序(重點)、堆排序、歸併排序等等。看下圖:

常見排序演算法-插入選擇冒泡

作者:egg 郵箱:[email protected] 本文就是介紹一些常見的排序演算法。排序是一個非常常見的應用場景,很多時候,我們需要根據自己需要排序的資料型別,來自定義排序演算法,但是,在這裡,我們只介紹這些基礎排序演算法,包括

常見排序演算法的C++描述

首先看一下幾種常見排序的特性 插入排序 void insertSort(vector<int> & arr) { int sz = arr.size();

常見排序演算法java和C++版)(參考《演算法》)

博主這裡要講的幾種排序演算法包括(從難到易):1.氣泡排序(最low的演算法) 2.插入排序 3.希爾排序 4.歸併排序 5.快速排序 6.快速排序的三項切分 氣泡排序: (1)簡介:這是最原始,最簡單的排序,幾乎不需要額外的空間 (2)基本原理:通過迴圈將最大的元素移到

總結排序演算法Java實現

1、氣泡排序 氣泡排序是一種交換排序,它的基本思想是:兩兩比較相鄰記錄的關鍵字,如果反序則交換,直到沒有反序的記錄為止。 Java程式碼: import java.util.Random; public class BubbleSort { /** * 改進的氣

Java學習排序演算法練習

幾大排序演算法練習 1、各種排序演算法** 氣泡排序 (Bubble Sort) 選擇排序 (Selection Sort) 插入排序 (Insertion Sort) 氣泡排序(Bubble Sort) 基本思想是對比相鄰的元素值,如果滿足條件就交換元

Java常用的八排序演算法與程式碼實現(一):氣泡排序插入排序選擇排序

這三種排序演算法適合小規模資料排序 ---   共同點:基於比較,時間複雜度均為O(n2),空間複雜度均為O(1)(原地排序演算法)   不同點:插入排序和氣泡排序是穩定的排序演算法,選擇排序不是 ---   穩定排序演算法:可以保持數值相等的兩個物件,在排序之

11. 常見的有哪排序演算法,試比較其時間複雜度,以及是否穩定,及各自使用的情形

1、幾種常見排序演算法的時間複雜度 排序方法 平均情況 最好情況 最壞情況 直接插入排序 O(n2) O(n) O(n2) 起泡排序 O(n2) O(n) O(n2) 快速排序 O(nlog2n) O(nlog2n)

排序演算法java實現

沒有配圖,過幾天補上 package com.sort; public class Sort { /** * 插入排序 * 原理:往有序的子陣列選擇一個合適的位置插進去 * */ public void insertSort(int sort[

Java常見的陣列排序演算法(包括冒泡選擇插入,快速排序)

1 public static void bubbleSort(int[] arr) { 2 3    for(int x=0; x<arr.length-1; x++) { 4 5    for(int y=0; y<arr.length-x-

排序演算法java版本

很早以前的程式碼了,今天發在這裡備份一下,也供有需要的朋友參考。1. 排序中的數值交換/** * 交換陣列中的兩個值的位置 * @param datas * @param ind1 * @param ind2 */ privat

Java常用的八排序演算法與程式碼實現(三):桶排序計數排序基數排序

三種線性排序演算法:桶排序、計數排序、基數排序 線性排序演算法(Linear Sort):這些排序演算法的時間複雜度是線性的O(n),是非比較的排序演算法 桶排序(Bucket Sort)   將要排序的資料分到幾個有序的桶裡,每個桶裡的資料再單獨進行排序,桶內排完序之後,再把桶裡的

Java常用的八排序演算法與程式碼實現(二):歸併排序快速排序

注:這裡給出的程式碼方案都是通過遞迴完成的 --- 歸併排序(Merge Sort):   分而治之,遞迴實現   如果需要排序一個數組,我們先把陣列從中間分成前後兩部分,然後對前後兩部分進行分別排序,再將排好序的數組合並在一起,這樣整個陣列就有序了   歸併排序是穩定的排序演算法,時間

排序演算法,記錄一下

個人也就會四種排序(bubble,select,insert,quick),哈哈,看官大人可能有點失望。自己也看過幾種,不過一直沒寫過其他的,就記錄下這四種吧。 程式碼均可直接通過編譯。各種版本實現都有出入,不過思想都是一樣。工作這麼久還沒有一次性完全寫正確過,功力還是差點。 #includ

【python資料結構與演算法排序演算法:氣泡排序快速排序

以下排序演算法,預設的排序結果是從小到大。 一.氣泡排序: 1.氣泡排序思想:越大的元素,就像越大的氣泡。最大的氣泡才能夠浮到最高的位置。 具體來說,即,氣泡排序有兩層迴圈,外層迴圈控制每一輪排序中操作元素的個數——氣泡排序每一輪都會找到遍歷到的元素的最大值,並把它放在最後,下一輪排序時

Java語言實現六排序演算法

Java語言實現六種排序演算法 Java語言實現六種排序演算法 氣泡排序 插入排序 歸併排序 快速排序 希爾排序 選擇排序

排序演算法

本文轉載自碼農網:http://www.codeceo.com/article/10-sort-algorithm-interview.html#0-tsina-1-10490-397232819ff9a47a7b7e80a40613cfe1 查詢和排