你們好,這是本人算法系列最後一篇,介紹回溯算法。感謝你們支持,但願指正。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,裁剪相應邊集Dbash
簡單推測:性能
目前邊集中最大值爲6,要麼x3=6,要麼x2=4. 若x3=6,x4-x3=1,而剩餘邊集D中沒有1,即x3=6不成立ui
若是x2=4,x2-xo=4和x5-x2=4,而剩餘邊集D中僅有1個4,即x2=4也不成立。 由此可知,x4=7不成立咱們須要進行回溯spa
嘗試x4=7不成立,回溯到初始狀態.net
嘗試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+1]>n-r+resultIndex,進行回溯,即resultIndex--;a[resultIndex]=a[resultIndex]+1;
當resultIndex==r 而且 a[resultIndex+1]==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; // 搜索到下一個數
}
}
}
}
複製代碼