算法每日练 -- 双指针篇(持续更新中)

介绍:

        常见的双指针有两种形式,一种是对撞指针(左右指针),一种是快慢指针(前后指针)。需要注意这里的双指针不是 int* 之类的类型指针,而是使用数组下标模拟地址来进行遍历的方式。

对撞指针:

  • 对撞指针一般用于顺序结构中。
  • 对撞指针从两端向中间移动,一个指针从最左端开始,另一个从最又端开始,然后逐渐往中间逼近。
  • 对撞指针的终止条件一般是两个指针相遇或者错开(也可能是在循环内部找到结果直接跳出循环),也就是left == right(两个指针指向同一个位置)或者left > right(两个指针错开)。

 快慢指针:

        快慢指针又称为龟兔赛跑算法,其基本思想就是使用两个移动速度不同的指针在数组或链表等序列结构上移动。这种方法对于处理环形链表或数组非常有用。其实不单单是环形链表或者是数组,如果我们要研究的问题出现循环往复的情况时,都可以考虑使用快慢指针的思想。

练习

1、移动零

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

请注意 ,必须在不复制数组的情况下原地对数组进行操作。

提示:

  • 1 <= nums.length <= 10^4
  • -2^31 <= nums[i] <= 2^31 - 1

 算法原理:

        本道题采用的是快排思想:数组分块的方法,数组分块是一种常见题型,主要是根据一种划分方式,将数组的内容分成左右两部分。这种类型的题目一般使用双指针来解决。

思路:

        在本题中,我们可以用一个 cur 来扫描整个数组,另一个 dest 来记录非零元素序列的最后一个位置。根据 cur 在扫描过程中,遇到的不同情况,分类处理,实现数组划分。在 cur 遍历期间,使[ 0, dest ]的元素全部都是非零元素,[ dest+1, cur-1 ]的元素全为0。

  1. 初始化 cur = 0(用来遍历数组),dest = -1(指向非零元素的最后一个位置)。因为刚开始我们不知道最后一个非零元素在什么位置,因此初始化为-1。
  2. cur 依次往后遍历元素,遍历到的元素会分为两种情况:
  • 遇到的元素值为0,此时 cur 直接 ++。因为我们的目标是让[ dest+1, cur-1 ]内的元素全部为0,因此当 cur 遇到 0 的时候,直接 ++,就可以让 0 在 cur-1 的位置上,从而保持在[ dest+1, cur-1 ]内。
  • 遇到的元素值不为0,此时 dest++,并且交换 cur 位置和 dest 位置的元素,之后让cur++,扫描下一个元素。

        因为 dest 指向的位置是非零元素区间的最后一个位置,如果扫描到一个新的非零元素,那么它的位置应该在 dest+1 的位置上,因此 dest 先++;

        dest++ 之后,指向的元素就是 0 元素(因为非零元素区间末尾最后一个元素就是0),因此可以交换到cur所处的位置上,实现[ 0, dest ]的元素全部都是非零元素,[ dest+1, cur-1 ]的元素都是0。

代码演示:

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        int cur = -1;
        int dest = 0;
        for(size_t dest = 0; dest<nums.size();dest++)
        {
            if(nums[dest] != 0)
            {
                cur++;
                std::swap(nums[cur], nums[dest]);
            }
        }
    }
};

2、复写零

        给你一个长度固定的整数数组 arr ,请你将该数组中出现的每个零都复写一遍,并将其余的元素向右平移。注意:请不要在超过该数组长度的位置写入元素。请对输入的数组 就地 进行上述修改,不要从函数返回任何东西。

提示:

  • 1 <= arr.length <= 10^4
  • 0 <= arr[i] <= 9

解题思路:(原地复写-双指针)

        如果从前向后进行原地复写操作的话,由于0的出现会复写两次,导致没有复写的数会被覆盖掉。因此我们选择从后往前的复写策略。

        而从后往前复写需要我们找到最后一个复写的元素,因此我们的思路可分为两步:

  1.  先找到最后一个复写的元素。
  2.  然后从后向前进行复写操作。

算法流程:

  • 初始化两个指针cur = 0, dest = 0;
  • 找到最后一个复写的元素:当 cur < n 时循环,判断cur位置的元素,如果为0,dest往后移动两位,否则移动一位。判断dest是否已经到结束位置,如果结束就break终止循环;如果没有结束,cur++,继续判断。
  • 判断dest是否越界:如果越界到n位置,执行(n-1位置元素值修改为0,cur向前移动一步,dest位置向前移动两步)。
  • 从cur位置开始往前遍历原数组,因此还原出复写后的结果数组:判断cur位置的值(如果是0,dest以及dest-1位置元素值修改为0,dest-=2;如果是非0,dest位置修改成非0值,dest-=1),cur--,复写下一个位置。

代码演示:

class Solution
{
public:
	void duplicateZeros(vector<int>& arr)
	{
		// 1. 先找到最后⼀个数
		int cur = 0, dest = -1, n = arr.size();
		while (cur < n)
		{
			if (arr[cur]) dest++;
			else dest += 2;
			if (dest >= n - 1) break;
			cur++;
		}
		// 2. 处理⼀下边界情况
		if (dest == n)
		{
			arr[n - 1] = 0;
			cur--; dest -= 2;
		}
		// 3. 从后向前完成复写操作
		while (cur >= 0)
		{
			if (arr[cur]) arr[dest--] = arr[cur--];
			else
			{
				arr[dest--] = 0;
				arr[dest--] = 0;
				cur--;
			}
		}
	}
};

3、快乐数

编写一个算法来判断一个数 n 是不是快乐数。

「快乐数」 定义为:

  • 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
  • 然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
  • 如果这个过程 结果为 1,那么这个数就是快乐数。

如果 n 是 快乐数 就返回 true ;不是,则返回 false 。

提示:

  • 1 <= n <= 2^31 - 1

题目分析:

        为了方便叙述,我将 “对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和”这一操作记为 x 操作。题目告诉我们,当我们不断重复 x 操作时,计算会出现两种死循环方式:

  • 一种是一直在1中死循环。
  • 另一种是在历史计算的数据中死循环,但始终不会变为1。

        由于上述的两种情况只会出现一种,因此,我们只要能确定循环是在第一种情况还是第二种情况中进行,就可以得到结果。

证明:

  •   我们知道输入的元素最大值为2 ^ 31 - 1 = 2147483647,我们取更大的9999999999,则经过一次变化后的最大值为 9 ^ 2 * 10 = 810,也就是说变化的区间在[ 1, 810 ]之间。
  • 根据鸽巢原理,一个数变化811次之后,必然会形成一个循环;
  • 因此变化的过程最终会走到一个圈里,因此本题可以使用快慢指针解决。

算法思路:

        重复执行 x 操作后,数据会陷入到一个循环中。根据快慢指针的特性,在一个圈中,快指针总是会追上慢指针,也就是说,两个指针总是会在一个位置上相遇,如果相遇位置是1,那么这个数一定是快乐数;如果相遇位置不为1,那么就不是快乐数。

补充知识:如何求一个数n每个位置上的数字的平方和

  • 把数n的每一位提取出来:循环迭代(int t = n % 10;  n/=10取掉原来的个位),直到n变为0。
  • 提取每一位的时候,用一个变量tmp来记录这一位的平方与之前提取位数的平方和:tmp = tmp + t * t;

代码演示:

class Solution
{
public:
	int bitSum(int n) // 返回 n 这个数每⼀位上的平⽅和
	{
		int sum = 0;
		while (n)
		{
			int t = n % 10;
			sum += t * t;
			n /= 10;
		}
		return sum;
	}
	bool isHappy(int n)
	{
		int slow = n, fast = bitSum(n);
		while (slow != fast)
		{
			slow = bitSum(slow);
			fast = bitSum(bitSum(fast));
		}
		return slow == 1;
	}
};

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/911644.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python学习从0到1 day26 第三阶段 Spark ①

要学会 剥落旧痂 然后 循此新生 —— 24.11.8 一、Spark是什么 定义&#xff1a; Apache Spark 是用于大规模数据处理的统一分析引擎 简单来说&#xff0c;Spark是一款分布式的计算框架&#xff0c;用于调度成百上千的服务器集群&#xff0c;计算TB、PB乃至EB级别的海量数据…

jenkins使用slave节点进行node打包报错问题处理

jenkins使用slave打包报错 Also: hudson.remoting.Channel$CallSiteStackTrace: Remote call to 21.136 解决方法&#xff1a; 重启从节点 选择断开连接再重新连

python登录功能实现

一.用python实现基本的登录功能 #-----------------1.基本登录功能------------------- nameinput("qq账号&#xff1a;") if name"jc":passwdinput("密码&#xff1a;")if passwd"123456":print("登录成功")else:print(&q…

三菱MR-J4伺服绝对位置检测系统

发生[AL.25 绝对位置丢失]或[AL.E3 绝对位置计数器警告]时&#xff0c;必须再次进行原点设定。否则可能会因此发生预料之外的动作。 概要 常规运行时&#xff0c;编码器由检测1转内位置的编码器和检测转数的旋转累计计数器构成。 绝对位置检测系统与伺服系统控制器电源…

mac 中python 安装mysqlclient 出现 ld: library ‘ssl‘ not found错误

1. 出现报错 2. 获取openssl位置 brew info openssl 3. 配置环境变量&#xff08;我的是在~/.bash.profile&#xff09; export LDFLAGS"-L/opt/homebrew/Cellar/openssl3/3.4.0/lib" export CPPFLAGS"-I/opt/homebrew/Cellar/openssl3/…

OceanBase详解及如何通过MySQL的lib库进行连接

OceanBase详解及如何通过MySQL的lib库进行连接 一、引言二、OceanBase概述1. 起源与发展2. 核心技术特点3. 应用场景三、OceanBase架构解析1. 系统架构2. 存储引擎3. 分布式架构四、如何使用MySQL的lib库连接OceanBase1. 前提条件2. 安装MySQL Connector/C3. 编写连接代码4. 编…

aspose如何获取PPT放映页“切换”的“持续时间”值

aspose如何获取PPT放映页“切换”的“持续时间”值 项目场景问题描述问题1&#xff1a;从官方文档和资料查阅发现并没有对切换的持续时间进行处理的方法问题2&#xff1a;aspose的依赖包中&#xff0c;所有的关键对象都进行了混淆处理 解决方案1、找到ppt切换的持续时间对应的混…

SpringBoot开发——SpringBoot3.3 中实现多端口监听

文章目录 1、项目环境与依赖配置2、配置多端口监听3、编写配置类实现多端口监听4、为每个端口创建独立的配置类4.1 8081 端口配置类4.2 8082 端口配置类 5、控制器类定义5.1 8080 端口的控制器&#xff08;保持原有配置&#xff09;5.2 8081 端口的控制器5.3 8082 端口的控制器…

大数据湖项目建设方案(100页WORD)

文档介绍&#xff1a; 随着信息技术的飞速发展&#xff0c;数据量呈现出爆炸性增长的趋势。企业、政府机构及科研单位在运营过程中积累了大量的数据资源&#xff0c;这些数据中蕴含着巨大的价值&#xff0c;但如何高效地存储、处理和分析这些数据&#xff0c;成为了一个亟待解…

【SpringBoot】SpringCloud一些常用依赖

SpringChoudAlibaba springcloud版本对应关系 可以按照hm这样写 ----以下是我自己使用过的&#xff0c;比较乱 parent <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><versi…

Webserver(5.3)线程池实现

目录 线程池locker.hthreadpool.h 线程池 相比于动态地创建子线程&#xff0c;选择一个已经存在的子线程的代价显然要小得多。至于主线程选择哪个子线程来为新任务服务&#xff0c;有多种方式&#xff1a; 主线程使用某种算法来主动选择子线程。最简单、最常用的算法是随机算…

解决:使用EasyExcel导入Excel模板时出现数据导入不进去的问题

解决&#xff1a;使用EasyExcel导入Excel模板时出现数据导入不进去的问题 在Java中&#xff0c;当我们用EasyExcel导入Excel时&#xff0c;可能会出现数据导入不进去的问题。例如&#xff1a; 这种异常等。 问题原因1&#xff1a;这个1代表从第几行开始&#xff0c;你的exce…

可视化建模与UML《类图实验报告》

史铁生&#xff1a; 余华和莫言扛着我上火车&#xff0c; 推着走打雪仗&#xff0c; 还带我偷西瓜&#xff0c; 被人发现后他们拔腿就跑&#xff0c; 却忘了我还在西瓜地里。 一、实验目的&#xff1a; 1、熟悉类图的构件事物。 2、熟悉类之间的泛化、依赖、聚合和组合关系…

python(自用查看版)

目录 1.注意事项 1.1 python的除法不是整除&#xff0c;得到的是浮点数 1.2算术符号基于数学的算术优先级。具体可自行查看。 1.3注释 1.4缩进 1.5换行 1.6常见关键字 1.7续行符 1.8报错 1.9赋值 1.10比较运算符 2.常量和表达式 3.变量 4.数据类型 4.1整型int …

TMDOG的Gin学习笔记_02——Gin集成支付宝支付沙箱环境

TMDOG的Gin学习笔记_02——Gin集成支付宝支付沙箱环境 博客地址&#xff1a;TMDOG的博客 作者自述&#xff1a; 最近忙着整理自己的项目代码&#xff0c;终于有时间更新一下博客。这次的内容是关于如何在Gin框架下集成支付宝的支付沙箱环境&#xff0c;具体包括如何初始化支付…

cmake could not find a package configuration file provided by “Microsoft.GSL“

通过查找 dir C:\vcpkg\installed\x64-windows /s /b | findstr Microsoft.GSL vspkg安装 git clone https://github.com/microsoft/vcpkg.git cd vcpkg .\bootstrap-vcpkg.bat .\vcpkg --version

cocos creator 3.8.3物理组件分组的坑

坑&#xff0c;坑的不行的大坑 group用的二进制的左移获取十进制的数值 目前是这样判断的&#xff0c;也不知道对不对&#xff0c;什么get、set Group没找到

【AI写作宝-注册安全分析报告-无验证方式导致安全隐患】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 1. 暴力破解密码&#xff0c;造成用户信息泄露 2. 短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉 3. 带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造…

计算机毕业设计Python流量检测可视化 DDos攻击流量检测与可视化分析 SDN web渗透测试系统 网络安全 信息安全 大数据毕业设计

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

2024.11最新Hexo+GitHub搭建个人博客

2024.11最新HexoGitHub搭建个人博客 一、Hexo介绍 Hexo 是一个快速、简洁且高效的博客框架&#xff0c;有丰富的主题和插件可供使用。 Hexo 使用 Markdown&#xff08;或其他标记语言&#xff09;解析文章&#xff0c;在几秒内&#xff0c;即可利用靓丽的主题生成静态网页。这…