回溯算法练习day.4

server/2024/9/22 15:41:26/

93.复原ip地址

链接:. - 力扣(LeetCode)

题目描述:

有效 IP 地址 正好由四个整数(每个整数位于 0255 之间组成,且不能含有前导 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 中的任何数字。你可以按 任何 顺序返回答案。

示例 1:

输入:s = "25525511135"
输出:["255.255.11.135","255.255.111.35"]

示例 2:

输入:s = "0000"
输出:["0.0.0.0"]

提示:

  • 1 <= s.length <= 20
  • s 仅由数字组成

思路:

因为是分割问题,因此可以使用回溯算法解决,我们可以将其抽象为一个树形结构

因为IP地址总共只有3个.,因此我们可以使用一个标记来记录逗点的数量,当逗点足够三个时,我们就不再需要向下遍历,因为再向下就已经不是合法的IP地址了,我们在这时候只需要对剩余的字符串进行判断,如果剩余的字符串是合法的,我们就将其进行收集,并插入逗点,这样就得到了一个合法的IP地址

代码如下:

// 记录结果数组
char** result;
// 结果数组的当前元素数量
int resultTop;
// 记录应该加入'.'的位置的数组
int segments[3];// 判断字符串片段是否有效
int isValid(char* s, int start, int end) {// 若起始位置大于结束位置,则不合法if(start > end)return 0;// 如果数字以0开头并且不止一位,则不合法if (s[start] == '0' && start != end) {return false;}int num = 0;// 遍历字符串片段,将字符转换为数字,并判断是否大于255for (int i = start; i <= end; i++) {// 遇到非数字字符,则不合法if (s[i] > '9' || s[i] < '0') {return false;}num = num * 10 + (s[i] - '0');// 如果数字大于255,则不合法if (num > 255) {return false;}}return true;
}// 回溯函数,用于生成符合条件的 IP 地址
void backTracking(char* s, int startIndex, int pointNum) {// 若'.'数量为3,分隔结束if(pointNum == 3) {// 若最后一段字符串符合要求,将当前的字符串放入结果数组中if(isValid(s, startIndex, strlen(s) - 1)) {// 分配临时字符串数组的内存空间,长度为原字符串长度加上最多3个'.'和一个结束符'\0'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 = (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;}}
}// 主函数,入口点,用于恢复 IP 地址
char ** restoreIpAddresses(char * s, int* returnSize){// 分配结果数组的初始内存空间result = (char**)malloc(0);resultTop = 0;// 调用回溯函数,生成符合条件的 IP 地址backTracking(s, 0, 0);// 将结果数量存储到returnSize指针指向的变量中*returnSize = resultTop;// 返回结果数组return result;
}

78.子集

链接:. - 力扣(LeetCode)

题目描述:

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

子集

(幂集)。

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

示例 1:

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

示例 2:

输入:nums = [0]
输出:[[],[0]]

提示:

  • 1 <= nums.length <= 10
  • -10 <= nums[i] <= 10
  • nums 中的所有元素 互不相同

思路:

因为这题也是求组合问题,因此还是使用回溯算法解决,我们将其抽象为树形结构

就可以发现,树的每个节点都是我们需要收集的子集,注意,我们在第一条支路中取了1,在后边就不需要的取1了,因此第一条支路向下递归搜索取能够取到12,13等,如果在后面的支路中再取前面取过的元素,就会出现21,31等,因为是组合,因此这两个是重复的,所以不需要再进行取值

回溯实现:

1.确定函数参数和返回值:回溯返回值一般为空,传入的参数应该为题目提供的集合以及我们每次遍历的开始位置

2.确定函数的终止条件:当我们每条支路的开始遍历位置已经为空时,即我们已经遍历到了末尾,就到达了叶子节点,就进行返回

3.确定单层递归逻辑,收集路径下的节点,再向下递归,之后再进行回溯,遍历另一条支路

代码实现:

int *path;               // 存储当前路径的数组
int pathtop;             // 当前路径的顶部索引int **result;            // 存储所有子集的数组
int resulttop;           // 存储数组的顶部索引int *len;                // 存储每个子集的长度void copy()
{int *temp = (int *)malloc(sizeof(int) * pathtop);  // 临时数组,用于复制当前路径for(int i = 0; i < pathtop; i++)                   // 复制当前路径到临时数组temp[i] = path[i];result = (int **)realloc(result, sizeof(int *) * (resulttop + 1)); // 重新分配存储子集的数组大小len = (int *)realloc(len, sizeof(int) * (resulttop + 1)); // 重新分配存储子集长度的数组大小len[resulttop] = pathtop;                          // 存储当前子集的长度result[resulttop++] = temp;                        // 将当前子集添加到结果数组中
}void backtracking(int *nums, int numsSize, int startindex)
{copy();                                            // 复制当前路径到结果数组中if(startindex >= numsSize )return;for(int i = startindex; i < numsSize; i++){path[pathtop++] = nums[i];                    // 将当前元素添加到路径中backtracking(nums, numsSize, i+1);             // 递归调用,查找以当前元素开头的所有子集pathtop--;                                     // 回溯,移除最后添加的元素} 
}int** subsets(int* nums, int numsSize, int* returnSize, int** returnColumnSizes) {path  = (int *)malloc(sizeof(int) * numsSize);     // 初始化路径数组result = (int **)malloc(sizeof(int *));            // 初始化存储子集的数组len = (int *)malloc(sizeof(int) * 1000);           // 初始化存储子集长度的数组resulttop = pathtop = 0;                           // 初始化路径顶部索引和结果顶部索引为0backtracking(nums, numsSize, 0);                   // 开始回溯查找所有子集*returnSize = resulttop;                           // 设置返回的子集个数*returnColumnSizes = (int *)malloc(sizeof(int) * resulttop); // 为每个子集的长度分配内存for(int i = 0; i < resulttop; i++)(*returnColumnSizes)[i] = len[i];              // 存储每个子集的长度return result;                                     // 返回所有子集数组
}

90.子集II

链接:. - 力扣(LeetCode)

题目描述:

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

子集

(幂集)。

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

示例 1:

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

示例 2:

输入:nums = [0]
输出:[[],[0]]

提示:

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

思路:

该题与上题一样,也是求组合问题,但是这个题要求不能包含重复子集,因为我们要进行去重操作,可以考虑到使用回溯算法解决,所以可以抽象为一个树形结构

我们可以看出,当我们横向出现重复的元素时,得到的结果就会重复,因此就需要在横向进行去重操作,而在纵向,因为我们取出的是集合中不同位置的元素,因此就不需要去重,在这里,我们收集的也是每个节点的结果,只是增加了去重的操作

回溯实现:

1.确定函数的参数和返回值,参数应该为题目提供和集合和开始遍历的位置

2.确定终止条件,当我们遍历到叶子节点,即开始位置已经是集合末尾时,退出

3.确定单层递归逻辑,当我们在遍历时,只要横向不重复,就进行收集路径,并进行递归和回溯,如果发现重复,则跳过

注意:我们应该在终止条件之前进行结果的收集,因为那是新的递归遍历的开始,每次开始前我们都应该对上次递归的结果进行收集,因为上次的结果是我们的节点

代码实现:

/*** 返回大小为 *returnSize 的数组的数组。* 数组的大小作为 *returnColumnSizes 数组返回。* 注意:返回的数组和 *columnSizes 数组都必须是通过 malloc 分配的,假设调用者会调用 free()。*/int *path; // 路径数组
int pathtop; // 路径长度int **result; // 结果数组
int resulttop; // 结果数组的长度int *len; // 每个子集的长度数组// 比较函数,用于排序数组
int cmp(const void *a, const void *b)
{return *((int *)a) - *((int *)b);
}// 复制当前路径并添加到结果数组中
void copy(void)
{int *temp = (int *)malloc(sizeof(int) * pathtop);for (int i = 0; i < pathtop; i++)temp[i] = path[i];result = (int **)realloc(result, sizeof(int *) * (resulttop + 1));len = (int *)realloc(len, sizeof(int) * (resulttop + 1));len[resulttop] = pathtop;result[resulttop++] = temp;
}// 回溯函数,用于递归生成所有子集
void backtracking(int *nums, int numsSize, int startindex, int *used)
{copy(); // 复制当前路径if (startindex >= numsSize)return;for (int i = startindex; i < numsSize; i++){if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == 0)continue; // 避免重复元素path[pathtop++] = nums[i];used[i] = 1;backtracking(nums, numsSize, i + 1, used);used[i] = 0;pathtop--;}
}// 主函数,生成所有不重复的子集
int **subsetsWithDup(int *nums, int numsSize, int *returnSize, int **returnColumnSizes)
{path = (int *)malloc(sizeof(int) * numsSize);len = (int *)malloc(sizeof(int) * 1000);int *used = (int *)malloc(sizeof(int) * numsSize);pathtop = resulttop = 0;qsort(nums, numsSize, sizeof(int), cmp); // 排序输入数组backtracking(nums, numsSize, 0, used); // 从第一个元素开始生成子集*returnSize = resulttop;*returnColumnSizes = (int *)malloc(sizeof(int) * resulttop);for (int i = 0; i < resulttop; i++)(*returnColumnSizes)[i] = len[i];return result; // 返回生成的子集数组
}


http://www.ppmy.cn/server/8052.html

相关文章

[2024更新]如何从Android恢复已删除的相机照片?

相信大家都经历过Android手机误删相机图片的经历。您是否正在寻找一种可行的方法来挽救这些丢失的照片&#xff1f;如果这是你迫切想解决的问题&#xff0c;那么这篇文章绝对可以帮助你。然而&#xff0c;与其考虑如何从Android恢复已删除的相机照片&#xff0c;我们更愿意建议…

图论——基础概念

文章目录 学习引言什么是图图的一些定义和概念图的存储方式二维数组邻接矩阵存储优缺点 数组模拟邻接表存储优缺点 边集数组优缺点排序前向星优缺点链式前向星优缺点 学习引言 图论&#xff0c;是 C 里面很重要的一种算法&#xff0c;今天&#xff0c;就让我们一起来了解一下图…

12.Ribbon饥饿加载

Ribbon默认是懒加载的&#xff0c;第一次使用Ribbon访问的时候才会去实例化对象&#xff0c;所以第一次访问比较耗时。 ribbon:eager-load:enabled: true # 开启饥饿加载clients: user-service #对user-service这个服务饥饿加载 多个微服务的写法&#xff1a; ribbon:eager-loa…

Vscode | Python | launch.json配置gevent多进程断点失效问题处理

Vscode | Python | launch.json配置gevent多进程断点失效问题处理 文章目录 情况描述↓↓↓解决办法直接看这里↓↓↓ 情况描述 launch.json {// Use IntelliSense to learn about possible attributes.// Hover to view descriptions of existing attributes.// For more i…

【Linux驱动层】iTOP-RK3568学习之路(二):vscode中设置头文件路径-完成代码自动补全

在Ubuntu下用vscode写Linux驱动层的时候&#xff0c;需要添加头文件&#xff1a; #include<linux/module.h> #include<linux/init.h> #include<linux/kernel.h>但vscode没有智能提示&#xff0c;因此需要我们手动添加自己的头文件路径&#xff1a; topeetu…

arm编译、u-boot编译过程、linux内核编译

arm编译 我们之前在linux编译时使用gcc就行,但是在arm中我们需使用arm-linux-gcc 我们需安装交叉编译工具,地址就在119行 若没有,可自行在网上下载 u-boot编译过程 u-boot作为开源项目,可在其官网下载源码,官网 但在实际开发过程中,我们不会直接去u-boot官网下载源码…

[前端]NVM管理器安装、nodejs、npm、yarn配置

NVM管理器安装、nodejs、npm、yarn配置 NVM管理器安装 nvm(Node.js version manager) 是一个命令行应用&#xff0c;可以协助您快速地 更新、安装、使用、卸载 本机的全局 node.js 版本。 nvm下载地址&#xff1a;https://github.com/coreybutler/nvm-windows/releases 1.全部…

Linux Centos 9保姆级系统安装教程

文章目录 下载Centos 9镜像文件安装Centos 下载Centos 9镜像文件 清华大学源网址https://mirrors.tuna.tsinghua.edu.cn/ 安装Centos 所需软件&#xff1a;VMware Workstation 16 Pro 版本里面没有Centos 9&#xff1b; 这里我们选择Centos 7同样可以使用 用户设置