代码随想录算法训练营第二十八天|93.复原IP地址(分割这部分还有问题)、78.子集 、90.子集II

目录

93. 复原 IP 地址

78. 子集

90. 子集 II

树枝去重与数层去重


题目描述:

有效 IP 地址 正好由四个整数(每个整数位于 0 到 255 之间组成,且不能含有前导 0),整数之间用 '.' 分隔。

    例如:"0.1.2.201" 和 "192.168.1.1" 是 有效 IP 地址,但是 "0.011.255.245"、"192.168.1.312" 和 "192.168@1.1" 是 无效 IP 地址。

给定一个只包含数字的字符串 s ,用以表示一个 IP 地址,返回所有可能的有效 IP 地址,这些地址可以通过在 s 中插入 '.' 来形成。你 不能 重新排序或删除 s 中的任何数字。你可以按 任何 顺序返回答案。

也属于分割问题。像上一题一样startIndex就表示分割线。

分割问题我真,上一道题是三维数组让我头疼,这道题代码逻辑我搞不清楚!

粘个代码随想录代码之后看!

//记录结果
char** result;
int resultTop;
//记录应该加入'.'的位置
int segments[3];
int isValid(char* s, int start, int end) {
    if(start > end)
        return 0;
    if (s[start] == '0' && start != end) { // 0开头的数字不合法
                return false;
    }
    int num = 0;
    for (int i = start; i <= end; i++) {
        if (s[i] > '9' || s[i] < '0') { // 遇到非数字字符不合法
            return false;
        }
        num = num * 10 + (s[i] - '0');
        if (num > 255) { // 如果大于255了不合法
            return false;
        }
    }
    return true;
}

//startIndex为起始搜索位置,pointNum为'.'对象
void backTracking(char* s, int startIndex, int pointNum) {
    //若'.'数量为3,分隔结束
    if(pointNum == 3) {
        //若最后一段字符串符合要求,将当前的字符串放入result种
        if(isValid(s, startIndex, strlen(s) - 1)) {
            char* tempString = (char*)malloc(sizeof(char) * strlen(s) + 4);
            int j;
            //记录添加字符时tempString的下标
            int count = 0;
            //记录添加字符时'.'的使用数量
            int count1 = 0;
            for(j = 0; j < strlen(s); j++) {
                tempString[count++] = s[j];
                //若'.'的使用数量小于3且当前下标等于'.'下标,添加'.'到数组
                if(count1 < 3 && j == segments[count1]) {
                    tempString[count++] = '.';
                    count1++;
                }
            }
            tempString[count] = 0;
            //扩容result数组
            result = (char**)realloc(result, sizeof(char*) * (resultTop + 1));
            result[resultTop++] = tempString;
        }
        return ;
    }

    int i;
    for(i = startIndex; i < strlen(s); i++) {
        if(isValid(s, startIndex, i)) {
            //记录应该添加'.'的位置
            segments[pointNum] = i;
            backTracking(s, i + 1, pointNum + 1);
        }
        else {
            break;
        }
    }
}

char ** restoreIpAddresses(char * s, int* returnSize){
    result = (char**)malloc(0);
    resultTop = 0;
    backTracking(s, 0, 0);
    *returnSize = resultTop;
    return result;
}

78. 子集

题目描述:

给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。

解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。

与之前题目不同的是:之前题目中总是在树形结构的叶子节点收获结果,而此次在树形结构的每一层都收获结果。每一层递归中都包含想要收获的结果。

收获结果代码的位置应该在判断终止之前而不是之后,要考虑最后一层是否进入结果集。

(终止条件可以没有,我转不过来还是写上吧)

int** ret;
int ret_num;
int* path;
int path_num;
int *len;

void backtracking(int* nums,int numsSize,int startIndex){
    //收获结果
    int* temp=(int*)malloc(sizeof(int)*path_num);
    for(int i=0;i<path_num;i++){
        temp[i]=path[i];
    }
    len[ret_num]=path_num;
    ret[ret_num++]=temp;
    //终止条件
    if(startIndex>=numsSize){
        return ;
    }
    //单层处理
    for(int i=startIndex;i<numsSize;i++){
        path[path_num++]=nums[i];
        //递归
        backtracking(nums,numsSize,i+1);
        //回溯
        path_num--;
    }
}
int** subsets(int* nums, int numsSize, int* returnSize, int** returnColumnSizes){
    ret=(int**)malloc(sizeof(int*)*1025);
    path=(int*)malloc(sizeof(int)*numsSize);
    len=(int**)malloc(sizeof(int*)*1025);
    ret_num=path_num=0;
    backtracking(nums,numsSize,0);
    *returnSize=ret_num;
    *returnColumnSizes = malloc(sizeof(int)*ret_num);
    for(int i=0;i<ret_num;i++){
        (*returnColumnSizes)[i]=len[i];
    }
    return ret;
}

90. 子集 II

题目描述:

给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。

解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。

与78. 子集不同点在于:78规范了数组中元素只出现一次各不相同,而本题中没有此要求

我的想法:(树枝去重)根据与78.的差异,想到将数组排序后去重再向78.一样收割结果。可以在递归开始前对数组进行去重,也可以在递归的单层处理过程中对与前者相同的元素不做递归操作,如下面代码中被注释掉的一行:

//单层处理
for(int i=startIndex;i<numsSize;i++){
        // if(i>0&&nums[i]==nums[i-1])     continue;
        path[path_num++]=nums[i];
        backtracking(nums,numsSize,i+1);
        path_num--;

很遗憾,这样行不通。会漏掉解。执行结果的差异如下:

 而正确的做法应该是数层去重

int** ret;
int ret_num;
int* path;
int path_num;
int *len;
int *state; //记录数组中元素的使用状态

int cmp_int(const void* e1,const void* e2){
    return *(int*)e1-*(int*)e2;
}
void backtracking(int* nums,int numsSize,int startIndex){
    //收割结果
    int* temp=(int*)malloc(sizeof(int)*path_num);
    for(int i=0;i<path_num;i++){
        temp[i]=path[i];
    }
    len[ret_num]=path_num;
    ret[ret_num++]=temp;
    //终止判断
    if(startIndex>=numsSize){
        return ;
    }
    //单层处理
    for(int i=startIndex;i<numsSize;i++){
        //树枝去重
        //state[i-1]==0表示做的是树枝去重
        if(i>0&&nums[i]==nums[i-1]&&state[i-1]==0)     continue;
        path[path_num++]=nums[i];
        //当前元素标记为使用
        state[i]=1;
        //递归
        backtracking(nums,numsSize,i+1);
        //回溯
        path_num--;
        //当前元素标记为未使用
        state[i]=0;
    }
}
int** subsetsWithDup(int* nums, int numsSize, int* returnSize, int** returnColumnSizes){
    //排序
    qsort(nums,numsSize,sizeof(int),cmp_int);
    ret=(int**)malloc(sizeof(int*)*1025);
    path=(int*)malloc(sizeof(int)*numsSize);
    len=(int*)malloc(sizeof(int)*1025);
    state=(int*)malloc(sizeof(int*)*numsSize);
    memset(state,0,sizeof(int*)*numsSize);
    ret_num=path_num=0;
    backtracking(nums,numsSize,0);
    *returnSize=ret_num;
    *returnColumnSizes = malloc(sizeof(int)*ret_num);
    for(int i=0;i<ret_num;i++){
        (*returnColumnSizes)[i]=len[i];
    }
    return ret;
}

树枝去重与数层去重

数层去重后每个结果中元素可以有重复。(也就是数值重复的元素不再重新选择)

树枝去重后每个结果中没有重复元素。

//树枝去重
if(i>0&&nums[i]==nums[i-1])     continue;
//数层去重
if(i>0&&nums[i]==nums[i-1]&&state[i-1]==0)     continue;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值