1.6道经典 递归、回溯、分治的相关题目
预备知识:递归函数与回溯算法
例1-a: 求子集(medium) (回溯法、位运算法)
例1-b: 求子集2(medium) (回溯法)
例1-c: 组合数之和2(medium) (回溯法、剪枝)
例2:生成括号(medium) (递归设计)
例3:N皇后 (hard) (回溯法)
预备知识:分治算法与归并排序
例4:逆序数 (hard) (分治法、归并排序应用)
例1-a: 求子集(medium) (回溯法、位运算法)
- 子集
给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例:
输入: nums = [1,2,3]
输出:
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]
public class Solution {
public List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> result=new ArrayList<>();
List<Integer> item=new ArrayList<>();
result.add(item);
generate(0,nums,item,result);
return result;
}
//生成item和result
public void generate(int i,int[] nums,List<Integer> item,List<List<Integer>> result){
if(i>=nums.length){
return;
}
item.add(nums[i]);
System.out.println(nums[i]);
result.add(new ArrayList<>(item));
generate(i+1,nums,item,result);
item.remove(item.size()-1);
generate(i+1,nums,item,result);
}
}
public class Solution {
public List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> result=new ArrayList<>();
int size=1<<nums.length;
for(int i=0;i<size;i++){
List<Integer> item=new ArrayList<>();
for(int j=0;j<nums.length;j++){
if((i&(1<<j))!=0){
item.add(nums[j]);
}
}
result.add(item);
}
return result;
}
}
例1-b: 求子集2(medium) (回溯法)
- 子集 II
给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例:
输入: [1,2,2]
输出:
[
[2],
[1],
[1,2,2],
[2,2],
[1,2],
[]
]
class Solution {
public List<List<Integer>> subsetsWithDup(int[] nums) {
HashSet<List<Integer>> temp=new HashSet<>();
List<List<Integer>> result=new ArrayList<>();
List<Integer> item=new ArrayList<>();
result.add(new ArrayList<>(item));
Arrays.sort(nums);
generate(0,nums,item,temp);
for(List<Integer> iterator:temp){
result.add(iterator);
}
return result;
}
public void generate(int i,int[] nums,List<Integer> item,HashSet<List<Integer>> temp){
if(i==nums.length){
return;
}
item.add(nums[i]);
temp.add(new ArrayList<>(item));
generate(i+1,nums,item,temp);
item.remove(item.size()-1);
generate(i+1,nums,item,temp);
}
}
例1-c: 组合数之和2(medium) (回溯法、剪枝)
- 组合总和 II
给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用一次。
说明:
所有数字(包括目标数)都是正整数。
解集不能包含重复的组合。
示例 1:
输入: candidates = [10,1,2,7,6,1,5], target = 8,
所求解集为:
[
[1, 7],
[1, 2, 5],
[2, 6],
[1, 1, 6]
]
示例 2:
输入: candidates = [2,5,2,1,2], target = 5,
所求解集为:
[
[1,2,2],
[5]
]
class Solution {
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
List<List<Integer>> result=new ArrayList<>();
HashSet<List<Integer>> temp=new HashSet<>();
List<Integer> item=new ArrayList<>();
int sum=0;
Arrays.sort(candidates);
generate(0,candidates,target,0,temp,item,result);
return result;
}
public void generate(int i,int[] candidates,int target,int sum,HashSet<List<Integer>> temp,List<Integer> item,List<List<Integer>> result){
if(i==candidates.length||sum>=target){
return;
}
item.add(candidates[i]);
sum+=candidates[i];
if(sum==target&&!temp.contains(item)){
temp.add(new ArrayList<>(item));
result.add(new ArrayList<>(item));
}
generate(i+1,candidates,target,sum,temp,item,result);
item.remove(item.size()-1);
sum-=candidates[i];
generate(i+1,candidates,target,sum,temp,item,result);
}
}
例2:生成括号(medium) (递归设计)
- 括号生成
数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
示例:
输入:n = 3
输出:[
“((()))”,
“(()())”,
“(())()”,
“()(())”,
“()()()”
]
public class Solution {
public List<String> generateParenthesis(int n) {
List<String> result=new ArrayList<>();
generate("",n,n,result);
return result;
}
public void generate(String item,int left,int right,List<String> result){
if(left==0&&right==0){
result.add(item);
return;
}
if(left>0){
generate(item+"(",left-1,right,result);
}
if(right>left){
generate(item+")",left,right-1,result);
}
}
}
例3:N皇后 (hard) (回溯法)
- N 皇后
n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
上图为 8 皇后问题的一种解法。
给定一个整数 n,返回所有不同的 n 皇后问题的解决方案。
每一种解法包含一个明确的 n 皇后问题的棋子放置方案,该方案中 ‘Q’ 和 ‘.’ 分别代表了皇后和空位。
示例:
输入:4
输出:[
[".Q…", // 解法 1
“…Q”,
“Q…”,
“…Q.”],
["…Q.", // 解法 2
“Q…”,
“…Q”,
“.Q…”]
]
解释: 4 皇后问题存在两个不同的解法。
提示:
皇后彼此不能相互攻击,也就是说:任何两个皇后都不能处于同一条横行、纵行或斜线上。
import java.util.ArrayList;
import java.util.List;
public class Solution {
public List<List<String>> solveNQueens(int n) {
List<List<String>> result=new ArrayList<>();//结果
List<String> local=new ArrayList<>();
int[][] mark=new int[n][n];
generate(0,n,result,local,mark);
return result;
}
//按行放置n皇后
void generate(int k,int n,List<List<String>> result,List<String> local,int [][] mark){
if(k==n){
result.add(new ArrayList<>(local));
return;
}
for(int i=0;i<n;i++){
if(mark[k][i]==0){
int markTemp[][]=new int[n][n];
for(int x=0;x<n;x++){
for(int y=0;y<n;y++){
markTemp[x][y]=mark[x][y];
}
}
String temp="";
for(int j=0;j<n;j++){
if(j==i){
temp=temp+"Q";
}else{
temp=temp+".";
}
}
local.add(temp);
mark[k][i]=1;
bulidMark(k,i,n,mark);//标记皇后辐射的位置
generate(k+1,n,result,local,mark);
local.remove(local.size()-1);
mark=markTemp;
}
}
}
void bulidMark(int x,int y,int n,int [][] mark){
int xDirection[]={-1,-1,-1,0,0,1,1,1};
int yDirection[]={-1,0,1,-1,1,-1,0,1};
for(int i=0;i<n;i++){
for(int j=0;j<8;j++){
int newX=x+i*xDirection[j];
int newY=y+i*yDirection[j];
if(newX>=0&&newY>=0&&newX<n&&newY<n){
mark[newX][newY]=1;
}
}
}
}
}
例4:逆序数 (hard) (分治法、归并排序应用)
- 计算右侧小于当前元素的个数
给定一个整数数组 nums,按要求返回一个新数组 counts。数组 counts 有该性质: counts[i] 的值是 nums[i] 右侧小于 nums[i] 的元素的数量。
示例:
输入:nums = [5,2,6,1]
输出:[2,1,1,0]
解释:
5 的右侧有 2 个更小的元素 (2 和 1)
2 的右侧仅有 1 个更小的元素 (1)
6 的右侧有 1 个更小的元素 (1)
1 的右侧有 0 个更小的元素
提示:
0 <= nums.length <= 10^5
-10^4 <= nums[i] <= 10^4
public class Solution {
public List<Integer> countSmaller(int[] nums) {
List<Integer> count=new ArrayList<>();
if(nums.length==0){
return count;
}
//Pair<Integer,Integer> pair=new Pair<>();
//List<Integer> count=new ArrayList<>();
List<Pair<Integer,Integer>> vec=new ArrayList<>();
for(int i=0;i<nums.length;i++){
count.add(0);
vec.add(new Pair<>(nums[i],i));
}
mergeSort(vec,count);
return count;
}
public void mergeSort(List<Pair<Integer,Integer>> vec,List<Integer> count){
if(vec.size()<2){
return;
}
int mid=vec.size()/2;
List<Pair<Integer,Integer>> vec1=new ArrayList<>();
List<Pair<Integer,Integer>> vec2=new ArrayList<>();
for(int i=0;i<mid;i++){
vec1.add(vec.get(i));
}
for(int i=mid;i<vec.size();i++){
vec2.add(vec.get(i));
}
mergeSort(vec1,count);
mergeSort(vec2,count);
vec.clear();
mergeTwo(vec1,vec2,vec,count);
}
public void mergeTwo(List<Pair<Integer,Integer>> vec1,List<Pair<Integer,Integer>> vec2,List<Pair<Integer,Integer>> vec,List<Integer> count){
int i=0;
int j=0;
while(i<vec1.size()&&j<vec2.size()){
if(vec1.get(i).getKey()<=vec2.get(j).getKey()){
vec.add(vec1.get(i));
int count_i=vec1.get(i).getValue();
count.set(count_i,count.get(count_i)+j);
//if(count_i==1)System.out.println(vec1.get(i)+","+(count_j+j));
i++;
}else{
vec.add(vec2.get(j));
j++;
}
}
while(i<vec1.size()){
vec.add(vec1.get(i));
int count_i=vec1.get(i).getValue();
count.set(count_i,count.get(count_i)+j);
//if(count_i==1)System.out.println(vec1.get(i)+","+(count_j+j));
i++;
}
while(j<vec2.size()){
vec.add(vec2.get(j));
j++;
}
}
}

本文精选六道经典算法题目,涵盖递归、回溯与分治等核心算法,深入解析求子集、生成括号、N皇后等问题的实现方法。
&spm=1001.2101.3001.5002&articleId=109344146&d=1&t=3&u=e8713d5477b14df390481605ef62e015)
635

被折叠的 条评论
为什么被折叠?



