快速排序通常採用遞歸方法(詳見快速排序及其優化),但遞歸方法通常均可以用循環代替。本文實現了java版的非遞歸快速排序。html
更多:數據結構與算法合集java
採用非遞歸的方法,首先要想到棧的使用,經過閱讀遞歸調用部分的代碼,思考如何用棧來代替。遞歸調用的核心代碼是 pivot = partition(a, low, high); 每次循環都必須包含這句核心代碼,能夠想到,若是要對該行代碼實現循環,只能對low和high採起操做,因此咱們在棧中壓入low和high,每一個循環彈出一對low和high,用於核心代碼的實現,當棧空後就說明沒有須要排序的部分了,結束循環。
下面是遞歸代碼和根據遞歸代碼修改爲的非遞歸代碼。算法
遞歸部分代碼:數組
/** * 遞歸調用 */ public void qSort(int[] a, int low, int high) { int pivot; if (low >= high) return; //原始遞歸操做 // pivot = partition(a, low, high); // 將數列一分爲二 // qSort(a, low, pivot - 1); // 對低子表排序 // qSort(a, pivot + 1, high); // 對高子表排序 // 優化遞歸操做 while (low < high) { pivot = partition(a, low, high); // 將數列一分爲二 qSort(a, low, pivot - 1); // 對低子表排序 low = pivot + 1; } }
修改爲的非遞歸代碼:數據結構
/** * 非遞歸 */ public void qSort2(int[] a, int low, int high) { int pivot; if (low >= high) return; Stack<Integer> stack = new Stack<Integer>(); stack.push(low); stack.push(high); while (!stack.empty()) { // 先彈出high,再彈出low high = stack.pop(); low = stack.pop(); pivot = partition(a, low, high); // 先壓low,再壓high if (low < pivot - 1) { stack.push(low); stack.push(pivot - 1); } if (pivot + 1 < high) { stack.push(pivot + 1); stack.push(high); } } }
注意點:棧彈出的順序與壓入的順序相反,要當心棧的壓入與彈出操做。ide
(含測試代碼)post
import java.util.Arrays; import java.util.Stack; /** * * @Description 快速排序的遞歸與非遞歸實現 * * @author yongh * @date 2018年9月14日 下午2:39:00 */ public class QuickSort { public void quickSort(int[] a) { if (a == null) return; qSort(a, 0, a.length - 1); } /** * 遞歸 */ public void qSort(int[] a, int low, int high) { int pivot; if (low >= high) return; //原始遞歸操做 // pivot = partition(a, low, high); // 將數列一分爲二 // qSort(a, low, pivot - 1); // 對低子表排序 // qSort(a, pivot + 1, high); // 對高子表排序 // 優化遞歸操做 while (low < high) { pivot = partition(a, low, high); // 將數列一分爲二 qSort(a, low, pivot - 1); // 對低子表排序 low = pivot + 1; } } public void quickSort2(int[] a) { if (a == null) return; qSort2(a, 0, a.length - 1); } /** * 非遞歸 */ public void qSort2(int[] a, int low, int high) { int pivot; if (low >= high) return; Stack<Integer> stack = new Stack<Integer>(); stack.push(low); stack.push(high); while (!stack.empty()) { // 先彈出high,再彈出low high = stack.pop(); low = stack.pop(); pivot = partition(a, low, high); // 先壓low,再壓high if (low < pivot - 1) { stack.push(low); stack.push(pivot - 1); } if (pivot + 1 < high) { stack.push(pivot + 1); stack.push(high); } } } /** * 對數組a中下標從low到high的元素,選取基準元素pivotKey, * 根據與基準比較的大小,將各個元素排到基準元素的兩端。 * 返回值爲最後基準元素的位置 */ public int partition(int[] a, int low, int high) { // 三數取中,將中間元素放在第一個位置 if (a[low] > a[high]) swap(a, low, high); if (a[(low + high) / 2] > a[high]) swap(a, (low + high) / 2, high); if (a[low] < a[(low + high) / 2]) swap(a, (low + high) / 2, low); int pivotKey = a[low]; // 用第一個元素做爲基準元素 while (low < high) { // 兩側交替向中間掃描 while (low < high && a[high] >= pivotKey) high--; a[low] = a[high]; // swap(a, low, high); //比基準小的元素放到低端 while (low < high && a[low] <= pivotKey) low++; a[high] = a[low]; // swap(a, low, high); //比基準大的元素放到高端 } a[low] = pivotKey; // 在中間位置放回基準值 return low; // 返回基準元素所在位置 } public void swap(int[] a, int i, int j) { int temp; temp = a[j]; a[j] = a[i]; a[i] = temp; } // =========測試代碼======= //測試的爲非遞歸方法quickSort2() public void test1() { int[] a = null; quickSort2(a); System.out.println(Arrays.toString(a)); } public void test2() { int[] a = {}; quickSort2(a); System.out.println(Arrays.toString(a)); } public void test3() { int[] a = { 1 }; quickSort2(a); System.out.println(Arrays.toString(a)); } public void test4() { int[] a = { 3, 3, 3, 3, 3 }; quickSort2(a); System.out.println(Arrays.toString(a)); } public void test5() { int[] a = { -3, 6, 3, 1, 3, 7, 5, 6, 2 }; quickSort2(a); System.out.println(Arrays.toString(a)); } public static void main(String[] args) { QuickSort demo = new QuickSort(); demo.test1(); demo.test2(); demo.test3(); demo.test4(); demo.test5(); } }
null [] [1] [3, 3, 3, 3, 3] [-3, 1, 2, 3, 3, 5, 6, 6, 7]
遞歸改成非遞歸,聯想到棧的使用,根據對核心代碼的循環,肯定棧中存儲什麼數據。測試
更多:數據結構與算法合集優化