排序算法
一、基本介紹
? 排序算法比較基礎,但是設計到很多計算機科學的想法,如下:
? 1、比較和非比較的策略
? 2、迭代和遞歸的實現
? 3、分而治之思想
? 4、最佳、最差、平均情況時間復雜度分析
? 5、隨機算法
二、排序算法的分類
算法分類
算法總結
三、冒泡排序
3.1 冒泡排序的介紹
? 冒泡排序是一種簡單的排序算法。它重復地走訪過要排序的數列,一次比較兩個元素,如果它們的順序錯誤就把它們交換過來。走訪數列的工作是重復地進行直到沒有再需要交換,也就是說該數列已經排序完成。這個算法的名字由來是因為越小的元素會經由交換慢慢“浮”到數列的頂端。
3.2 冒泡排序的原理:
? 1、如果元素大小關系不正確,交換這兩個數(在本例中為a> b),
? 2、比較一對相鄰元素(a,b),
? 3、重復步驟1和2,直到我們到達數組的末尾(最后一對是第(N-2)和(N-1)項,因為我們的數組從零開始)
? 4、到目前為止,最大的元素將在最后的位置。 然后我們將N減少1,并重復步驟1,直到N = 1。
3.3 動圖演示
3.4 代碼演示
public static void bubbleSort(int array[]) {int t = 0;for (int i = 0; i < array.length - 1; i++){for (int j = 0; j < array.length - 1 - i; j++){if (array[j] > array[j + 1]) {t = array[j];array[j] = array[j + 1];array[j + 1] = t;}}}}
四、選擇排序
4.1 選擇排序的介紹
? 選擇排序(Selection-sort)是一種簡單直觀的排序算法。它的工作原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再從剩余未排序元素中繼續尋找最小(大)元素,然后放到已排序序列的末尾。以此類推,直到所有元素均排序完畢。
4.2 選擇排序的原理
? 1、在 [L … N-1] 范圍內找出最小項目 X 的位置,
? 2、用第 L 項交換X,
? 3、將下限 L 增加1并重復步驟1直到 L = N-2。
4.3 動態圖演示
4.4 代碼演示
public static void selectionSort(int[] array) {for (int i = 0; i < array.length; i++) {int index = i;for (int j = i; j < array.length; j++) {if (array[j] < array[index]) index = j; }int temp = array[index];array[index] = array[i];array[i] = temp;}}
五、插入排序
5.1 插入排序的介紹
? 插入排序(Insertion sort)是一種簡單直觀且穩定的排序算法。如果有一個已經有序的數據序列,要求在這個已經排好的數據序列中插入一個數,但要求插入后此數據序列仍然有序,這個時候就要用到一種新的排序方法——插入排序法,插入排序的基本操作就是將一個數據插入到已經排好序的有序數據中,從而得到一個新的、個數加一的有序數據,算法適用于少量數據的排序,時間復雜度為O(n^2)。是穩定的排序方法。插入算法把要排序的數組分成兩部分:第一部分包含了這個數組的所有元素,但將最后一個元素除外(讓數組多一個空間才有插入的位置),而第二部分就只包含這一個元素(即待插入元素)。在第一部分排序完成后,再將這個最后元素插入到已排好序的第一部分中。
5.2 插入排序的原理
? 1、從第一個元素開始,該元素可以認為已經被排序;
? 2、取出下一個元素,在已經排序的元素序列中從后向前掃描;
? 3、如果該元素(已排序)大于新元素,將該元素移到下一位置;
? 4、重復步驟3,直到找到已排序的元素小于或者等于新元素的位置;
? 5、將新元素插入到該位置后;
? 6、重復步驟2~5。
5.3 動態圖演示
5.4 代碼演示
public static void insertionSort(int[] array) {int current;for (int i = 0; i < array.length - 1; i++) {current = array[i + 1];int preIndex = i;while (preIndex >= 0 && current < array[preIndex]) {array[preIndex + 1] = array[preIndex];preIndex--;}array[preIndex + 1] = current;}}
六、歸并排序
6.1 歸并排序的介紹
? 歸并排序(MERGE-SORT)是建立在歸并操作上的一種有效的排序算法,該算法是采用分治法(Divide and Conquer)的一個非常典型的應用。將已有序的子序列合并,得到完全有序的序列;即先使每個子序列有序,再使子序列段間有序。若將兩個有序表合并成一個有序表,稱為二路歸并。
6.2 歸并排序的原理
? 1、將每對單個元素(默認情況下,已排序)歸并為2個元素的有序數組,
? 2、將2個元素的每對有序數組歸并成4個元素的有序數組,重復這個過程…,
? 3、最后一步:歸并2個N / 2元素的排序數組(為了簡化討論,我們假設N是偶數)以獲得完全排序的N個元素數組。
6.3 動態圖演示
6.4 代碼演示
public static int[] MergeSort(int[] array) {if (array.length < 2) return array;int mid = array.length / 2;int[] left = Arrays.copyOfRange(array, 0, mid);int[] right = Arrays.copyOfRange(array, mid, array.length);return merge(MergeSort(left), MergeSort(right));}public static int[] merge(int[] left, int[] right) {int[] result = new int[left.length + right.length];for (int index = 0, i = 0, j = 0; index < result.length; index++) {if (i >= left.length)result[index] = right[j++];else if (j >= right.length)result[index] = left[i++];else if (left[i] > right[j])result[index] = right[j++];elseresult[index] = left[i++];}return result;}
七、快速排序
7.1 快速排序的介紹
? 快速排序(Quicksort)是對冒泡排序的一種改進。
? 它的基本思想是:通過一趟排序將要排序的數據分割成獨立的兩部分,其中一部分的所有數據都比另外一部分的所有數據都要小,然后再按此方法對這兩部分數據分別進行快速排序,整個排序過程可以遞歸進行,以此達到整個數據變成有序序列。
7.2 快速排序的原理
? 1、從數列中挑出一個元素,稱為 “基準”(pivot);
? 2、重新排序數列,所有元素比基準值小的擺放在基準前面,所有元素比基準值大的擺在基準的后面(相同的數可以到任一邊)。在這個分區退出之后,該基準就處于數列的中間位置。這個稱為分區(partition)操作;
? 3、遞歸地(recursive)把小于基準值元素的子數列和大于基準值元素的子數列排序。
7.3 動態圖演示
7.4 代碼演示
/*** 快速排序方法* @param array* @param start* @param end* @return*/public static int[] QuickSort(int[] array, int start, int end) {if (array.length < 1 || start < 0 || end >= array.length || start > end) return null;int smallIndex = partition(array, start, end);if (smallIndex > start)QuickSort(array, start, smallIndex - 1);if (smallIndex < end)QuickSort(array, smallIndex + 1, end);return array;}/*** 快速排序算法——partition* @param array* @param start* @param end* @return*/public static int partition(int[] array, int start, int end) {int pivot = (int) (start + Math.random() * (end - start + 1));int smallIndex = start - 1;swap(array, pivot, end);for (int i = start; i <= end; i++)if (array[i] <= array[end]) {smallIndex++;if (i > smallIndex)swap(array, i, smallIndex);}return smallIndex;}/*** 交換數組內兩個元素* @param array* @param i* @param j*/public static void swap(int[] array, int i, int j) {int temp = array[i];array[i] = array[j];array[j] = temp;}
八、希爾排序
8.1 希爾排序的介紹
? 希爾排序(Shell’s Sort)是插入排序的一種又稱“縮小增量排序”(Diminishing Increment Sort),是直接插入排序算法的一種更高效的改進版本。希爾排序是非穩定排序算法。該方法因D.L.Shell于1959年提出而得名。
? 希爾排序是把記錄按下標的一定增量分組,對每組使用直接插入排序算法排序;隨著增量逐漸減少,每組包含的關鍵詞越來越多,當增量減至1時,整個文件恰被分成一組,算法便終止。
8.2 希爾排序的原理
? 1、選擇一個增量序列t1,t2,…,tk,其中ti>tj,tk=1;
? 2、按增量序列個數k,對序列進行k 趟排序;
? 3、每趟排序,根據對應的增量ti,將待排序列分割成若干長度為m 的子序列,分別對各子表進行直接插入排序。僅增量因子為1 時,整個序列作為一個表來處理,表長度即為整個序列的長度。
8.3 動態圖演示
8.4 代碼演示
/*** 希爾排序** @param array* @return*/public static int[] ShellSort(int[] array) {int len = array.length;int temp, gap = len / 2;while (gap > 0) {for (int i = gap; i < len; i++) {temp = array[i];int preIndex = i - gap;while (preIndex >= 0 && array[preIndex] > temp) {array[preIndex + gap] = array[preIndex];preIndex -= gap;}array[preIndex + gap] = temp;}gap /= 2;}return array;}
九、計數排序
9.1 計數排序的介紹.
? 計數排序的核心在于將輸入的數據值轉化為鍵存儲在額外開辟的數組空間中。 作為一種線性時間復雜度的排序,計數排序要求輸入的數據必須是有確定范圍的整數。
? 計數排序(Counting sort)是一種穩定的排序算法。計數排序使用一個額外的數組C,其中第i個元素是待排序數組A中值等于i的元素的個數。然后根據數組C來將A中的元素排到正確的位置。它只能對整數進行排序。
9.2 計數排序的原理
? 1、找出待排序的數組中最大和最小的元素;
? 2、統計數組中每個值為i的元素出現的次數,存入數組C的第i項;
? 3、對所有的計數累加(從C中的第一個元素開始,每一項和前一項相加);
? 4、反向填充目標數組:將每個元素i放在新數組的第C(i)項,每放一個元素就將C(i)減去1。
9.3 動態圖演示
9.4 代碼演示
/*** 計數排序** @param array* @return*/public static int[] CountingSort(int[] array) {if (array.length == 0) return array;int bias, min = array[0], max = array[0];for (int i = 1; i < array.length; i++) {if (array[i] > max)max = array[i];if (array[i] < min)min = array[i];}bias = 0 - min;int[] bucket = new int[max - min + 1];Arrays.fill(bucket, 0);for (int i = 0; i < array.length; i++) {bucket[array[i] + bias]++;}int index = 0, i = 0;while (index < array.length) {if (bucket[i] != 0) {array[index] = i - bias;bucket[i]--;index++;} elsei++;}return array;}
十、桶排序
10.1桶排序的介紹
? 桶排序 (Bucket sort)或所謂的箱排序,是一個排序算法,工作的原理是將數組分到有限數量的桶子里。每個桶子再個別排序(有可能再使用別的排序算法或是以遞歸方式繼續使用桶排序進行排序)。桶排序是鴿巢排序的一種歸納結果。但桶排序并不是 比較排序,他不受到 O(n log n) 下限的影響。
10.2 桶排序的原理
? 1、設置一個定量的數組當作空桶;
? 2、遍歷輸入數據,并且把數據一個一個放到對應的桶里去;
? 3、對每個不是空的桶進行排序;
? 4、從不是空的桶里把排好序的數據拼接起來。
10.3 動態圖演示
10.4 代碼演示
/*** 桶排序* * @param array* @param bucketSize* @return*/public static ArrayList<Integer> BucketSort(ArrayList<Integer> array, int bucketSize) {if (array == null || array.size() < 2)return array;int max = array.get(0), min = array.get(0);// 找到最大值最小值for (int i = 0; i < array.size(); i++) {if (array.get(i) > max)max = array.get(i);if (array.get(i) < min)min = array.get(i);}int bucketCount = (max - min) / bucketSize + 1;ArrayList<ArrayList<Integer>> bucketArr = new ArrayList<>(bucketCount);ArrayList<Integer> resultArr = new ArrayList<>();for (int i = 0; i < bucketCount; i++) {bucketArr.add(new ArrayList<Integer>());}for (int i = 0; i < array.size(); i++) {bucketArr.get((array.get(i) - min) / bucketSize).add(array.get(i));}for (int i = 0; i < bucketCount; i++) {if (bucketSize == 1) { // 如果帶排序數組中有重復數字時 感謝 @見風任然是風 朋友指出錯誤for (int j = 0; j < bucketArr.get(i).size(); j++)resultArr.add(bucketArr.get(i).get(j));} else {if (bucketCount == 1)bucketSize--;ArrayList<Integer> temp = BucketSort(bucketArr.get(i), bucketSize);for (int j = 0; j < temp.size(); j++)resultArr.add(temp.get(j));}}return resultArr;}
十一、基數排序
11.1 基數排序的介紹
? 基數排序也是非比較的排序算法,對每一位進行排序,從最低位開始排序,復雜度為O(kn),為數組長度,k為數組中的數的最大的位數;
? 基數排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次類推,直到最高位。有時候有些屬性是有優先級順序的,先按低優先級排序,再按高優先級排序。最后的次序就是高優先級高的在前,高優先級相同的低優先級高的在前。基數排序基于分別排序,分別收集,所以是穩定的
11.2 基數排序的原理
? 1、取得數組中的最大數,并取得位數;
? 2、arr為原始數組,從最低位開始取每個位組成radix數組;
? 3、對radix進行計數排序(利用計數排序適用于小范圍數的特點);
11.3 動態圖演示
11.4 代碼演示
/*** 基數排序* @param array* @return*/public static int[] RadixSort(int[] array) {if (array == null || array.length < 2)return array;// 1.先算出最大數的位數;int max = array[0];for (int i = 1; i < array.length; i++) {max = Math.max(max, array[i]);}int maxDigit = 0;while (max != 0) {max /= 10;maxDigit++;}int mod = 10, div = 1;ArrayList<ArrayList<Integer>> bucketList = new ArrayList<ArrayList<Integer>>();for (int i = 0; i < 10; i++)bucketList.add(new ArrayList<Integer>());for (int i = 0; i < maxDigit; i++, mod *= 10, div *= 10) {for (int j = 0; j < array.length; j++) {int num = (array[j] % mod) / div;bucketList.get(num).add(array[j]);}int index = 0;for (int j = 0; j < bucketList.size(); j++) {for (int k = 0; k < bucketList.get(j).size(); k++)array[index++] = bucketList.get(j).get(k);bucketList.get(j).clear();}}return array;}
十二、堆排序
12.1 堆排序的介紹
? 堆排序(英語:Heapsort)是指利用堆這種數據結構所設計的一種排序算法。堆是一個近似完全二叉樹的結構,并同時滿足堆積的性質:即子結點的鍵值或索引總是小于(或者大于)它的父節點。
12.2 堆排序的原理
? 1、將初始待排序關鍵字序列(R1,R2….Rn)構建成大頂堆,此堆為初始的無序區;
? 2、將堆頂元素R[1]與最后一個元素R[n]交換,此時得到新的無序區(R1,R2,……Rn-1)和新的有序區(Rn),且滿足R[1,2…n-1]<=R[n];
? 3、由于交換后新的堆頂R[1]可能違反堆的性質,因此需要對當前無序區(R1,R2,……Rn-1)調整為新堆,然后再次將R[1]與無序區最后一個元素交換,得到新的無序區(R1,R2….Rn-2)和新的有序區(Rn-1,Rn)。不斷重復此過程直到有序區的元素個數為n-1,則整個排序過程完成。
12.3 動態圖演示
12.4 代碼演示
//聲明全局變量,用于記錄數組array的長度;
static int len;/*** 堆排序算法** @param array* @return*/public static int[] HeapSort(int[] array) {len = array.length;if (len < 1) return array;//1.構建一個最大堆buildMaxHeap(array);//2.循環將堆首位(最大值)與末位交換,然后在重新調整最大堆while (len > 0) {swap(array, 0, len - 1);len--;adjustHeap(array, 0);}return array;}/*** 建立最大堆** @param array*/public static void buildMaxHeap(int[] array) {//從最后一個非葉子節點開始向上構造最大堆for (int i = (len/2 - 1); i >= 0; i--) { //感謝 @讓我發會呆 網友的提醒,此處應該為 i = (len/2 - 1) adjustHeap(array, i);}}/*** 調整使之成為最大堆** @param array* @param i*/public static void adjustHeap(int[] array, int i) {int maxIndex = i;//如果有左子樹,且左子樹大于父節點,則將最大指針指向左子樹if (i * 2 < len && array[i * 2] > array[maxIndex])maxIndex = i * 2;//如果有右子樹,且右子樹大于父節點,則將最大指針指向右子樹if (i * 2 + 1 < len && array[i * 2 + 1] > array[maxIndex])maxIndex = i * 2 + 1;//如果父節點不是最大值,則將父節點與最大值交換,并且遞歸調整與父節點交換的位置。if (maxIndex != i) {swap(array, maxIndex, i);adjustHeap(array, maxIndex);}}