回溯算法练习day.4

news2024/11/16 1:48:16

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;
    // 遍历字符串片段,将字符转换为数字,并判断是否大于255
    for (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;                           // 初始化路径顶部索引和结果顶部索引为0

    backtracking(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.coloradmin.cn/o/1612438.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于单片机的智能病床呼叫系统设计与仿真

摘 要 本文设计的病床呼叫系统采用单片机作为控制器。该系统具有远程控制、病人的身体情况检测、报警呼叫、显示和执行器运动的功能。远程控制由红外线传感器和矩阵键盘组成&#xff0c;检测电路由温湿度传感器DH22、心率传感器Pulse Sensor、压力传感器MPX4115组成&#x…

苹果电脑虚拟机黑屏了怎么办解决 MAC系统升级后虚拟机黑屏问题 苹果电脑虚拟机卡住了怎么办

虚拟机是一种可以在一台电脑上运行多个操作系统的软件&#xff0c;它可以让用户在苹果电脑上安装和使用Windows、Linux等其他系统。但是&#xff0c;有时候在升级Mac系统或者虚拟机软件后&#xff0c;虚拟机会出现黑屏的现象&#xff0c;无法正常启动或者使用。这种情况该如何解…

java swing电商出入库管理系统eclipse开发Mysql数据库CS结构java编程

一、源码特点 java swing 电商出入库管理系统 是一套完善的窗体设计系统&#xff0c;对理解SWING java 编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;&#xff0c;系统主要采用C/S模式开发。 应用技术&#xff1a;javamysql 开发工具&#xff1…

一堆喷儿香喷儿香的工具网站-已经收藏-搜嗖工具箱!

文心一言 https://yiyan.baidu.com/ ​ ChatGpt横空出世的横空出世好像一把钥匙&#xff0c;开启了大模型时代&#xff0c;国内也有不错的产品&#xff0c;比如百度的文心一言&#xff0c;从3.5到4.0看得见的成长&#xff0c;现在的文心一言是我们工作中不可缺少的好帮手&am…

vulfocus靶场之redis命令执行cve-2022-0543漏洞复现

漏洞&#xff1a; Redis是著名的开源Key-Value数据库&#xff0c;其具备在沙箱中执行Lua脚本的能力。 Debian以及Ubuntu发行版的源在打包Redis时&#xff0c;不慎在Lua沙箱中遗留了一个对象package&#xff0c;攻击者可以利用这个对象提供的方法加载动态链接库liblua里的函数&…

【Visual Studio 2012中文版】下载安装以及使用方法

文章目录 前言一、下载安装包二、安装步骤1.双击VS2012_ULT_chs.iso文件打开2.双击vs_ultimate.exe打开安装程序3.选择要安装的功能4.软件正在安装&#xff0c;请耐心等待10分钟5.安装成功&#xff0c;点击“启动”6.激活码&#xff08;产品密钥&#xff09; 三、VS2012使用&am…

【Java】实现一个简单的线程池

&#x1f4dd;个人主页&#xff1a;哈__ 期待您的关注 一、线程池的模式 线程池顾名思义就是管理线程的一个池子&#xff0c;我们把创建线程的过程交给线程池来处理&#xff0c;而这个线程池当中的线程都会从阻塞队列当中取获取任务执行。 我们不在直接把任务的创建过程写到…

京东微服务microApp使用总结

前言 基于现有业务门户进行微服务基础平台搭建 主应用框架&#xff1a;vue3vite 子应用框架&#xff1a;vue2webpack / vue3vite在这里插入代码片 本地调试即可&#xff1a;主应用子应用进行打通&#xff08;注意&#xff1a;两者都是vue3vite&#xff09; 问题总结 1.嵌入…

基于SSM+Jsp+Mysql的高校毕业生就业满意度调查统计系统

开发语言&#xff1a;Java框架&#xff1a;ssm技术&#xff1a;JSPJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包…

new[]与delete[]

&#xff08;要理解之前关于new,delete的一些概念&#xff0c;看​​​​​​ CSDN&#xff09; 引子&#xff1a; 相比new&#xff0c;new[]不仅仅是个数的增加&#xff0c;还有int大小记录空间的创建&#xff0c; 下图中错误的用模拟多个new来替代new[]&#xff0c;释放步…

从C到JAVA之学习JAVA的第一周笔记

文章目录 java语言概述JDK与JRE编写执行过程第一份java代码解读编写编译运行其他 注释三种注释方法 java API文档关键字标识符数据类型基本数据类型自动类型提升规则引用数据类型 string概述String与基本数据类型的变量间的运算 运算符键盘录入运行控制语句数组定义与静态初始化…

【MyBatis】(MyBatis 其他查询操作 多表查询 #{} 和 ${} 排序功能 like 查询 #{} 和 ${} 的区别 数据库连接池)

文章目录 MyBatis其他查询操作多表查询#{} 和 ${}排序功能like 查询#{} 和 ${} 的区别 数据库连接池 MyBatis 其他查询操作 创建表&#xff1a; -- 创建⽂章表 DROP TABLE IF EXISTS articleinfo; CREATE TABLE articleinfo (id INT PRIMARY KEY auto_increment,title VARCH…

2024/4/19学习笔记 vector模拟实现(2)

本次学习重点 1.迭代器区间构造和size_t n 构造 2.string扩容问题 3.erase的缺陷 1.迭代器区间构造和size_t n 构造 vector支持用一段迭代器区间构造&#xff0c;也可以支持任意类型的迭代器区间&#xff0c;所以要写成函数模板 template <class InputIterator> ve…

SL7220线性降压恒流3.6A 外围只需两个电阻 耐压40V汽车大灯IC

概述&#xff1a; SL7220 是一款双路线性降压LED恒流驱动器&#xff0c;外围只需两个电阻&#xff0c;输出电流10MA-3600MA。 SL7220 内置过热保护功能&#xff0c;内置输入过压保护功能。 SL7220 静态电流典型值为120uA。 特点 ●输入电压范围&#xff1a;2.5V-40V ●电…

浅识数据结构之时间复杂度

P. S.&#xff1a;以下代码均在VS2019环境下测试&#xff0c;不代表所有编译器均可通过。 P. S.&#xff1a;测试代码均未展示头文件stdio.h的声明&#xff0c;使用时请自行添加。 文章目录 前言一. 时间复杂度1.1 时间复杂度的概念1.2 时间复杂度如何计算1.3 时间复杂度如何表…

谷歌地球引擎Google Earth Engine下载数字高程模型DEM数据的方法

本文介绍在谷歌地球引擎&#xff08;Google Earth Engine&#xff0c;GEE&#xff09;中&#xff0c;批量下载指定时间与空间范围内的数字高程模型&#xff08;DEM&#xff09;数据的方法。 本文是谷歌地球引擎&#xff08;Google Earth Engine&#xff0c;GEE&#xff09;系列…

c语言中的数组

数组 数组是一种构造类型&#xff0c;是由基本类型构造而成。当我们想用一群变量来描述同一类相同的东西时候&#xff0c;比如100个年龄变量&#xff0c;我们可以这样int age1&#xff1b;int age2&#xff1b;int age3&#xff1b;……int age 100;这样即不方便书写&#xff…

GPU异构内存管理

通过异构内存管理简化 GPU 应用程序开发 异构内存管理 (HMM) 是一项 CUDA 内存管理功能&#xff0c;它扩展了 CUDA 统一内存编程模型的简单性和生产力&#xff0c;以包括具有 PCIe 连接的 NVIDIA GPU 的系统上的系统分配内存。 系统分配内存是指最终由操作系统分配的内存&#…

深入理解MD5:Message Digest Algorithm 5

title: 深入理解MD5&#xff1a;Message Digest Algorithm 5 date: 2024/4/21 18:10:18 updated: 2024/4/21 18:10:18 tags: MD5哈希函数密码学数据完整性碰撞攻击安全性替代算法 导论 MD5的背景和历史 MD5&#xff08;Message Digest Algorithm 5&#xff09;是一种广泛使用…

Axure引用ECharts图表 解决火狐浏览器出错

Axure原型添加Echarts图表&#xff0c;没耐心看文章的可以直接下载示例 Axure中使用ECharts图表示例 1. 打开Axure新建页面 2. 页面添加元件 元件类型随意&#xff0c;矩形、动态面板、热区、图片 甚至段落都可以3. 命名元件 随意命名&#xff0c;单个页面用到多个图表时名…