2021-2回溯,滑动窗口,并查集

回溯,滑动窗口,并查集

递归

树的前序遍历,中序遍历,后序遍历:

//前序
void preOrder(root){
sys...
preOrder(root.left);
preOrder(root.right);


}
//中序
void inOrder(root){
inOrder(root.left);
sys...
inOrder(root.right);
}
//后序
void postOrder(root){
postOrder(root.left);
postOrder(root.right);
sys...
}
回溯

回想dfs:

public void dfs(root){
for(child:root){
...
dfs(child);
...
}
}

可以知道dfs与回溯是类似的:

  • 回溯的重点在于要做出选择和撤销选择

leedcode 46 全排列

class Solution {
    List<List<Integer>> res =new LinkedList<>();
    public List<List<Integer>> permute(int[] nums) {
         //路径,(已经搜索过)
         LinkedList <Integer> track=new LinkedList<>();
         backtrack(nums,track);
         return res;
    }
    void backtrack(int [] nums ,LinkedList <Integer> track){
      if(track.size()==nums.length){
          res.add(new LinkedList(track));
          return;
      }
      for(int i=0;i<nums.length;i++){
      //或者用一个vis数组记录
          if(track.contains(nums[i]))
          continue;
          track.add(nums[i]);
          backtrack(nums,track);
          track.removeLast();
      }
    }
}
//改用vis比if(track.contains(nums[i]))更快
class Solution {
    List<List<Integer>> res =new LinkedList<>();
     int []vis;
    public List<List<Integer>> permute(int[] nums) {
        if (nums == null || nums.length == 0) {
            res.add(new ArrayList<Integer>());
            return res;
        }
        int n=nums.length;
        vis=new int[n];
        for(int i=0;i<n;i++)vis[i]=0;
         //路径,(已经搜索过)
         LinkedList <Integer> track=new LinkedList<>();
         backtrack(nums,track);
         return res;
    }
    void backtrack(int [] nums ,LinkedList <Integer> track){
      if(track.size()==nums.length){
          res.add(new LinkedList(track));
          return;
      }
      for(int i=0;i<nums.length;i++){
          if( vis[i]==1) continue;
          vis[i]=1;
          track.add(nums[i]);
          backtrack(nums,track);
          vis[i]=0;
          track.removeLast();
      }
    }
}

leetcode全排列2 (有重复数子)

class Solution {
    List<List<Integer>> res =new LinkedList<>();
     int []vis;
    public List<List<Integer>> permuteUnique(int[] nums) {
        
        if (nums == null || nums.length == 0) {
            res.add(new ArrayList<Integer>());
            return res;
        }
        Arrays.sort(nums);
        int n=nums.length;
        vis=new int[n];
        for(int i=0;i<n;i++)vis[i]=0;
         //路径,(已经搜索过)
         LinkedList <Integer> track=new LinkedList<>();
         backtrack(nums,track);
         return res;
    }
    void backtrack(int [] nums ,LinkedList <Integer> track){
      if(track.size()==nums.length){
          res.add(new LinkedList(track));
          return;
      }
      for(int i=0;i<nums.length;i++){
         
         //同一层,而且之前那个没有用过
          if( vis[i]==1||(i>0&&nums[i]==nums[i-1]&&(vis[i-1]==0))) continue;
          vis[i]=1;
          track.add(nums[i]);
          backtrack(nums,track);
          vis[i]=0;
          track.removeLast();
      }
    }
}
//先要排序

leetcode 组合总和39:

class Solution {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<List<Integer>> ans = new ArrayList<List<Integer>>();
        List<Integer> combine = new ArrayList<Integer>();
        dfs(candidates, target, ans, combine, 0);
        return ans;
    }

    public void dfs(int[] candidates, int target, List<List<Integer>> ans, List<Integer> combine, int idx) {
        if (idx == candidates.length) {
            return;
        }
        if (target == 0) {
            ans.add(new ArrayList<Integer>(combine));
            return;
        }
        // 直接跳过
        dfs(candidates, target, ans, combine, idx + 1);
        // 选择当前数
        if (target - candidates[idx] >= 0) {
            combine.add(candidates[idx]);
            dfs(candidates, target - candidates[idx], ans, combine, idx);
            combine.remove(combine.size() - 1);
        }
    }
}

leetcode组合总和2,有重复数字:

class Solution {
    List<List<Integer>> list=new ArrayList<>();
    List<Integer> path=new ArrayList<>();
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        Arrays.sort(candidates);
        dfs(candidates,target,0);
        return list;
    }
    private void dfs(int[] candidates, int target,int index){
        if(target==0){
            list.add(new ArrayList<>(path));
            return;
        }
        
        for(int i=index;i<candidates.length;i++){
            if(candidates[i]<=target){
//同一层的话
                if(i>index&&candidates[i]==candidates[i-1]){
                    continue;
                }
                path.add(candidates[i]);
                dfs(candidates,target-candidates[i],i+1);
                path.remove(path.size()-1);
            }
        }
    }
}

leetcode 78子集问题(最入门的一道)

class Solution {
    List<List<Integer>> ans =new ArrayList<List<Integer>>();
    public List<List<Integer>> subsets(int[] nums) {
  dfs(0,nums);
  return ans;
    }
  List<Integer> res=new ArrayList<Integer>();
 void dfs(int i,int []nums){
    if(i==nums.length){
        ans.add(new ArrayList(res));
        return ;
    }
     //子集都有两种选择,要么在要么不在结果中
     res.add(nums[i]);
    dfs(i+1,nums);
    res.remove(res.size()-1);
    dfs(i+1,nums);
 }
}

leetcode90子集2

class Solution {

    List<List<Integer>> res =new LinkedList<>();
    LinkedList<Integer>  ans=new LinkedList<>();
    int n;
    public List<List<Integer>> subsetsWithDup(int[] nums) {
        //排序
        Arrays.sort(nums);
    n=nums.length;
  traverse(nums,ans,0);
  return res;
    }
   void traverse(int []nums ,LinkedList<Integer> track,int p){
        if(p==n+1){
            return ;
        }
        res.add(new LinkedList(track));
        for(int i=p;i<n;i++){
        //并不用像排列一样,同一层剪枝需要。。。
            if(i>p&&nums[i]==nums[i-1]){
                continue;
            }
           track.add(nums[i]);
           traverse(nums,track,i+1);
           track.removeLast();
            
        }
    }
}

在做有重复数字的题时,把握不好return,还是for+continue比较保险

leetcode 93复原ip地址

class Solution {

    //四段:
    static final int seg_count=4;
    int []segments=new int[seg_count];
    List<String> ans=new ArrayList<>();
    public List<String> restoreIpAddresses(String s) {
        //string segID segSTART
         
         dfs(s,0,0);
         return ans;
    }
    void dfs(String s,int segID,int segSTART){
       if(segID==seg_count){
           if(segSTART==s.length()){
           StringBuffer ip=new StringBuffer();
           for(int i=0;i<seg_count;i++){
             ip.append(segments[i]);
             if(i!=seg_count-1){
                 ip.append('.');
             }
           }
           ans.add(ip.toString());
          
       } return;
       }
       

       if(segSTART==s.length()) return ;
       //先判断是否有零
       if(s.charAt(segSTART)=='0'){
           segments[segID]=0;
      dfs(s,segID+1,segSTART+1);
       }

//因为要比较大小,所以要转成int类型
//一定要枚举每一种可能性
//addr放在外面
  int addr =0;
       for(int i=segSTART;i<s.length();i++){
     addr=addr*10+(s.charAt(i)-'0');
     if(addr>0&&addr<=0xFF){
         segments[segID]=addr;
         dfs(s,segID+1,i+1);
     }else {
      break;
     }
       }
      
    }
   
    
}

一道dfs的题目(记得new一个vis数组)

leetcode529扫雷

class Solution {
    public char[][] updateBoard(char[][] board, int[] click) {
      if(board[click[0]][click[1]] == 'M'){
            board[click[0]][click[1]] = 'X';
            return board;
        } 
        //不必要重复判断
         int[][] visited = new int[board.length][board[0].length];   
         search(board, click[0], click[1], visited);
        return board;
    }
    public void search(char[][] board,int x,int y,int [][]visited){
        if(x >= 0 && y >= 0 && x < board.length && y < board[0].length && visited[x][y] == 0){
            visited[x][y] = 1;
             if(board[x][y] == 'E'){
                 //递归的终点
                if(mine(board, x, y) > 0){
                    board[x][y] = (char)('0' + mine(board, x, y));
                    return;
                } 
                else{
                    board[x][y] = 'B';
                    search(board, x - 1, y, visited);
                    search(board, x - 1, y - 1, visited);
                    search(board, x - 1, y + 1, visited);
                    search(board, x + 1, y, visited);
                    search(board, x + 1, y - 1, visited);
                    search(board, x + 1, y + 1, visited);
                    search(board, x, y + 1, visited);
                    search(board, x, y - 1, visited);
                } 
            }  
        

        }
    }
    int mine(char[][] board,int x,int y){
 return boom(board, x - 1, y) + boom(board, x - 1, y - 1) + boom(board, x - 1, y + 1) + boom(board, x + 1, y) + boom(board, x + 1, y - 1) + boom(board, x + 1, y + 1) + boom(board, x, y + 1) + boom(board, x, y - 1);
    }
     public int boom(char[][] board, int x, int y){
        return (x >= 0 && y >= 0 && x < board.length && y < board[0].length && board[x][y] == 'M')?1:0;
    }
}

递归的总结:

1、递归的终点!!!

2、return 容易出错

leetcode473火柴

class Solution {
    int [] cnt;
    public boolean makesquare(int[] nums) {
        int  total=0;
        for(int num:nums){
            total+=num;
        }
        if(total==0||total%4!=0) return false;
        Arrays.sort(nums);
        cnt=new int[4];
        return traver(nums.length-1,nums,total/4);
        
    }
    boolean traver(int index ,int []nums, int target){
  if(index==-1){
      if(cnt[0]==cnt[1]&&cnt[1]==cnt[2]&&cnt[2]==cnt[3]) return true;
return false;
  }

  for(int i=0;i<4;i++){
      //加上(i>0&&cnt[i]==cnt[i-1])剪枝,,,
    if(cnt[i]+nums[index]>target||(i>0&&cnt[i]==cnt[i-1]))continue;
    cnt[i]+=nums[index];
    if(traver(index-1,nums,target)) return true;
    cnt[i]-=nums[index];
  }
    return false;
    }

总结之前遇到需要遍历的题目,不管是排列还是火柴棒,如果都是要做出选择,则首先应当考虑怎样做出选择

  • 从终止条件思考:边要有四个,火柴也要全部用完,都有终止条件!!!也可以想到要递归

  • 回溯的思想:从要做选择,到底是边选择火柴,还是火柴选择边。—>也就是递归的终止条件是遍历完了所有的边,还是遍历完了所有的火柴。
    两种方法:

  • 如果是dfs(4条边),先凑一条边(for所有的火柴),也就是边选择火柴,可以用vis[i]来记录每条边是否被用到,但最后要判断是不是所有的边都被用到(用target/4)。

  • 如果是travel(所有的火柴),火柴选择边,则对于每个火柴,我们都可以选择四条边(for四条边),并且此时应该想办法剪枝。

滑动窗口

leetcode3 [无重复字符的最长子串]

class Solution {
    public int lengthOfLongestSubstring(String s) {
      if(s.length()==1||s.length()==0) return s.length();
      int right=0,left=0;
    Set<Character> window = new HashSet<Character>();
   int res=0;
        //用集合的话,可以先添加第一个
   window.add(s.charAt(right));
   right++;
       while(right<s.length())
       { 
           char c=s.charAt(right);
           while(left<s.length()&&window.contains(c)){
               char d=s.charAt(left);
               left++;
               window.remove(d);
           }
           window.add(c);
            res=Math.max(res,right-left+1);
            right++;
       }
  return res;
    }
}
//题目求最长无重复,故可以算出所有无重复,求最大值

leetcode424[替换后的最长重复字符]

class Solution {
    public int characterReplacement(String s, int k) {
   int [] num =new int[26];
   int right=0,left=0;
   int res=0;
   while(right<s.length()){
    int c=s.charAt(right)-'A';
    num[c]++;
    res=Math.max(res,num[c]);
       //如果大于则
    if(right-left+1>k+res){
        int d=s.charAt(left)-'A';
        num[d]--;
        left++;
    }
    right++;
   }
        //res一直都是最大的
   return right-left;
        //return res+k;
    }
}
//题目最长重复字符,故可以先算出最长的重复,如果此后的有比它大的,则再进行比较

两种思路,是求出所有的无重复,或者所有的重复,还是先求出最大的无重复,或者最大的重复。

leetcode1004 最长连续1的个数

class Solution {
    public int longestOnes(int[] A, int K) {
     int right=0,left=0;
     int count=0;
     int res=0;
     while(right<A.length){
  if(A[right]==0)  count++;
  if(count>K){
      if(A[left++]==0) --count;
  }
  res=Math.max(res,right-left+1);
  right++;
     }
     return res;
    }
}
//这就是求出了所有的重复

while(right<…){

.//…对新添加的right进行处理

while(…某些条件.){

left++;

}

res =max(res ,left-right+1);

right++;

}

leetcode1208尽可能让所有字符相等

class Solution {
    public int equalSubstring(String s, String t, int maxCost) {
    int n=s.length();
    int [] newarray=new  int[n];
    for(int i=0;i<n;i++){
        int a=t.charAt(i)-'A';
        int b=s.charAt(i)-'A';
      newarray[i]=Math.abs(a-b);
    }
    int left =0,right =0;
    int windowcost=0;
    int res=0;
    while(right<n){
        windowcost+=newarray[right];
        while(windowcost>maxCost){
          //res =Math.max(right-left,res);
         
          windowcost-=newarray[left]; left++;
        }
        res=Math.max(res,right-left+1);
        right++;
    }
    return res;
    }
}

leetcode1493删掉一个数后

class Solution {
    public int longestSubarray(int[] nums) {
  int left=0,right=0;
  int n=nums.length;
  int count=0;
  int res=0;
  while(right<n){
      if(nums[right]==0) count++;
      while(count>1){
         if(nums[left]==0)count--; 
         left++;
      }
      res=Math.max(res,right-left);
      right++;
  }
  return res;
    }
}

leetcode295


leetcode209

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
    int left=0,right=0;
    int n=nums.length;
    int res=10001;
    int rest=target;
    while(right<n){
        rest-=nums[right];
        while(rest<=0){ 
             res=Math.min(res,right-left+1);
            rest+=nums[left];
            left++;
        }
        //res=Math.min(res,right-left+1);
         right++;
    }
    return res==10001?0:res;
    }
}

leetcode76最小覆盖子串

class Solution {
    public String minWindow(String s, String t) {
  int right=0,left=0;
  int n1=s.length();
  int n2 =t.length();
  int []need =new int[128];
  int []window =new int[128];
  for(int i=0;i<n2;i++){
      need[t.charAt(i)-'A']++;
  }
  int vaild=0;
  int ans=100001;
  String res="";
  while(right<n1){
      int c=s.charAt(right)-'A';
      if(need[c]>0&&window[c]<need[c]){
            vaild++;
      }
      window[c]++;
    while(vaild>=n2){
        int d=s.charAt(left)-'A';
        if(right-left+1<ans){
            ans=right-left+1;
            res=s.substring(left,right+1);
        }
        if(need[d]>0&&window[d]<=need[d]){
            vaild--;
        }
        window[d]--;
        left++;
    }
    right++;
  }
  return res;
  }
}

leetcode438找到所有字母的异位词

class Solution {
    public List<Integer> findAnagrams(String s, String p) {
     int n1=s.length();
     int n2=p.length();
     int left=0,right=0;
     int[] need=new int[27];
     int[] window=new int[27];
     for(int i=0;i<n2;i++){
    need[p.charAt(i)-'a']++;
     }
     List <Integer> res=new ArrayList<>();
     int vaild=0;
     while(right<n1){
         int c=s.charAt(right)-'a';
          if(need[c]>0&&window[c]<need[c])vaild++;
         window[c]++;
      while(right-left+1>=n2){
      if(vaild==n2) res.add(left);

      int d=s.charAt(left)-'a';
      if(need[d]>0){
               if(window[d]>0&&window[d]<=need[d]){
          vaild--;
      }  
      }
      window[d]--;
      left++;
       }
       right++;
     }
     return res;
    }
}
并查集
  • 想像一个问题:若某个家族人员过于庞大,要判断两个是否是亲戚,确实不容易,给出某个亲戚关系图,求任意给出的两个人是否具有亲戚关系。

  • 出现并集、寻找根

    1、初始化
    int fa[MAXN];
    //设置n个子集
    //一开始,将每个元素得父节点设置为自己
     void init(int n)
    {
        for (int i = 1; i <= n; ++i)
            fa[i] = i;
    }
    2、查找
    //递归,一层一层的访问父节点,直到访问到根节点(根节点的标志就是父节点是本身)
    int find(int x)
    {
        if(fa[x] == x)
            return x;
        else
            return find(fa[x]);
    }
    
    3、合并
    //将前一个元素的父节点设置为后一个元素的父节点
     void merge(int i, int j)
    {
        fa[find(i)] = find(j);
    }
    
    4、路径压缩
    int find(int x)
    {
        if(x == fa[x])
            return x;
        else{
            fa[x] = find(fa[x]);  //父节点设为根节点
            return fa[x];         //返回父节点
        }
    }
    
    通常可以简写成:
    int find(int x)
    {
        return x == fa[x] ? x : (fa[x] = find(fa[x]));
    }
    5、按秩合并
    (1)初始化(秩)
     void init(int n)
    {
        for (int i = 1; i <= n; ++i)
        {
            fa[i] = i;
            rank[i] = 1;
        }
    }2)合并
    void merge(int i, int j)
    {
        int x = find(i), y = find(j);    //先找到两个根节点
        if (rank[x] <= rank[y])
            fa[x] = y;
        else
            fa[y] = x;
        if (rank[x] == rank[y] && x != y)
            rank[y]++;                   //如果深度相同且根节点不同,则新的根节点的深度+1
    }
    
    
    
    
    
    
    

  - 例题:c++编写

    ```c++
    1、程序自动分析
    //离散化
    #include <iostream>
    #include <map>
    #include <cstdio>
    using namespace std;
    //fa数组
    int pre[110000<<4];
    struct ask{
    int p1;int p2;int e;
    }a[1000];
    //对应得离散化
    map<int,int> Map;
    int tot=0;
    //离散化
    int reset(int x){
        //Map.find(x)!=Map.end()判断是否已经初始化
    if(Map.find(x)!=Map.end())return Map[x];
    Map[x]=++tot;
    return tot;
    }
    
    int find(int x){
    int r=x;
    //一直找父节点
    while(pre[r]!=r){
        r=pre[r];
    }
    return r;
    }
    //合并
    void join(int x,int y){
    int fx=find(x);
    int fy=find(y);
    if(fx!=fy)
        pre[fx]=fy;
    }
    int main()
    {
        int total;
        int n;
        int fx,fy;
        int t=read();
        int p1,p2,k;Map.clear();
        while(t--){
      tot=0;
            n=read();
        //至少2*n个数
            for(int i=1;i<=n*2;i++){
                pre[i]=i;
            }
           for(int i=1;i<=n;i++){
               //
            p1=a[i].p1=read();
            p2=a[i].p2=read();
            a[i].e=read();
            p1=reset(p1);
            p2=reset(p2);
            //如果相等,,,合并
            if(a[i].e){
             fx=find(p1);
              fy=find(p2);
           pre[fx]=fy;
    
            }
           }
           bool flag=true;
           for(int i=1;i<=n;i++){
            if(!a[i].e){
                 p1=reset(a[i].p1);
                 p2=reset(a[i].p2);
                 fx= find(p1);
                 fy=find(p2);
                 //如果已经合并得不相等
                if(fx==fy)
                {    flag=false;
                    break;
                }
            }
           }
           if(flag)
            puts("YES");
           else puts("NO");
        }
        return 0;
    } 

leetcode947

class Solution {
    int []fa;
    int n;
    public void init(){
        n=0;
    fa=new int [10001];
    for(int i=1;i<10001;i++){
          fa[i]=i;
        }
       
    }
    public int find(int x){
        if(fa[x]==x){
            return x;
        }
        else {
            fa[x]=find(fa[x]);
            return fa[x];
        }
    }
    public void merge(int i,int j){
       fa[find(i)]=find(j);
       n++;
    }

    public int removeStones(int[][] stones) {
     int len=stones.length;
        init();
        for(int i=0;i<len;i++){
            for(int j=i;j<len;j++){
                if(find(i)!=find(j))
                if(stones[i][0]==stones[j][0]||stones[j][1]==stones[i][1]){
                    merge(i,j);
                }
            }
        }
        return n;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值