回溯算法(back tracking)實際上一個相似枚舉的搜索嘗試過程,主要是在搜索嘗試過程當中尋找問題的解,當發現已不知足求解條件時,就「回溯」返回,嘗試別的路徑。算法
能夠理解爲深度優先算法。
經過枚舉各類類型,根據條件限制,獲得符合條件的結果。數組
通常用於要求出全部可能的結果。例如排列組合等等。
回溯算法框架:框架
result = [] def backtrack(路徑, 選擇列表): if 知足結束條件: result.add(路徑) return for 選擇 in 選擇列表: 作選擇 backtrack(路徑, 選擇列表) 撤銷選擇
以全排列爲例:code
給定一個不含重複數字的數組 nums ,返回其 全部可能的全排列 。你能夠 按任意順序 返回答案。
int **ret = NULL; int ret_num = 0; int *array = NULL; int array_num = 0; int *used = NULL; void backtrack(int *nums, int numsSize, int index) { if(index == numsSize) { int *temp = malloc(sizeof(int) * numsSize); memcpy(temp, array, sizeof(int) * numsSize); ret[ret_num] = temp; ret_num++; } else { for(int i =0; i < numsSize; i++) { if(used[i] == 1) continue; array[array_num] = nums[i]; array_num++; used[i] = 1; backtrack(nums, numsSize, array_num);//array num 其實就是遍歷的深度。 used[i] = 0; array_num--; } } } int** permute(int* nums, int numsSize, int* returnSize, int** returnColumnSizes) { if(numsSize <= 0) { *returnSize = 0; return ret; } ret_num = array_num = 0; int total_num = 1; for(int i = 1; i <= numsSize; i++) total_num *= i; used = malloc(sizeof(int) * numsSize); memset(used, 0, sizeof(int) * numsSize); ret = malloc(sizeof(int*) * total_num); array = malloc(sizeof(int) * numsSize); backtrack(nums, numsSize, 0); *returnColumnSizes = malloc(sizeof(int) * total_num); for(int i = 0; i < total_num; i++) (*returnColumnSizes)[i] = numsSize; *returnSize = total_num; return ret; }
此處使用一個數組來表示數字是否已經使用。每次選擇只選擇沒有使用的數字。
重點是每次backtracking完之後,要恢復現場,恢復到backtracking的上一步,保證對下次選擇沒有影響。get
又如組合問題:for循環
給定兩個整數 n 和 k,返回範圍 [1, n] 中全部可能的 k 個數的組合。 你能夠按 任何順序 返回答案。
int **ret = NULL; int ret_num = 0; int *array = NULL; int array_size = 0; long long get_num(int n) { long long num = 1; for(int i =1; i <= n; i++) { num *= i; } return num; } void dfs(int *table, int n, int k, int deepth, int m)//m防止出現相同的組合。 { if(deepth == k) { int *temp = malloc(sizeof(int) * k); for(int i =0; i < k; i++) temp[i] = array[i]; ret[ret_num] = temp; ret_num++; } else { for(int i = m; i <= n; i++) { if(table[i] == 0) continue; array[array_size++] = i; table[i] = 0; dfs(table, n, k , array_size, i);//i防止出現重複的結果。 array_size--; table[i] = 1; } } } int** combine(int n, int k, int* returnSize, int** returnColumnSizes) { ret_num = array_size = 0; int *table = malloc(sizeof(int) * (n + 1)); table[0] = 0; for(int i = 1; i <= n; i++) table[i] = 1; int num = get_num(n)/get_num(k)/get_num(n-k); ret = malloc(sizeof(int *) * num); array = malloc(sizeof(int) * k); dfs(table, n, k, 0, 0); *returnSize = ret_num; *returnColumnSizes = malloc(sizeof(int) * num); for(int i =0; i < num; i++) (*returnColumnSizes)[i] = k; free(table); free(array); return ret; }
一樣的,使用一個數組來存儲數據是否被選擇。
每次backtracking完之後要恢復現場。
同時在組合過程當中,爲了防止出現相同的數據,例如:1,2,3和3,2,1和1,3,2等狀況。
先for循環中,須要跳過選擇過的數據,日後循環。table