代码随想录第二十五天(一刷&&C语言)|递增子序列&&全排列&&全排列II

本文详细讲解了递增子序列和全排列的解决方案,涉及回溯法在LeetCode题目中的应用,以及如何利用回溯思想解决相关问题。
摘要由CSDN通过智能技术生成

创作目的:为了方便自己后续复习重点,以及养成写博客的习惯。

组合和排列问题是在树形结构的叶子节点上收集结果,而子集问题就是取树上所有节点的结果

一、递增子序列

思路:参考carl文档

        已经是递增序列故而不用排序,元素不能重复使用,需要startIndex,调整下一层递归的起始位置。在遍历树形结构找每一个节点,可以通过startIndex结束递归,可以不加终止条件。通过树形结构了解到,同一层元素使用过就不能再使用了。

ledcode题目:https://leetcode.cn/problems/non-decreasing-subsequences/

AC代码:

int* path;
int pathTop;
int** ans;
int ansTop;
int* length;
//将当前path中的内容复制到ans中
void copy() {
    int* tempPath = (int*)malloc(sizeof(int) * pathTop);
    memcpy(tempPath, path, pathTop * sizeof(int));
    length[ansTop] = pathTop;
    ans[ansTop++] = tempPath;
}

//查找uset中是否存在值为key的元素
int find(int* uset, int usetSize, int key) {
    int i;
    for(i = 0; i < usetSize; i++) {
        if(uset[i] == key)
            return 1;
    }
    return 0;
}

void backTracking(int* nums, int numsSize, int startIndex) {
    //当path中元素大于1个时,将path拷贝到ans中
    if(pathTop > 1) {
        copy();
    }
    int* uset = (int*)malloc(sizeof(int) * numsSize);
    int usetTop = 0;
    int i;
    for(i = startIndex; i < numsSize; i++) {
        //若当前元素小于path中最后一位元素 || 在树的同一层找到了相同的元素,则continue
        if((pathTop > 0 && nums[i] < path[pathTop - 1]) || find(uset, usetTop, nums[i]))
            continue;
        //将当前元素放入uset
        uset[usetTop++] = nums[i];
        //将当前元素放入path
        path[pathTop++] = nums[i];
        backTracking(nums, numsSize, i + 1);
        //回溯
        pathTop--;
    }
}

int** findSubsequences(int* nums, int numsSize, int* returnSize, int** returnColumnSizes){
    //辅助数组初始化
    path = (int*)malloc(sizeof(int) * numsSize);
    ans = (int**)malloc(sizeof(int*) * 33000);
    length = (int*)malloc(sizeof(int*) * 33000);
    pathTop = ansTop = 0;

    backTracking(nums, numsSize, 0);

    //设置数组中返回元素个数,以及每个一维数组的长度
    *returnSize = ansTop;
    *returnColumnSizes = (int*)malloc(sizeof(int) * ansTop);
    int i;
    for(i = 0; i < ansTop; i++) {
        (*returnColumnSizes)[i] = length[i];
    }
    return ans;
}

二、全排列

思路:参考carl文档

        处理排列问题就不需要使用startIndex而是需要一个used数组,标记已经选元素。当收集元素的数组path的大小和nums数组一样大时,说明找到了一个全排列,也表示到达了叶子节点。排列问题每次都要从头开始搜索,used数组就是记录path里有哪些元素使用了,一个排列里一个元素只能使用一次。

lecode题目:https://leetcode.cn/problems/permutations/

AC代码:

int* path;
int pathTop;
int** ans;
int ansTop;

//将used中元素都设置为0
void initialize(int* used, int usedLength) {
    int i;
    for(i = 0; i < usedLength; i++) {
        used[i] = 0;
    }
}

//将path中元素拷贝到ans中
void copy() {
    int* tempPath = (int*)malloc(sizeof(int) * pathTop);
    int i;
    for(i = 0; i < pathTop; i++) {
        tempPath[i] = path[i];
    }
    ans[ansTop++] = tempPath;
}

void backTracking(int* nums, int numsSize, int* used) {
    //若path中元素个数等于nums元素个数,将nums放入ans中
    if(pathTop == numsSize) {
        copy();
        return;
    }
    int i;
    for(i = 0; i < numsSize; i++) {
        //若当前下标中元素已使用过,则跳过当前元素
        if(used[i])
            continue;
        used[i] = 1;
        path[pathTop++] = nums[i];
        backTracking(nums, numsSize, used);
        //回溯
        pathTop--;
        used[i] = 0;
    }
}

int** permute(int* nums, int numsSize, int* returnSize, int** returnColumnSizes){
    //初始化辅助变量
    path = (int*)malloc(sizeof(int) * numsSize);
    ans = (int**)malloc(sizeof(int*) * 1000);
    int* used = (int*)malloc(sizeof(int) * numsSize);
    //将used数组中元素都置0
    initialize(used, numsSize);
    ansTop = pathTop = 0;

    backTracking(nums, numsSize, used);

    //设置path和ans数组的长度
    *returnSize = ansTop;
    *returnColumnSizes = (int*)malloc(sizeof(int) * ansTop);
    int i;
    for(i = 0; i < ansTop; i++) {
        (*returnColumnSizes)[i] = numsSize;
    }
    return ans;
}

三、全排列II

思路:参考carl文档

         与全排列的区别在于给定一个可重复数字的序列,要返回所有不重复的全排列(去重)。而且去重一定要对元素进行排序,通过相邻的节点来判断元素是否重复使用了。     同一树层的前一位(nums[i-1])如果使用过,那么就进行去重。

ledcode题目:https://leetcode.cn/problems/permutations-ii/description/

AC代码:

//临时数组
int *path;
//返回数组
int **ans;
int *used;
int pathTop, ansTop;

//拷贝path到ans中
void copyPath() {
    int *tempPath = (int*)malloc(sizeof(int) * pathTop);
    int i;
    for(i = 0; i < pathTop; ++i) {
        tempPath[i] = path[i];
    }
    ans[ansTop++] = tempPath;
}

void backTracking(int* used, int *nums, int numsSize) {
    //若path中元素个数等于numsSize,将path拷贝入ans数组中
    if(pathTop == numsSize)
        copyPath();
    int i;
    for(i = 0; i < numsSize; i++) {
        //若当前元素已被使用
        //或前一位元素与当前元素值相同但并未被使用
        //则跳过此分支
        if(used[i] || (i != 0 && nums[i] == nums[i-1] && used[i-1] == 0))
            continue;

        //将当前元素的使用情况设为True
        used[i] = 1;
        path[pathTop++] = nums[i];
        backTracking(used, nums, numsSize);
        used[i] = 0;
        --pathTop;
    }
}

int cmp(void* elem1, void* elem2) {
    return *((int*)elem1) - *((int*)elem2);
}

int** permuteUnique(int* nums, int numsSize, int* returnSize, int** returnColumnSizes){
    //排序数组
    qsort(nums, numsSize, sizeof(int), cmp);
    //初始化辅助变量
    pathTop = ansTop = 0;
    path = (int*)malloc(sizeof(int) * numsSize);
    ans = (int**)malloc(sizeof(int*) * 1000);
    //初始化used辅助数组
    used = (int*)malloc(sizeof(int) * numsSize);
    int i;
    for(i = 0; i < numsSize; i++) {
        used[i] = 0;
    }

    backTracking(used, nums, numsSize);

    //设置返回的数组的长度
    *returnSize = ansTop;
    *returnColumnSizes = (int*)malloc(sizeof(int) * ansTop);
    int z;
    for(z = 0; z < ansTop; z++) {
        (*returnColumnSizes)[z] = numsSize;
    }
    return ans;
}

全篇后记:

        在明确思路的前提下,才能开始做,不然总会去走回头路。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值