【算法】【算法杂谈】将路径数组变为统计数组(单数组的调整与转换)

news/2024/10/28 18:30:31/

目录

  • 前言
  • 问题介绍
  • 解决方案
  • 代码编写
    • java语言版本
    • c语言版本
    • c++语言版本
  • 思考感悟
  • 写在最后

前言

当前所有算法都使用测试用例运行过,但是不保证100%的测试用例,如果存在问题务必联系批评指正~

在此感谢左大神让我对算法有了新的感悟认识!

问题介绍

原问题
给定一个数组arr,其中该arr[i] = j表示区域i直接连接的区域是区域j,如下图所示:
在这里插入图片描述

其中arr[3] = 1,arr[6] = 1,arr[2] = 1,其中arr[1] = 0代表 1是首都
现在要求:
1、不使用新数组
2、时间复杂度在O(n)
3、将arr[i] = j代表的意义转换成 到1的距离为i的节点数有几个,如上图 arr[1] = 3
已知:路径图不会成环并且都最终通往首都

解决方案

原问题
1、不是用新数组,那么就只能使用arr这个入参提供的空间
2、时间复杂度控制在O(n)也就是只能最多使用一层循环,避免循环嵌套
3、解决该问题:arr需要经历2个周期转换,第一个周期转换为:将arr[i] = j 代表 i指向j 转换为 arr[i] = j 代表 i到首都的距离为j
第二个周期转换为: arr[i] = j 代表 i到首都的距离为j 转换为 arr[i] = j 代表 到首都的距离为i的节点数有几个

其中 转换周期1的方法:
从arr[0]开始遍历,取出arr[0],代表下一个节点j,arr[0]是起点,所以先让arr[0]为0,之后看arr[j],将arr[j]的下一个节点暂存后,再将arr[j]设置为0,代表j节点的上一个节点是节点0,如此往复一定会查到首都节点,此时将首都设置为0,开始往回走,每回去一个节点该节点的数值更新为上一个节点-1,直到回到起点为止。形象一点如下:
去过程:原2节点现在指向节点0,当遇到节点1时发现1是首都,次时往回走,看回过程
在这里插入图片描述
回过程:当从1回到2的时候,arr[1]为0,所以arr[2] = arr[1] - 1,最终这条路径变成负数路径
在这里插入图片描述
转换周期二:距离数组转统计数组
到这里arr = [-2, 0, -1, -1, -2, -2, -1]
现在开始转换成统计数组,首先不能使用多余的空间,所以我们还是需要在该数组上做转换
arr[0] = -2 说明0节点到首都的距离为2,此时判断arr[2] 是否大于0,如果小于0,说明当前位置并没有被统计过,所以暂存,改为0,然后+1。如果大于0 ,说明已经统计过,此时直接+1即可,被统计过的arr[0]此时已经统计过,但是暂时没有人距离为0(首都没遍历到),所以arr[0]变为0
一轮交换转换之后统计数组即可出现。

代码编写

java语言版本

原问题:

    /*** 二轮测试:从arr[i] = j 代表数值i的结点指向j含义转化为 arr[i]代表到首都的距离为i的结点数量* 时间复杂度O(N),空间复杂度O(1)* @param arr* @return*/public static int[] path2NumsCp1(int[] arr) {if (arr == null) {return null;}else if (arr.length == 0) {return new int[]{0};}// 在原arr的基础上直接计算每一个点到首都的距离int cap = convertPath2Length(arr);arr = convertLength2Nums(arr, cap);return arr;}/*** 将代表path的arr转换成距离首都的距离(用负数表达)* @param arr*/private static int convertPath2Length(int[] arr) {// 记录一下首都的indexint capIndex = -1;for (int i = 0; i < arr.length; i++) {// 首先判断i是否已经计算过if (arr[i] < 0 || i == capIndex) {continue;}// 记录一下起点,标记起点为-1int cur = i;// next代表下一跳int next = arr[cur];arr[cur] = -1;while (arr[next] >= 0 && next != capIndex) {// 先判断是否是首都if (arr[next] == next) {// 首都直接返回,并且首都到首都的距离是0arr[next] = 0;capIndex = next;break;}// 需要跳跃int temValue = arr[next];// 记录回去的路arr[next] = cur;cur = next;next = temValue;}// 到这里next可能代表首都,也可能arr[next]已经计算过,cur代表着上一个节点的index// 所以从这里开始往回更新距离了while (arr[cur] != -1) {// 暂存一下下一个的位置int tem = arr[cur];// 上一个距离-1arr[cur] = arr[next] - 1;// 更新游标next = cur;cur = tem;}// 最后再更新一下arr[cur]arr[cur] = arr[next] - 1;}return capIndex;}/*** 从长度到统计数组的转换* @param arr* @return*/private static int[] convertLength2Nums(int[] arr, int cap) {for (int i = 0; i < arr.length; i ++) {if (arr[i] >= 0 && i != cap) {// 大于0的说明统计过了continue;}// 到这里的都要进行统计// 首先拿出来arr[i],取相反数才是真的lenint cur = arr[i];arr[i] = 0;boolean flag = false;while (cur <= 0) {// 当前位置距离首都的距离int len = -cur;if (arr[len] >= 0) {arr[len] ++;flag = true;break;}else {// 说明了需要循环替换int next = arr[len];// 替换arr[len] = 0;arr[len] ++;// 下一个游标cur = next;}}if (!flag) {arr[cur] ++;}}return arr;}public static void main(String[] args) {CommonUtils.printArr(path2NumsCp1(new int[]{9,1,4,9,0,4,8,9,0,1}));}

c语言版本

正在学习中

c++语言版本

正在学习中

思考感悟

这道题很考验大脑的寄存器,,,
首先记录数组的变化非常绕,不让使用多余空间时,就要在一个数组上面进行交换和更新,其中需要注意的两个点:
1、用负数处理非常巧妙的将数组当做两个数组来用
2、0这个位置和0这个值非常特殊,因为其代表首都也代表距离,容易搞混,所以需要有一个变量专门标注首都以备后面判断使用。
总之需要好好体会数组的转换,并且如果限制了空间,可以使用正负数的方式将数组进行界线分割,其次,回溯的过程也很巧妙,用于路径数组变成长度数组时使用。

写在最后

方案和代码仅提供学习和思考使用,切勿随意滥用!如有错误和不合理的地方,务必批评指正~
如果需要git源码可邮件给2260755767@qq.com
再次感谢左大神对我算法的指点迷津!


http://www.ppmy.cn/news/68876.html

相关文章

Python 爬虫(一):爬虫伪装

1 简介 对于一些有一定规模或盈利性质比较强的网站&#xff0c;几乎都会做一些防爬措施&#xff0c;防爬措施一般来说有两种&#xff1a;一种是做身份验证&#xff0c;直接把虫子挡在了门口&#xff0c;另一种是在网站设置各种反爬机制&#xff0c;让虫子知难而返。 2 伪装策…

从零开始Vue3+Element Plus后台管理系统(六)——状态管理Pinia和持久化

Pinia 官网&#xff1a;https://pinia.vuejs.org/zh/ Pinia 是 Vue 的专属状态管理库&#xff0c;相比Vuex更好用&#xff0c;优点不多了说官网有&#xff0c;用起来最重要&#xff01; 在应用的根部注入创建的 pinia // main.ts import { createApp } from vue import { c…

代码量原地缩减50%,这个Java工具类库太香了

Guava是google公司开发的一款Java类库扩展工具包&#xff0c;内含了丰富的API&#xff0c;涵盖了集合、缓存、并发、I/O等多个方面。使用这些API一方面可以简化我们代码&#xff0c;使代码更为优雅&#xff0c;另一方面它补充了很多jdk中没有的功能&#xff0c;能让我们开发中更…

构造函数(包括默认构造函数) ,析构函数的使用与特性

文章目录 一、构造函数二、默认构造函数&#xff08;也是构造函数&#xff09;默认构造函数的种类&#xff1a;1.无参类型2.全缺省类型3.编译器自动生成的4.汇总 三、析构函数 一、构造函数 构造函数是一个特殊的成员函数&#xff0c;名字与类名相同,创建类类型对象时由编译器自…

汇编五、伪指令与汇编程序结构

1、伪指令 1.1、概念 (1)伪指令是用于对汇编过程进行控制的指令&#xff0c;该类指令并不是可执行指令&#xff0c;没有对应机器码&#xff0c;只用于汇编过程中为汇编程序提供汇编信息&#xff0c;帮助编译器编译。 1.2、ASM51提供的伪指令 伪指令分为如下几类。 1.2.1、…

SpringSecurity 一文彻底掌握

文章目录 前言一、SpringSecurity Web方案&#x1f353;Test Controller 测试请求控制器&#x1f923;SpringSecurity 基本原理&#x1f30d;代码底层流程&#xff1a;重点看三个过滤器FilterSecurityInterceptor 方法级的权限过滤器ExceptionTranslationFilter 异常过滤器User…

golang web学习随便记6-模板引擎

以下代码是几乎最简单的一个模板&#xff0c;{{ . }} 表示执行模板时将嵌入的数据 <!DOCTYPE html> <html><head><meta http-equiv"Content-Type" content"text/html; charsetutf-8"><title>Go Web 编程</title> <…

C语言200行代码实现简易三子棋

前言 三子棋应该是是我们最早接触到的棋类游戏&#xff0c;用C语言实现三子棋对初学者来说是一种不错的锻炼 编写三子棋只需要用到数组、函数和生成随机数的知识&#xff0c;所以比较适合成为编程学习者编写的第一个小游戏。 一.代码实现 第一部分是源码复制就可以使用&…