回溯算法练习day.4

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.hqwc.cn/news/636758.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Kafka导航【Kafka】

Kafka导航【Kafka】 前言版权推荐Kafka随堂笔记 第三章 生产者3.4生产者分区3.4.1.分区好处3.4.2 生产者发送消息的分区策略3.4.3 自定义分区器 3.5 生产经验——生产者如何提高吞吐量3.6 生产经验——数据可靠性3.7 生产经验——数据去重3.7.1 数据传递语义3.7.2 幂等性3.7.3生…

虚拟机扩容方法

概述 我的虚拟机开始的内存是40G,接下来要扩成60GB 扩容步骤 步骤1 步骤2 步骤3 修改扩容后的磁盘大小&#xff0c;修改后的值只可以比原来的大&#xff0c;修改完成后点击扩展&#xff0c;等待扩展完成 步骤4 虽然外面扩展成功&#xff0c;但是新增的磁盘空间虚拟机内部还…

C++:类与对象完结篇

hello&#xff0c;各位小伙伴&#xff0c;本篇文章跟大家一起学习《C&#xff1a;运算符重载》&#xff0c;感谢大家对我上一篇的支持&#xff0c;如有什么问题&#xff0c;还请多多指教 &#xff01; 文章目录 重新认识构造函数1.初始化列表2.explicit关键字 static成员1.sta…

密码学 | 承诺:常见的承诺方案

&#x1f951;原文&#xff1a;密码学原语如何应用&#xff1f;解析密码学承诺的妙用 - 知乎 1 简介 密码学承诺 涉及 承诺方、验证方 两个参与方&#xff0c;以及以下两个阶段&#xff1a; 承诺阶段&#xff1a;承诺方选择一个敏感数据 v v v&#xff0c;为它计算出相应…

Adobe Acrobat DC 2022:全方位PDF编辑利器,解锁文档处理新境界

在当今信息爆炸的时代&#xff0c;PDF格式因其跨平台性、稳定性以及易读性而备受欢迎&#xff0c;成为办公、学习和交流的常用格式。Adobe Acrobat DC 2022作为专业的PDF编辑软件&#xff0c;凭借其卓越的性能和丰富的功能&#xff0c;赢得了众多用户的青睐。 Adobe Acrobat D…

paho-mqtt 库揭秘

文章目录 **paho-mqtt 库揭秘**第一部分&#xff1a;背景介绍第二部分&#xff1a;paho-mqtt 是什么&#xff1f;第三部分&#xff1a;如何安装这个库&#xff1f;第四部分&#xff1a;库函数使用方法第五部分&#xff1a;场景应用第六部分&#xff1a;常见Bug及解决方案第七部…

AI大模型探索之路-实战篇1:基于OpenAI智能翻译助手实战落地

文章目录 前言一、需求规格描述二、系统架构设计三、技术实施方案四、核心功能说明五、开源技术选型六、代码实现细节1.图形用户界面&#xff08;GUI&#xff09;的开发2.大型模型调用的模块化封装3.文档解析翻译结果处理 总结 前言 在全球化的浪潮中&#xff0c;语言翻译需求…

Ubuntu无法安装向日癸15.2.0.63062_amd64.deb最新版

Ubuntu安装向日葵远程控制 安装包下载 安装方式 方式一&#xff1a;运行安装包安装 方式二&#xff1a;终端命令安装 通过以下教程可以快速的安装向日葵远程控制&#xff0c;本教程适用于Ubuntu18.04/20.04/22.04 安装包下载 进入向日葵远程控制下载官网下载向日葵远程控制Lin…

【Linux】对system V本地通信的内核级理解

一、system V版本的进程间通信技术 通过之前的学习&#xff0c;我们大致可以感受出来&#xff0c;共享内存&#xff0c;消息队列和信号量在使用的时候是有很多共性的。它们三个的接口&#xff0c;包括接口中传的参数有的都有很大的相似度。其实&#xff0c;共享内存&#xff…

Learn ComputeShader 01 First Computer Shader

使用Unity版本&#xff1a;2019.4.12f1 整体流程&#xff1a; 1添加一个quad object并添加一个无光照材质 2.相机投影模式设置为正交 3.调整quad使其完全显示在相机内 4.创建脚本并且使用计算着色器覆盖quad的纹理 5.创建一个compute shader 前三步完成以后结果应该是这…

宽带上网技术发展(xDSL、PON)

文章目录 xDSL&#xff08;x数字用户线&#xff0c;x Digital Subscriber Line&#xff09;IDSL(基于ISDN数字用户线路)ADSL(不对称数字用户线路)RADSL(速率自适应数字用户线路)HDSL(高速率数字用户线路)VDSL(极高速率数字用户线路)SDSL(单对线路/对称数字用户线路) PON&#x…

《深入浅出.NET框架设计与实现》笔记2——C#源码从编写到执行的流程

中间语言&#xff08;Intermediate Language&#xff0c;IL&#xff09; C#编译器在编译时&#xff0c;会将源代码作为输入&#xff0c;并以中间语言形式输入出&#xff0c;该代码保存在*.exe文件中或*.dll文件中。 公共语言运行时&#xff08;CLR&#xff09; 可以将IL代码…