暴力递归问题
所有可能的满二叉树
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<TreeNode*> process(int n){//递归,返回n个节点的所有可能满二叉树
vector<TreeNode*>res;
if(n==1){
TreeNode* root=new TreeNode(0);
res.push_back(root);
return res;
}
for(int i=1;i<n-1;++i){//以i为根节点,0~i-1为左树,i+1~n-1为右子树
if((i&1)==0||((n-1-i)&1)==0){
continue;//左右子树的节点个数必须为奇数
}
vector<TreeNode*>left=process(i);
vector<TreeNode*>right=process(n-1-i);
for(auto l:left){
for(auto r:right){
//遍历左子树和右子树根节点
TreeNode* root=new TreeNode(0);
root->left=l;
root->right=r;
res.push_back(root);
}
}
}
return res;
}
vector<TreeNode*> allPossibleFBT(int n) {
return process(n);
}
};
整数替换
class Solution {
public:
int process(int n){//n 变为 1 所需的 最小替换次数
if(n==1)return 0;
if((n&1)==0){//偶数只能/2
return 1+process(n>>1);
}
//奇数: n-1 n+1(存在的问题是:n+1会溢出)
//问题可以转化为: n: n-1/2 n/2+1
return 2+min(process((n-1)>>1),process((n>>1)+1));
}
int integerReplacement(int n) {
return process(n);
}
};
反转链表
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode process(ListNode x){
if(x==null)return null;
ListNode head=process(x.next);
x.next=null;
if(head==null)return x;
//找到尾部节点tilt
ListNode tilt=head;
while(tilt.next!=null){
tilt=tilt.next;
}
tilt.next=x;
return head;
}
public ListNode reverseList(ListNode head) {
return process(head);
}
}
汉诺塔问题
class Solution {
public void process(List<Integer> A, List<Integer> B, List<Integer> C,int n){
if(n==0)return;
//把A n-1个,借助C,移动到B上
process(A,C,B,n-1);
//把A最底下一个直接移动到C上;
C.add(A.remove(A.size()-1));//从后往前去盘子
//把B上的n-1个,借助A,移动到C上
process(B,A,C,n-1);
}
public void hanota(List<Integer> A, List<Integer> B, List<Integer> C) {
int n=A.size();
process(A,B,C,n);
//Collections.sort(C,new Compare());
}
}
移除链表元素
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode process(ListNode x,int val){
if(x==null)return null;
if(x.val!=val){
x.next=process(x.next,val);
return x;
}
x.next=process(x.next,val);
return x.next;
}
public ListNode removeElements(ListNode head, int val) {
return process(head,val);
}
}
合并两个有序链表
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode process(ListNode x,ListNode y){
if(x==null)return y;
if(y==null)return x;
ListNode head=(x.val<=y.val?x:y);
head.next=process((x.val<=y.val?x.next:x),(x.val<=y.val?y:y.next));
return head;
}
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
return process(list1,list2);
}
}
汉诺塔问题
import java.util.Scanner;
public class Hanoi {
public static void Hanoi(int N,String from,String to,String other) {
if(N==1) {
System.out.println("Move "+1+" from "+from+" to "+to);
return;
}
Hanoi(N-1,from,other,to);
System.out.println("Move "+N+" from "+from+" to "+to);
Hanoi(N-1,other,to,from);
}
public static void main(String[] args) {
Scanner scan=new Scanner(System.in);
int N=scan.nextInt();
Hanoi(N,"left","right","mid");
}
}
打印全部子序列
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
* 用来求出一个字符串的全部子序列,包含空字符串(因为这个不好统一输出的子序列,所以按照上课所讲写这个代码)
* @param s string字符串 需要打印全部子序列的那个字符串
* @return string字符串一维数组
*/
public static void process(char[] chs,int index,String path,List<String> ans) {
if(index==chs.length) {
ans.add(path);
return;
}
process(chs,index+1,path,ans);
process(chs,index+1,path+chs[index]+"",ans);
}
public String[] getAllSubs (String s) {
// write code here
List<String> ans=new ArrayList<>();
if(s==null)return null;
char[] chs=s.toCharArray();
process(chs,0,"",ans);
String[] list=new String[ans.size()];
for(int i=0;i<ans.size();++i){
list[i]=ans.get(i);
}
return list;
}
}
全排列
注意: 我这里实现的代码可以同样处理字符串含有重复字母的情况,但是要想按照字典序排列输出,必须提前对目标字符串进行排序。
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Scanner;
import java.util.Set;
//全排列
public class Main {
public static List<String> getAllPermutation(String s){
if(s==null)return null;
List<Character>chs=new ArrayList<>();
for(char ch:s.toCharArray()) {
chs.add(ch);
}
List<String> ans=new ArrayList<>();
process(chs,"",ans);
return ans;
}
public static void process(List<Character> chs,String path,List<String> ans) {
if(chs.isEmpty()) {
ans.add(path);
return;
}
Set<Character>hashSet=new HashSet<>();
for(int i=0;i<chs.size();++i) {
if(!hashSet.contains(chs.get(i))) {
//会报错,属于指针:List<Character>tmp=chs;
List<Character>tmp=new ArrayList<>(chs);//复制值
tmp.remove(i);
process(tmp,path+chs.get(i),ans);
hashSet.add(chs.get(i));
}
}
}
public static void main(String[] args) {
Scanner scan=new Scanner(System.in);
String str=scan.nextLine();
List<String> ans=getAllPermutation(str);
for(String s:ans) {
System.out.println(s);
}
}
}
数字转换为字符串
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
* 用来计算一个数字有多少种不同的转换方法
* @param number string字符串 给定的数字
* @return int整型
*/
public static int process(char[] chs,int index){
if(index==chs.length)return 1;
if(chs[index]=='0')return 0;
else if(chs[index]=='1'){
int res=process(chs,index+1);
if(index+1<chs.length){
res+=process(chs,index+2);
}
return res;
}
else if(chs[index]=='2'){
int res=process(chs,index+1);
if(index+1<chs.length&&chs[index+1]<='6'){
res+=process(chs,index+2);
}
return res;
}
return process(chs,index+1);
}
public int translateNumber (String number) {
// write code here
if(number==null)return 0;
int ans=process(number.toCharArray(),0);
return ans;
}
}
背包问题
import java.util.Scanner;
//背包问题
public class Main {
public static int getMaxValue(int N,int V,int[] C,int[] W) {
return process(V,0,C,W,0);
}
public static int process(int V,int alreadyW,int[] C,int[] W,int index) {
if(alreadyW>V)return -1;
if(index==C.length)return 0;
int v1=process(V,alreadyW,C,W,index+1);//返回index+1之后的货物总价值
int v2=process(V,alreadyW+W[index],C,W,index+1);//可能是无效解,不能直接加C[index],需要判断一下
if(v2!=-1) {
v2+=C[index];
}
return Math.max(v1, v2);
}
public static void main(String[] args) {
Scanner scan=new Scanner(System.in);
int N=scan.nextInt();
int V=scan.nextInt();
int[] C=new int[N];
int[] W=new int[N];
for(int i=0;i<N;++i) {
C[i]=scan.nextInt();
W[i]=scan.nextInt();
}
int ans=getMaxValue(N,V,C,W);
System.out.println(ans);
}
}
排成一条线的纸牌博弈问题
import java.util.Scanner;
//排成一条线的纸牌博弈问题
public class Main {
public static int getWinnerGrade(int[] cards) {
return First(cards,0,cards.length-1);
}
public static int First(int[] cards,int L,int R) {
if(L==R)return cards[L];
return Math.max(cards[L]+Second(cards,L+1,R), cards[R]+Second(cards,L,R-1));
}
public static int Second(int[] cards,int L,int R) {
if(L==R)return 0;
return Math.min(First(cards,L+1,R), First(cards,L,R-1));
}
public static void main(String[] args) {
Scanner scan=new Scanner(System.in);
int N=scan.nextInt();
int[] cards=new int[N];
for(int i=0;i<N;++i) {
cards[i]=scan.nextInt();
}
int ans=getWinnerGrade(cards);
System.out.println(ans);
}
}
暴力递归解法显然运行超时,最优解是用 动态规划 。
N皇后问题
N皇后问题只能用暴力递归来解,改不成动态规划 。
暴力递归解一
import java.util.Scanner;
//n皇后问题
public class Main {
public static int getNQueen(int N) {
if(N<1)return 0;
int[] col=new int[N];
return process(N,0,col);
}
public static int process(int N,int index,int[] col) {
if(index==N)return 1;
int res=0;
for(int i=0;i<N;++i) {
if(isValid(N,index,i,col)) {
col[index]=i;
res+=process(N,index+1,col);
}
}
return res;
}
public static boolean isValid(int N,int index,int i,int[] col) {
for(int j=0;j<index;++j) {
if(col[j]==i||Math.abs(j-index)==Math.abs(col[j]-i))return false;
}
return true;
}
public static void main(String[] args) {
Scanner scan=new Scanner(System.in);
int N=scan.nextInt();
int ans=getNQueen(N);
System.out.println(ans);
}
}
暴力递归解二
在解一的基础上进行了常数时间的优化,但是两种暴力递归时间复杂度是一样的。
import java.util.Scanner;
//n皇后问题
public class Main {
public static int getAns(int N) {
if(N<1||N>32)return 0;
int limit=N==32?-1:((1<<N)-1);
return process1(limit,0,0,0);
}
public static int process1(int limit,int colLimit,int leftLimit,int rightLimit) {
if(limit==colLimit)return 1;
int pos=limit&(~(colLimit|leftLimit|rightLimit));
int res=0;
while(pos!=0) {
int mostRightOne=pos&(~pos+1);
res+=process1(limit,colLimit|mostRightOne,(leftLimit|mostRightOne)<<1,(rightLimit|mostRightOne)>>>1);
pos-=mostRightOne;
}
return res;
}
public static void main(String[] args) {
Scanner scan=new Scanner(System.in);
int N=scan.nextInt();
int ans=getAns(N);
System.out.println(ans);
}
}
数的划分
数论知识:
import java.util.Scanner;
//数的划分
public class Main {
public static int Partition(int n,int k) {
return process(n,k);
}
public static int process(int n,int k) {
if(n<k)return 0;
if(n==k||k==1)return 1;
return process(n-1,k-1)+process(n-k,k);
}
public static void main(String[] args) {
Scanner scan=new Scanner(System.in);
int n=scan.nextInt();
int k=scan.nextInt();
int ans=Partition(n,k);
System.out.println(ans);
}
}
选数
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Scanner;
import java.util.Set;
//选数
public class Main {
public static boolean isPrime(int x) {
if(x<2)return false;
if(x==2)return true;
for(int i=2;i<x;++i) {
if(x%i==0)return false;
}
return true;
}
public static int getAns(int n,int k,List<Integer>number) {
Set<Integer>hashSet=new HashSet<>();
return process(n,k,0,number,0);
}
public static int process(int n,int k,int index,List<Integer>number,int alreadyNum) {
if(k==0) {
if(isPrime(alreadyNum)) {
return 1;
}
return 0;
}
if(index==n)return 0;
int res=0;
res+=process(n,k,index+1,number,alreadyNum);
res+=process(n,k-1,index+1,number,alreadyNum+number.get(index));
return res;
}
public static void main(String[] args) {
Scanner scan=new Scanner(System.in);
int n=scan.nextInt();
int k=scan.nextInt();
List<Integer>number=new ArrayList<>(n);
for(int i=0;i<n;++i) {
number.add(scan.nextInt());
}
int ans=getAns(n,k,number);
System.out.println(ans);
}
}
爬楼梯
import java.util.Scanner;
//爬楼梯
public class Main {
public static int getAns(int n) {
return process(n,0);
}
public static int process(int n,int index) {
if(index==n)return 1;
if(index>n)return 0;
return process(n,index+1)+process(n,index+2);
}
public static void main(String[] args) {
Scanner scan=new Scanner(System.in);
int n=scan.nextInt();
int ans=getAns(n);
System.out.println(ans);
}
}
我写的暴力递归的代码是正确的,但是最优的算法是用 动态规划 。
翻转数位
class Solution {
//暴力递归
public int process(int num,int index,int sum,boolean isOk){
//[index-1...0]目前最长的一串1,而且index-1位置必是1
if(index==32){
return sum;
}
if(((num>>index)&1)==1){
return process(num,index+1,sum+1,isOk);
}
if(isOk){
return Math.max(process(num,index+1,0,true),process(num,index+1,sum+1,false));
}
return Math.max(sum,process(num,index+1,0,false));
}
public int reverseBits(int num) {
return process(num,0,0,true);
}
}
传递信息
class Solution {
//暴力递归
//index:当前的玩家号 rest:当前还剩下rest轮
public int process(int[][] relation,Map<Integer,List<Integer>>map,int n,int index,int rest){
if(rest==0){
return index==n-1?1:0;
}
//当前传递尚未结束
if(!map.containsKey(index)){//index无法再往下传
return 0;
}
int ans=0;
List<Integer>list=new ArrayList<>(map.get(index));
for(int i=0;i<list.size();++i){
ans+=process(relation,map,n,list.get(i),rest-1);
}
return ans;
}
public int numWays(int n, int[][] relation, int k) {
Map<Integer,List<Integer>>map=new HashMap<>();
for(int i=0;i<relation.length;++i){
if(!map.containsKey(relation[i][0])){
List<Integer>arr=new ArrayList<>();
arr.add(relation[i][1]);
map.put(relation[i][0],arr);
}else{
map.get(relation[i][0]).add(relation[i][1]);
}
}
return process(relation,map,n,0,k);
}
}