面试算法总结----回溯(subset i, subset ii , permutation i , permutation ii, )

subset
class Solution {
    List> result=new ArrayList<>();
    public List> subsets(int[] nums) {
        List tmp=new ArrayList<>();
        dfs(nums,0,tmp);
        result.add(new ArrayList<>());
        return result;
        
        
    }
    public void dfs(int[] nums,int begin, List tmp){
        for(int i=begin;i(tmp));
            dfs(nums,i+1,tmp);
            tmp.remove(tmp.size()-1);
        }
    }
    
}

对于找子集 有一个特定是不论当前tmp的长度 只要是子集就都要加入到最后的结果中,直到到达数组的末尾 第二个特点是 起始点随着深度优先遍历的推进而推进 不像找全排列一样每次都完成完整的一次搜索 ,所以对于找子集 需要记录一个index 表示开始的位置
因此只需要在dfs函数中定义一个循环 循环的开始位置为index记录位置 结束位置为数组长度 每一次都将数组index位置的元素放入到tmp中 并将当前tmp放入到result中 进行下一次dfs搜索 搜索一次完成后将当前tmp中最后一个元素删除

subset 2
class Solution {
    List> result=new ArrayList<>();
    public List> subsetsWithDup(int[] nums) {
        List tmp=new ArrayList<>();
        Arrays.sort(nums);
        dfs(nums,0,tmp);
        result.add(new ArrayList<>());
        return result;
    }
    public void dfs(int[] nums,int start,List tmp){
        for(int i=start;istart&& nums[i]==nums[i-1]){
                continue;
            }
            tmp.add(nums[i]);
            result.add(new ArrayList<>(tmp));
            dfs(nums,i+1,tmp);
            tmp.remove(tmp.size()-1);
        }
    }
    
}

对于有重复数据时 首先将原始的数组排序 然后和没重复数据唯一的区别就是 在一个tmp状态已经访问过某元素 将该元素remove之后又访问相同的元素 会造成重复 因此只需要设定 当i>start 同时 nums[i] == nums[i-1] 时 略过

全排列
class Solution {
    List> result=new ArrayList<>();
    public List> permute(int[] nums) {
        List tmp=new ArrayList<>();
        boolean[] isVisit=new boolean[nums.length];
        dfs(nums,tmp,isVisit);
        return result;
    }
    public void dfs(int[] nums,List tmp,boolean[]isVisit){
        if(tmp.size()==nums.length){
            result.add(new ArrayList<>(tmp));
            return;
        }
        for(int i=0;i

对于全排列 每一次都是从0开始遍历 需要额外记录当前位置是否已经访问过

全排列 2
class Solution {
    List> result=new ArrayList<>();
    public List> permuteUnique(int[] nums) {
        Arrays.sort(nums);
        List tmp=new ArrayList<>();
        boolean[] visit=new boolean[nums.length];
        dfs(nums,tmp,visit);
        return result;
    }
    public void dfs(int[]nums,Listtmp,boolean[]visit){
        if(nums.length==tmp.size()){
            result.add(new ArrayList<>(tmp));
            return;
        }
        for(int i=0;i0&&nums[i]==nums[i-1]&&visit[i-1]==false)){
                continue;
            }
            tmp.add(nums[i]);
            visit[i]=true;
            dfs(nums,tmp,visit);
            tmp.remove(tmp.size()-1);
            visit[i]=false;
        }
    }
}
Permutation Sequence leetcode 60

这道题重点在于推导每一位取值
而每一位的取值和当前位之前的连乘积有关 例如 n=4 k=9
当n=1 时 全排列只有一种可能
当 n= 2 时 全排列有1 * 2 种可能
当n=3时 全排列有3 * 1 * 2 种可能
当n等于4 时 全排列有 4 * 1 * 2 * 3种可能 所以第一位取几取决于 (k-1)/ (n-1)! (因为存储索引从0开始 第九个的index为9-1 )同时n=4的全排列总共包含第一位为1的6种 和第一位为2的6种 … 第一位为4的6种

class Solution {
    
    public String getPermutation(int n, int k) {
        int[] factorial=new int[n];
        factorial[0]=1;
        for(int i=1;i totalNum=new ArrayList<>();
        for(int i=1;i<=n;i++){
            totalNum.add(i);
        }
        StringBuffer buffer=new StringBuffer();
        int alis=k-1;
        while(totalNum.size()!=0){
            int tmpIndex=alis/factorial[n-1];
            alis=alis%factorial[n-1];
            buffer.append(totalNum.get(tmpIndex));
            totalNum.remove(tmpIndex);
            n=n-1;
            
        }
        return buffer.toString();
        
    }
    
}

每一次循环的k相当于上一次与连乘积取余

下一个全排列
class Solution {
    public void nextPermutation(int[] nums) {
        int index=nums.length-2;
        while(index>=0 && nums[index]>=nums[index+1]){
            index--;
        }
        if(index>=0){
            int j= nums.length-1;
            while(j>=0&& nums[j]<=nums[index]){
                j--;
            }
            swap(nums,index,j);
        }
        reverse(nums,index+1);
    }
    public void swap(int[]nums,int index,int j){
        int tmp=nums[index];
        nums[index]=nums[j];
        nums[j]=tmp;
    }
    public void reverse(int[] nums,int index){
       int i=index;
       int j=nums.length-1;
        while(i

下一个 全排列主要的思想就是从后往前寻找第一个非递增的位置 这就说明某一个位置可以通过和之后的位置交换得到更大的排列 再一次从后往前找找到第一个大于非递增位置数据的位置 二者交换 交换完之后把递增序列翻转变成递减序列 得到下一个全排列

给定一个字符串 找到其中所有符合IP规则的排列 (leetcode Restore IP Address)
class Solution {
    
    public List restoreIpAddresses(String s) {
        List result=new ArrayList<>();
        if(s.length()<4||s.length()>12){
            return result;
        }
        Listip=new ArrayList<>();
        backTrack(s,ip,result,0);
        return result;
        
        
    }
    public void backTrack(String s, Listip, List result, int begin){
        if(ip.size()==4){
            if(begin==s.length()){
            result.add(ip.get(0)+'.'+ip.get(1)+'.'+ip.get(2)+'.'+ip.get(3));
            return;
        }else{
                return;
            }
        }
        
        for(int i=begin;i

这个题关键点在于循环的时候 给定初始begin 每次结束的时候只需要i 同时ip地址如果首位出现0那么长度只能是1
以及在放入到最后result的时候 只需要判断当前的begin是不是等于s的长度就能确定tmp里面的四个部分是否把全部数字覆盖全

矩阵中的路线 判断一个矩阵中是否包含某个字符串的全部字符,在矩阵中只能上下左右运动 且运动过的位置不能重复使用

public class Solution {
    public boolean hasPath(char[] matrix, int rows, int cols, char[] str)
    {   
        boolean[] tag=new boolean[matrix.length];
        for(int i=0;i=rows||ncols<0||ncols>=cols||tag[nrows*cols+ncols]==true){
            return false;
        }
        if(matrix[nrows*cols+ncols]==str[nums]){
            tag[nrows*cols+ncols]=true;
            boolean result= dfs(matrix,rows,cols,nrows+1,ncols,nums+1,str,tag)||dfs(matrix,rows,cols,nrows,ncols+1,nums+1,str,tag)||dfs(matrix,rows,cols,nrows-1,ncols,nums+1,str,tag)
                            ||dfs(matrix,rows,cols,nrows,ncols-1,nums+1,str,tag);
            tag[nrows*cols+ncols]=false;
            return result;
                
        }
        return false;
    }


}

0,1 矩阵相关问题

1.在0,1矩阵中联通的1区域个数
public int numIslands(char[][] grid) {
        int totalNum=0;
        for(int i=0;i=grid.length||n<0||n>=grid[0].length||grid[m][n]=='0'){
            return;
        }
        grid[m][n]='0';
        dfs(grid,m+1,n);
        dfs(grid,m-1,n);
        dfs(grid,m,n-1);
        dfs(grid,m,n+1);
    }

因为要找联通区域个数 首先当出现1的时候计数在原始基础上加1 ,这个加1带来的效果就是穷尽该联通区域所有的1,为了标记已经访问过的区域,可以使用boolean[][] isvisited 也可以直接将访问过的位置标 ‘0’ ,当坐标范围超出或者遇到‘0’则返回,这样可以将完整的联通区域都标 0 同时在基础totalNum上加1.

2. 0,1矩阵的联通区域中寻找最大联通区域的大小
class Solution {
    public int maxAreaOfIsland(int[][] grid) {
        int max=0;
        for(int i=0;i=grid.length||n<0||n>=grid[0].length||grid[m][n]==0){
            return 0;
        }
        grid[m][n]=0;
        int left=dfs(grid,m,n-1);
        int right=dfs(grid,m,n+1);
        int up=dfs(grid,m-1,n);
        int down=dfs(grid,m+1,n);
        return 1+left+right+up+down;
    }
}

寻找最大连接面积 也就是在遍历的过程中遇到1,则将这个1所有连接的区域面积都加起来,而所有连接区域面积和等于 当前位置1+left+right+up+down,作为返回值即可

3. 在0,1矩阵的联通区域中寻找最大正方形的面积
 public int maximalSquare(char[][] matrix) {
        int row=matrix.length;
        int col=row>0?matrix[0].length:0;
        int[][] dp=new int[row+1][col+1];
        int max=0;
        for(int i=1;i<=row;i++){
            for(int j=1;j<=col;j++){
                if(matrix[i-1][j-1]=='1'){
                     dp[i][j]=Math.min(Math.min(dp[i-1][j],dp[i][j-1]),dp[i-1][j-1])+1;
                     max=Math.max(max,dp[i][j]);
            
                }
               
        }
    }
        return max*max;
}

最大正方形面积 需要找到最大正方形的边长 ,找到最大正方形边长 需要判断当前i,j 是否为1 同时 找到 dp[i-1][j] ,dp[i][j-1],以及dp[i-1][j-1] 的最小值

4. 在 0,1 矩阵中联通区域的最大矩形面积

在一个0,1联通区域中求最大的矩形面积 可以转化为在给定的矩形每一行都求一次在该行以上的柱状图中的最大的矩形面积,因此整个问题分解为求柱状图的最大矩形面积

public int largestRectangleArea(int[] heights) {
        Stack stack=new Stack<>();
        int begin=0;
        int end=heights.length-1;
        int max=0;
        while(begin<=end){
            if(stack.isEmpty()||heights[begin]>heights[stack.peek()]){
                stack.push(begin);
                begin++;
            }else{
                int tmp=stack.pop();
                int height=heights[tmp];
                int left=stack.isEmpty()?-1:stack.peek();
                max=Math.max(max,height*(begin-left-1));
            }
        }
        while(!stack.isEmpty()){
            int tmp=stack.pop();
            int height=heights[tmp];
            int left=stack.isEmpty()?-1:stack.peek();
            max=Math.max(max,height*(begin-left-1));
        }
        return max;
    }

使用一个stack来保存index,因为要计算矩形面积 最重要的是确定height和宽,stack中存储递增的元素,当当前元素小于栈顶元素时,就进行面积计算,因为栈顶是最大的所以每次都是用pop出来的index对应的元素值作为高,从pop出的index作为left边界 当前遍历位置的index作为right边界 就能够很方便的求得面积

对于0,1矩阵只需要将每一层求一次最大直方图面积即可

public int maximalRectangle(char[][] matrix) {
        int max=0;
        if(matrix.length==0){
            return 0;
        }
        int[]nums=new int[matrix[0].length];
        for(int i=0;i stack=new Stack<>();
        int begin=0;
        int end=heights.length-1;
        int max=0;
        while(begin<=end){
            if(stack.isEmpty()||heights[begin]>heights[stack.peek()]){
                stack.push(begin);
                begin++;
            }else{
                int tmp=stack.pop();
                int height=heights[tmp];
                int left=stack.isEmpty()?-1:stack.peek();
                max=Math.max(max,height*(begin-left-1));
            }
        }
        while(!stack.isEmpty()){
            int tmp=stack.pop();
            int height=heights[tmp];
            int left=stack.isEmpty()?-1:stack.peek();
            max=Math.max(max,height*(begin-left-1));
        }
        return max;
    }
5. 在一个二维矩阵中 使用一个长为m,宽为n的矩形在矩阵上进行滑动,求滑动过程中窗口中元素的和的最大值

对于该题目而言 求长为m,宽为n的滑窗在二维矩阵上滑动 如果有点复杂的话,那么可以将该题目化简为一维的滑窗在一维数组上滑动 求该滑窗窗口内的和的最大值,那么如何使用一维的滑窗来实现二维滑窗的效果呢 ,最直接的想法就是将数组在列维度上进行压缩,这样就可以将问题转化为一维的问题 进而使用滑动窗口求解

public int getResult(int[][]nums,int h,int k){
       int w=nums[0].length;
       for(int i=0;i
6. 在一维数组中求连续最大乘积

在一维数组中有正有负,当前连乘的最大值只有可能是当前值本身,当前值和之前的最大值的乘积,当前值和之前最小值的乘积

  int maxProduct(int[]nums) {
        int n=nums.size();
        if(n==1) return nums[0];
        int maxpre=nums[0];//连乘到上个元素的最大乘积
        int minpre=nums[0];//连乘到上个元素的最小乘积
        int maxcur;//连乘到当前元素的最大乘积
        int mincur;//连乘到当前元素的最小乘积
        int maxres=nums[0];//全局最大乘积
        for(int i=1;i

你可能感兴趣的:(面试问题总结)