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);
}