回溯法题目合集——叁

1.子集2

给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。

题目链接.

解析过程:
返回所有子集,因此不存在判断条件和剪枝条件,每一次递归都需要将临时数组内容拷贝至二维数组之中

求子集是求组合,因此元素不能有重复解,因此需要传递一个sub变量作为下标,来控制用过的元素不会再用

数组中有重复元素,因此需要去重,去重方法是先排序,然后判断相同的元素是不是第二次出现,如果是第二次出现,则舍弃这一个元素

去重原理:
当排序后,相同的元素紧挨在了一起,因此不管是先用谁,接下来的子集中都是重复的,因为两个相同元素做为父节点后,其余元素还是子节点,因此得到的子集并没有什么不同,所以相同元素只能使用一次
在这里插入图片描述

void GetNum(int* nums, int numsSize, int* returnSize, int** returnColumnSizes,int **ret,int *temp,int *flag,int temp_sub,int sub)
{
    //进行拷贝
    ret[*returnSize]=(int *)malloc(sizeof(int)*temp_sub);
    memcpy(ret[*returnSize],temp,temp_sub*sizeof(int));
    returnColumnSizes[0][*returnSize]=temp_sub;
    (*returnSize)++;

    for(int i=sub;i<numsSize;i++)
    {
        if(i>sub&&nums[i-1]==nums[i])//去重,允许上下级,不允许同一层,在同一层中sub是不会变的
        continue;
        
        temp[temp_sub]=nums[i];

        GetNum( nums,  numsSize, returnSize, returnColumnSizes,ret,temp,flag,temp_sub+1,i+1);
    }
}

int Compare(int *x,int *y)
{
    return *x-*y;
}

int** subsetsWithDup(int* nums, int numsSize, int* returnSize, int** returnColumnSizes){

qsort(nums,numsSize,sizeof(int),Compare);//排序

//开辟数组空间
int **ret=(int **)malloc(sizeof(int*)*2000);
*returnSize=0;
returnColumnSizes[0]=(int *)calloc(2000,sizeof(int));
int *temp=(int *)malloc(sizeof(int)*1000);

GetNum( nums,  numsSize, returnSize, returnColumnSizes,ret,temp,flag,0,0);

return ret;

}

}

2.括号生成

数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
题目链接

根据题意,是需要找出所有符合要求的括号组合,组合问题,特别是不同的组合,第一想法就应该是回溯法;

我们可以很容易的找到剪枝的条件,即左括号或者右括号的数量大于n的时候,肯定不能满足条件,直接进行剪枝

我们先用一个临时数组容纳括号,当满足条件左括号数量等于右括号,且括号总数满足2*n,即可以进行拷贝到返回数组之中

接下来,就应该寻找写递归的方式和条件。递归过程之中,我们可以先将左括号全部填满,再去填写右括号

在这里插入图片描述

void Get(char**ret,char *temp,int *returnSize,int n,int left,int right,int sub)
{
    if(left>n||right>n)//超过一半代表不会匹配
    {
        return;
    }

    if(left==right&&left+right==2*n)//左右括号相等且相加等于总括号数
    {
        ret[*returnSize]=(char *)malloc(sizeof(char)*(2*n+1));//开辟空间,\0留空间
        memcpy(ret[*returnSize],temp,sizeof(char)*n*2);
        ret[*returnSize][2*n]='\0';//终止符
        (*returnSize)++;
        return;
    }

    //往临时数组之内填入括号

    if(left<n)
    {
        temp[sub]='(';
         Get(ret,temp,returnSize,n,left+1,right,sub+1);
    }

if(right<left)
 {
     temp[sub]=')';
     Get(ret,temp,returnSize,n,left,right+1,sub+1);
 }
}
char ** generateParenthesis(int n, int* returnSize){
    
    //回溯算法实现,通过树状结构,给定两个变量记录左括号和右括号的数
    //如果有括号的数量大于左括号,则进行剪枝处理
    //一开始全用左括号

    char **ret=(char **)malloc(sizeof(char*)*2000);
    char *temp=(char *)malloc(sizeof(char)*(n*2+1));//每个一维数组内有多少括号,需要给\0开辟空间
    *returnSize=0;

    Get(ret,temp,returnSize,n,0,0,0);
    return ret;
}

3.数独

编写一个程序,通过填充空格来解决数独问题。
一个数独的解法需遵循如下规则:
数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。
空白格用 ‘.’ 表示。

题目链接
在这里插入图片描述

int JudgeNum(char **board,int row,int col,char ch)//判断是否能够放入字符
{
    for(int i=0;i<9;i++)
    {
        if(board[row][i]==ch)//当前行是否有重复数字
        return 0;

        if(board[i][col]==ch)//当前列是否有重复数字
        return 0;

        if(board[3*(row/3)+i/3][3*(col/3)+i%3]==ch)//当前九宫格有重复数字
        return 0;
    }
    return 1;//可以填入

}   

int GetNum(char** board,int row,int col)
{
    if(row==8&&col==9)//递归出口,刚刚填好最后一个字符
            return 1;
    if(col==9)//只是填好某一行的最后一个字符
    {
        row++;
        col=0;
    }

    while(board[row][col]!='.')//当前位置有字符
        {
            col++;

            if(row==8&&col==9)//递归出口
            return 1;

            if(col==9)//当前行填满了
            {
                row++;
                 col=0;
             }
        }

        //经过上面的循环到达这里,说明此处位置需要填入字符
    
    for(char i='1';i<='9';i++)//循环填入字符
    {
        if(JudgeNum(board,row,col,i))//判断是否能够填入
           {
                board[row][col]=i;//填入数字

                int num=GetNum( board,row,col+1);//下一个位置继续填写

                if(num)//填好了,所有数字都满足条件
                {
                    return 1;
                }
                else//有位置不能填入,回溯重新填写字符
                {
                  board[row][col]='.';//回溯'
                }
           }
        else
        continue;//当前字符不能填入,换一个字符   
    }
    //到达此位置,说明,所有的字符都不能填入,返回0
    return 0;
}


void solveSudoku(char** board, int boardSize, int* boardColSize){

    GetNum(board,0,0);

}
  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值