你們好,這是本人算法系列最後一篇,介紹回溯算法。感謝你們支持,但願指正。java
回溯算法至關於窮舉搜索的巧妙實現,可是性能通常不理想。回溯算法中常常使用裁剪,
裁剪,即在一步刪除一大組可能性的作法。
下面以兩個例子進行說明。git
設給定N個點P1,P2,.......,PN,它們位於X軸上。Xi是Pi點的X座標。進一步假設X1=0以及這些點從左到右給出。着N個點肯定,在每一對,點間的N(N-1)/2個,形如|Xi - Xj|(i !=j)的距離。收費公路重建問題就是由這些距離重構一個點集。github
以書上例子說明: 令D是距離集合,且D={1,2,2,2,3,3,3,4,5,5,5,6,7,8,10},由D=15能夠知道N=6,若設X1=0,則有x6=10,x5=8。算法
下面以X1=0,x6=10,x5=8爲初始狀態進行嘗試。數組
目前邊集中最大值爲7,要麼x4=7,要麼x2=3,咱們分別進行嘗試
嘗試x4=7,裁剪相應邊集D性能
簡單推測:.net
目前邊集中最大值爲6,要麼x3=6,要麼x2=4. 若x3=6,x4-x3=1,而剩餘邊集D中沒有1,即x3=6不成立3d
若是x2=4,x2-xo=4和x5-x2=4,而剩餘邊集D中僅有1個4,即x2=4也不成立。 由此可知,x4=7不成立咱們須要進行回溯code
嘗試x4=7不成立,回溯到初始狀態blog
嘗試x2=3 裁剪相應邊集D
簡單推測: 目前邊集中最大值爲6,要麼x4=6,要麼x2=4.
若是x2=4,x2-xo=4和x5-x2=4,而剩餘邊集D中僅有1個4,即x2=4不成立。
若x4=6,x10-x4=4,x5-x4=8-6=2,x4-x2=6-3=3,x4-x0=6-0=6,均在剩餘邊集合D中,x4=6 可選。
嘗試x2=3 基礎上嘗試x4=6,裁剪相應邊集合D
簡單推測:
目前邊集中最大值爲5,要麼x3=5.
X3-x1=5-0=5,x3-x2=5-3=2,x4-x5=6-5=1,x5-x3=8-5=3,x6-x3=10-5=5,整好全部邊集均清空,咱們找到最終答案以下:
/** * @param x * 結果點集合 * @param distSet * 邊的優先隊列,注意隊列從大到小 * @param n * 根據邊集大小計算出來的 預計點個數, n(n-1)/2=distSet.size(); * @return */ public static boolean turnpike(int[] x, PriorityQueue<Integer> distSet, int n) { // 倒序排列邊集合 x[1] = 0; x[n] = distSet.poll(); x[n - 1] = distSet.poll(); if (distSet.contains(x[n] - x[n - 1])) { distSet.remove(new Integer(x[n] - x[n - 1])); return place(x, distSet, n, 2, n - 2); } else { return false; } } /** * 重建高速公路-回溯算法核心 * @param x 結果集合 * @param distSet 剩餘邊集合 從大到小構建的堆 * @param n 預計包含的點集合 * @param left 本次嘗試的左邊界 * @param right 本次嘗試的右邊開始點 */ private static boolean place(int[] x, PriorityQueue<Integer> distSet, int n, int left, int right) { int dmax = 0; boolean found = false; if (distSet.isEmpty()) { return true; } dmax = distSet.peek(); // 嘗試x[right]爲 dmax if (CheckIfRight(x, distSet, n, left, right, dmax)) { x[right] = dmax; for (int j = 1; j < left; j++) { distSet.remove(new Integer(Math.abs(x[j] - x[right]))); } for (int j = right + 1; j <= n; j++) { distSet.remove(new Integer(Math.abs(x[j] - x[right]))); } found = place(x, distSet, n, left, right - 1); if (!found) { for (int j = 1; j < left; j++) { distSet.add(new Integer(Math.abs(x[j] - x[right]))); } for (int j = right + 1; j <= n; j++) { distSet.add(new Integer(Math.abs(x[j] - x[right]))); } } } // x[left]=x[n]-dmax if (!found && CheckIfleft(x, distSet, n, left, right, dmax)) { x[left] = x[n] - dmax; for (int j = 1; j < left; j++) { distSet.remove(new Integer(Math.abs(x[n] - dmax - x[j]))); } for (int j = right + 1; j <= n; j++) { distSet.remove(new Integer(Math.abs(x[n] - dmax - x[j]))); } found = place(x, distSet, n, left + 1, right); if (!found) { for (int j = 1; j < left; j++) { distSet.add(new Integer(Math.abs(x[n] - dmax - x[j]))); } for (int j = right + 1; j <= n; j++) { distSet.add(new Integer(Math.abs(x[n] - dmax - x[j]))); } } } return found; }
例如 n爲5,r爲3,輸出 輸出:
[1, 2, 3] [1, 2, 4] [1, 2, 5]
[1, 3, 4] [1, 3, 5]
[1, 4, 5]
[2, 3, 4] [2, 3, 5]
[2, 4, 5]
[3, 4, 5]
第1位最大值爲n-r+1=3,
第2位的最大值爲n-r+2=4
第3位的最大值爲n-r+3=5
能夠推測出第i位最大值爲n-r+i,也就是每一位超過相應值須要回溯。
當resultIndex<r 咱們進行下探,即a[resultIndex+1]=a[resultIndex]+1
當resultIndexr==r時進行末位試探,即a[resultIndex]=a[resultIndex]+1
當 a[resultIndex]>n-r+resultIndex,進行回溯,即resultIndex--;a[resultIndex]=a[resultIndex]+1;
當resultIndex==r 而且 a[resultIndex]==n-r+resultIndex,進行末位回溯 即resultIndex--;a[resultIndex]=a[resultIndex]+1;
/** * 核心處理方法 * @param selectIndexList 存儲結果 */ private void combOrder(List<int[]> selectIndexList) { if(r==0){ return ; } if(r==1){ for (int i = 0; i<n; i++) { selectIndexList.add(new int[] { i }); } return; } int resultIndex=1; a[1]=1; while(a[1]<=(n-r+1)){//根據第一位進行裁剪,第一位取值爲1<a[1]<n-r+1 列入5個數選3個 第一位最大爲5-3+1=3 if(resultIndex<r){ if(a[resultIndex]<=n-r+resultIndex){ a[resultIndex+1] = a[resultIndex]+1; resultIndex++; }else{// 已經超過每一位的最大值 n-r+resultIndex,進行回溯 resultIndex--; a[resultIndex]=a[resultIndex]+1; } }else if(resultIndex==r){ int[] selectIndex = new int[r]; selectIndexList.add(selectIndex); for (int j = 1; j <= r; j++) {//存儲結果 selectIndex[j - 1] = a[j]-1; } if (a[resultIndex]==n) { // 若當前搜索結果爲1,表示本次搜素完成,進行回溯 此時條件至關爲 ri+a[ri]==r+1 由於ri=r因此簡化啦 resultIndex--; a[resultIndex] = a[resultIndex]+1; // 回溯 } else { a[resultIndex] = a[resultIndex]+1; // 搜索到下一個數 } } } }