leetcode526. 优美的排列

时间:2021-08-16
本文章向大家介绍leetcode526. 优美的排列,主要包括leetcode526. 优美的排列使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

题目链接:https://leetcode-cn.com/problems/beautiful-arrangement/

说实话一开始没懂这个题目的意思,是必须用到N个数字,还是从N个数字中选取任意个数字组成数组,看了题解,指的是前者。
我的思路是:1.枚举所有数字的排列组合2.找出符合条件的数组

回溯

回溯法可以被认为是一个有过剪枝的DFS过程。

这里科普一下递归、回溯的dfs
原文链接:https://blog.csdn.net/fengchi863/article/details/80086915

  • 递归是一种算法结构,回溯是一种算法思想。
  • 一个递归就是在函数中调用函数本身来解决问题。
  • 回溯就是通过不同的尝试来生成问题的解,有点类似于穷举,但是和穷举不同的是回溯会“剪枝”。

剪枝的意思也就是说对已经知道错误的结果没必要再枚举接下来的答案了,比如一个有序数列1,2,3,4,5,我要找和为5的所有集合,从前往后搜索我选了1,然后2,然后选3的时候发现和已经大于预期,那么4,5肯定也不行,这就是一种对搜索过程的优化。

回溯搜索是深度优先搜索(DFS)的一种。对于某一个搜索树来说(搜索树是起记录路径和状态判断的作用),回溯和DFS,其主要的区别是,回溯法在求解过程中不保留完整的树结构,而深度优先搜索则记下完整的搜索树。

为了减少存储空间,在深度优先搜索中,用标志的方法记录访问过的状态,这种处理方法使得深度优先搜索法与回溯法没什么区别了。

递归的一般结构:

void f()  
{  
     if(符合边界条件)  
     {  
        ///////  
        return;  
     }  

     //某种形式的调用  
     f();  
}  

回溯的一般结构:

void DFS(int 当前状态)  
{  
      if(当前状态为边界状态)  
      {  
        记录或输出  
        return;  
      }  
      for(i=0;i<n;i++)       //横向遍历解答树所有子节点  
      {  
           //扩展出一个子状态。  
           修改了全局变量  
           if(子状态满足约束条件)  
            {  
              dfs(子状态)  
           }  
            恢复全局变量//回溯部分  
      }  
}  

对于本题,可以用回溯法来解决,从左到右依次向目标排列中放入数即可。

定义函数backtrack(index,n),表示尝试向位置index放入数,其中n表示排列的长度。首先需要找到一个符合条件的未被使用过的数,然后递归地执行backtrack(index+1,n),当该函数执行完毕,回溯到当前层,我们再尝试下一个符合条件的未被使用过的数即可。

回溯过程中,我们可以用vis数组标记哪些数被使用过,每当我们选中一个数x,就将vis[x]标记为true,回溯完成后,我们再将其置为false。

class Solution {
public:
    vector<bool>visited;
    int ans=0;
	
    void backtrack(int n,int index){
        if(index == n+1){//当找到n个符合要求的序列时,满足要求,ans++
            ans++;
        }
        for(int i = 1;i <= n; i++){//枚举寻找第index个满足要求且没有遍历过的元素
            if( !visited[i] && (i%index==0||index%i==0) ){
                visited[i]=true;//标记为遍历过
                backtrack(n, index+1);//寻找第index+1个满足要求的元素
                visited[i]=false;//回溯
            }
        }
    }
	
    int countArrangement(int n) {
        visited.resize(n+1);
        backtrack(n, 1);
        return ans;
    }
};

为了优化回溯效率,我们可以预处理每个位置符合条件的数有哪些,用二维数组match保存。当我们尝试向位置index放入数时,只需要遍历match[index]即可。

class Solution {
public:
    vector<vector<int>> match;
    vector<int> vis;
    int num;

    void backtrack(int index, int n) {
        if (index == n + 1) {
            num++;
            return;
        }
        for (auto &x : match[index]) {
            if (!vis[x]) {
                vis[x] = true;
                backtrack(index + 1, n);
                vis[x] = false;
            }
        }
    }

    int countArrangement(int n) {
        vis.resize(n + 1);
        match.resize(n + 1);
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                if (i % j == 0 || j % i == 0) {
                    match[i].push_back(j);
                }
            }
        }
        backtrack(1, n);
        return num;
    }
};

原文地址:https://www.cnblogs.com/LingFengJ/p/15149855.html