首页 > 编程语言 >代码随想录算法训练营day25:回溯04:491.递增子序列;46.全排列

代码随想录算法训练营day25:回溯04:491.递增子序列;46.全排列

时间:2024-07-27 22:54:07浏览次数:10  
标签:numsSize 04 returnSize day25 nums 随想录 int ans path

491.递增子序列

力扣题目链接(opens new window)

给定一个整型数组, 你的任务是找到所有该数组的递增子序列,递增子序列的长度至少是2。

示例:

  • 输入: [4, 6, 7, 7]
  • 输出: [[4, 6], [4, 7], [4, 6, 7], [4, 6, 7, 7], [6, 7], [6, 7, 7], [7,7], [4,7,7]]

说明:

  • 给定数组的长度不会超过15。
  • 数组中的整数范围是 [-100,100]。
  • 给定数组中可能包含重复数字,相等的数字应该被视为递增的一种情况。

分析:

使序列合法的办法非常简单,即给「选择」做一个限定条件,只有当前的元素大于等于上一个选择的元素的时候才能选择这个元素,这样枚举出来的所有元素都是合法的

那如何保证没有重复呢?我们需要给「不选择」做一个限定条件,只有当当前的元素不等于上一个选择的元素的时候,才考虑不选择当前元素,直接递归后面的元素。因为如果有两个相同的元素,我们会考虑这样四种情况:——考虑怎么样会重复?需要避免的情况是什么样的?

x前者被选择,后者被选择
前者被选择,后者不被选择
前者不被选择,后者被选择
前者不被选择,后者不被选择
其中第二种情况和第三种情况其实是等价的,我们这样限制之后,舍弃了第二种,保留了第三种,于是达到了去重的目的。——也就是前者被选择的时候,后者一定会被选择

作者:力扣官方题解

终止情况不仅要考虑合法的、输出的情况,更需要考虑遍历完了 但是非法的情况
 

void bp(int *nums,int numsSize,int *returnSize,int *path,int p,int **returnColumnSizes,int start,int **ans){
    if(start>=numsSize && p>1){//至少有两个元素 
        ans[*returnSize]=malloc(sizeof(int)*p);
        memcpy(ans[*returnSize], path, sizeof(int)*p);
        (*returnColumnSizes)[(*returnSize)++]=p;
        return;
    }
    else if(start>=numsSize) return;

    int j=start;
    if(p>=1 && nums[j]==path[p-1]){//和前一个数字相同必须放入
        path[p]=nums[j];
        bp(nums, numsSize, returnSize,path,  p+1, returnColumnSizes, j+1,ans);
    }
    else if(p==0 || ( p>=1 && nums[j]>=path[p-1] ))
    {//放入第一个数,或者和前一个放入的数字不同 且是递增关系的时候,可以任选放入和不放入
        //不放
        bp(nums, numsSize, returnSize,path,  p, returnColumnSizes, j+1,ans);

        //放
        path[p]=nums[j];
        bp(nums, numsSize, returnSize,path,  p+1, returnColumnSizes, j+1,ans);

    }
    else bp(nums, numsSize, returnSize,path,  p, returnColumnSizes, j+1,ans);//不放
    

}

int** findSubsequences(int* nums, int numsSize, int* returnSize, int** returnColumnSizes) {
    int **ans=(int **)malloc(sizeof(int *)*(pow(2,numsSize)-numsSize-1));
    *returnSize=0;

    int *path=(int *)malloc(sizeof(int)*numsSize);
    
    *returnColumnSizes=(int *)malloc(sizeof(int)*(pow(2,numsSize)-numsSize-1));
    bp(nums, numsSize, returnSize, path, 0, returnColumnSizes, 0,ans);
    return ans;
}

46.全排列

力扣题目链接(opens new window)

给定一个 没有重复 数字的序列,返回其所有可能的全排列。

示例:

  • 输入: [1,2,3]
  • 输出: [ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1] ]

分析:

已经很像dfs搜索了,需要用used数组来判断这个数据是否被轮到过了,然后再bp之后 回溯的部分 再把used修改回来

代码:

void bp (int *nums,int numsSize,int *returnSize,int **returnColumnSizes,int *path,int p,int **ans,int *used){
    if(p==numsSize){
        ans[*returnSize]=malloc(sizeof(int)*p);
        memcpy(ans[*returnSize], path, sizeof(int)*p);
        (*returnColumnSizes)[(*returnSize)++]=p;

        return;
    }
    for (int i=0;i<numsSize;i++){
        //不放
        if(used[i]==1) continue;
        
        //放入
        path[p]=nums[i];
        used[i]=1;
        bp(nums, numsSize, returnSize, returnColumnSizes, path,p+1, ans, used);
        used[i]=0;
    }

}


int** permute(int* nums, int numsSize, int* returnSize, int** returnColumnSizes) {
    int **ans =malloc(sizeof(int *)*6*5*4*3*2);
    *returnSize=0;

    int *path=(int *)malloc(sizeof(int)*numsSize);
    int *used=(int *)malloc(sizeof(int)*numsSize);
    

    *returnColumnSizes=(int *)malloc(sizeof(int)*6*5*4*3*2);
    for (int i=0;i<numsSize;i++){
        used[i]=0;
    }
    bp(nums, numsSize, returnSize, returnColumnSizes, path,  0, ans, used);
    return ans;
}

47.全排列 II

力扣题目链接(opens new window)

给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。

示例 1:

  • 输入:nums = [1,1,2]
  • 输出: [[1,1,2], [1,2,1], [2,1,1]]

示例 2:

  • 输入:nums = [1,2,3]
  • 输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

提示:

  • 1 <= nums.length <= 8
  • -10 <= nums[i] <= 10

分析:

这是为什么呢,就是上面我刚说的,如果要对树层中前一位去重,就用used[i - 1] == false,如果要对树枝前一位去重用used[i - 1] == true

对于排列问题,树层上去重和树枝上去重,都是可以的,但是树层上去重效率更高!

这么说是不是有点抽象?

来来来,我就用输入: [1,1,1] 来举一个例子。

树层上去重(used[i - 1] == false),的树形结构如下:

47.全排列II2

树枝上去重(used[i - 1] == true)的树型结构如下:

47.全排列II3

大家应该很清晰的看到,树层上对前一位去重非常彻底,效率很高,树枝上对前一位去重虽然最后可以得到答案,但是做了很多无用搜索。

代码:

int cmp(const void *a,const void *b) {
	return *(int*)a-*(int*)b;
}

void bp (int *nums,int numsSize,int *returnSize,int **returnColumnSizes,int *path,int p,int **ans,int *used){
    if(p==numsSize){
        ans[*returnSize]=malloc(sizeof(int)*p);
        memcpy(ans[*returnSize], path, sizeof(int)*p);
        (*returnColumnSizes)[(*returnSize)++]=p;
        return;
    }
    int prior;
    for (int i=0;i<numsSize;i++){
        //不放
        if(used[i]==1) continue;
        if(i<numsSize-1  && nums[i]==nums[i+1]  && used[i+1]==0) continue;
        
        //放入
        path[p]=nums[i];
        used[i]=1;
        bp(nums, numsSize, returnSize, returnColumnSizes, path,p+1, ans, used);
        used[i]=0;
    }

}

int** permuteUnique(int* nums, int numsSize, int* returnSize, int** returnColumnSizes) {
    int len=1;
    for (int x=1;x<=numsSize;x++){
        len=len*x;
    }
    int **ans =malloc(sizeof(int *)*len);
    *returnSize=0;

    qsort(nums, numsSize, sizeof(int), cmp);

    int *path=(int *)malloc(sizeof(int)*numsSize);
    int *used=(int *)malloc(sizeof(int)*numsSize);

    *returnColumnSizes=(int *)malloc(sizeof(int)*len);
    for (int i=0;i<numsSize;i++){
        used[i]=0;
    }
    bp(nums, numsSize, returnSize, returnColumnSizes, path,  0, ans, used);
    return ans;
}

标签:numsSize,04,returnSize,day25,nums,随想录,int,ans,path
From: https://blog.csdn.net/weixin_65180740/article/details/140738728

相关文章

  • 代码随想录算法训练营第22天-leetcode-回溯算法part01:
    #回溯算法理论基础能解决的问题:组合问题:N个数里面按一定规则找出k个数的集合切割问题:一个字符串按一定规则有几种切割方式子集问题:一个N个数的集合里有多少符合条件的子集排列问题:N个数按一定规则全排列,有几种排列方式棋盘问题:N皇后,解数独等等第77题.组合力扣题目链接(op......
  • 代码随想录算法训练营第24天:回溯03:93.复原IP地址;78.子集;90.子集II
    93.复原IP地址力扣题目链接(opensnewwindow)给定一个只包含数字的字符串,复原它并返回所有可能的IP地址格式。有效的IP地址正好由四个整数(每个整数位于0到255之间组成,且不能含有前导0),整数之间用'.'分隔。例如:"0.1.2.201"和"192.168.1.1"是有效的IP地址,......
  • P10463 Interval GCD
    P10463IntervalGCD原题传送门思路首先,有个性质:对于任意多整数,它们的最大公约数与它们的差分序列的最大公约数相同,可以通过以下证明。\(\foralla,b,c\in\mathbb{N}\text{,有}\gcd(a,b,c)=\gcd(a,b-a,c-b)\)\(\text{证明:设}d\mida,d\midb,d\midc\)......
  • 题解:P10481 Sudoku
    Sudoku来自蓝书思路优化搜索顺序,位运算常数优化。优化搜索顺序数独问题的搜索很简单,“搜索状态”就是每个位置上填了什么数。在每个状态下,选择一个未填位置,检查那些填入仍能满足数独条件(合法)的数字。构成的新的局面即为该状态的“分支”。足够暴力的写法为依次对每个位置进......
  • 代码随想录算法训练营第九天 | 151.翻转字符串里的单词,卡码网:55.右旋转字符串,28. 实现
    151.翻转字符串里的单词题目链接:力扣题目链接文章讲解:代码随想录 视频讲解:字符串复杂操作拿捏了!|LeetCode:151.翻转字符串里的单词思路这道题目可以说是综合考察了字符串的多种操作。其实这道题和反转字符串这道题目很像,而且用法也是通用的方法一:切片,reverse,以及......
  • 代码随想录算法训练营第十天 | 232.用栈实现队列 , 225. 用队列实现栈 , 20. 有效的括号
    前两道题目之前单独写了文章,此处就不再重复。232.用栈实现队列-CSDN博客225.用队列实现栈-CSDN博客20.有效的括号题目链接:力扣题目链接文章讲解:代码随想录 视频讲解:栈的拿手好戏!|LeetCode:20.有效的括号思路括号匹配是使用栈解决的经典问题。由于栈结构的......
  • 代码随想录 day 37 完全背包 | 零钱兑换 II | 组合总和 Ⅳ | 爬楼梯 (进阶)
    完全背包完全背包解题思路由于我们可以重复放入物体,那么在遍历背包重量时就必须从前往后遍历,因为这样就可以重复放入了,其余的部分和01背包相同知识点完全背包心得学会了如何解决纯完全背白零钱兑换II零钱兑换II解题思路和之前01背包求总数的思路相同,唯一的不同点在......
  • 04HTML+CSS
    今天开始学了CSS,CSS叫做-层叠样式表。主要是来美化界面的。今日学习内容有1.CSS的引入方式,CSS的引入方式有三种内部样式表:学习使用CSS代码写在style标签里面l外部样式表:开发使用lCSS代码写在单独的CSS文件中(.css)在HTML使用link标签引入,在.CSS文件中的书写方式和......
  • 第十天|栈与队列| 232.用栈实现队列,225. 用队列实现栈,20. 有效的括号,1047. 删除字符串
    目录232.用栈实现队列225.用队列实现栈两个队列模拟栈实现思路1:实现思路2:实现思路3:一个队列模拟栈实现思路1:实现思路2:实现思路3:20.有效的括号1047.删除字符串中的所有相邻重复项方法1:使用Deque堆栈方法2:用字符串直接当作栈方法3:双指针Day10学习到用栈来解......
  • 洛谷P1042 [NOIP2003 普及组] 乒乓球
    题目链接:-P1042[NOIP2003普及组]乒乓球[NOIP2003普及组]乒乓球题目背景国际乒联现在主席沙拉拉自从上任以来就立志于推行一系列改革,以推动乒乓球运动在全球的普及。其中11分制改革引起了很大的争议,有一部分球员因为无法适应新规则只能选择退役。华华就是其中一位,他退役......