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

介绍:

        常见的双指针有两种形式,一种是对撞指针(左右指针),一种是快慢指针(前后指针)。需要注意这里的双指针不是 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;}
};

 

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

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

相关文章

qt QListView详解

1、概述 QListView 是 Qt 框架中的一个视图类&#xff0c;用于展示模型中的数据。它基于 QAbstractItemView&#xff0c;支持多种视图模式&#xff0c;如列表视图&#xff08;List View&#xff09;、图标视图&#xff08;Icon View&#xff09;等。QListView 是模型/视图框架…

数据集市是什么?有什么优势?

一、数据集市是什么&#xff1f; 1、数据集市的产生背景&#xff1a; 因为数据仓库的工作范围和成本比较巨大&#xff0c;技术部门必须对所有的以全企业的眼光对待任何一次决策分析&#xff0c;这样就变成了成本高、耗时高的大项目&#xff0c;而且这种集中式的数据处理方式往往…

apk因检测是否使用代理无法抓包绕过方式

最近学习了如何在模拟器上抓取APP的包&#xff0c;APP防恶意行为的措施可分为三类&#xff1a; &#xff08;1&#xff09;反模拟器调试 &#xff08;2&#xff09;反代理 &#xff08;3&#xff09;反证书检验 第一种情况&#xff1a; 有的app检验是否使用系统代理&#xff0c…

TDengine 签约蘑菇物联,改造通用设备工业互联网平台

在当前工业互联网迅猛发展的背景下&#xff0c;企业面临着日益增长的数据处理需求和智能化转型的挑战。通用工业设备的高能耗问题愈发突出&#xff0c;尤其是由这些设备组成的公辅能源车间&#xff0c;亟需更高效的解决方案来提升设备运行效率&#xff0c;降低能源消耗。为此&a…

Python怎么查看编码

在读取中文的情况下&#xff0c;通常会遇到一些编码的问题&#xff0c;但是首先需要了解目前的编码方式是什么&#xff0c;然后再用decode或者encode去编码和解码&#xff0c;下面是使用chardet库来查看编码方式的。 import chardet path "E:/t.csv" #path "E…

sqoop Oracle 导入到hive 日期时间消失

sqoop脚本&#xff1a; sqoop import -D mapred.job.queue.namehighway \ -D mapreduce.map.memory.mb4096 \ -D mapreduce.map.java.opts-Xmx3072m \ --connect "jdbc:oracle:thin://localhost:61521/LZY2" \ --username LZSHARE \ --password 123456 \ --query &q…

[产品管理-59]:项目组合中产品或项目的类型分类

目录 一、概述 1、突破型项目&#xff1a;全新产品&#xff0c;颠覆性产品 2、平台型项目&#xff1a;平台产品 3、衍生型项目&#xff1a;衍生出来的新产品&#xff0c;不同于现有产品&#xff0c;但与现有产品有关联 4、支持性项目&#xff1a;现有产品的改进&#xff0…

第2章立项-2.4如何进行立项评审?

2.4 如何进行立项评审&#xff1f; 2.4 如何进行立项评审&#xff1f;2.4.1 立项沟通不充分会带来的问题2.4.2 让大家都参与到立项评审中发表意见 2.4 如何进行立项评审&#xff1f; 研发项目不仅仅和研发相关&#xff0c;而且跟采购、工艺、制造、销售、市场都相关。如果相关…

基于vue3实现的聊天机器人前端(附代码)

<template><div class"container"><!-- 页面头部 --><header><h1>跟它说说话吧&#xff01;</h1><p>一个活泼的伙伴&#xff0c;为你提供情感支持&#xff01;</p></header><!-- 聊天容器 --><div c…

【安卓13 源码】Input子系统(4)- InputReader 数据处理

1. 多指触控协议 多指触控协议有 2 种&#xff1a; > A类&#xff1a; 处理无关联的接触&#xff1a; 用于直接发送原始数据&#xff1b; > B类&#xff1a; 处理跟踪识别类的接触&#xff1a; 通过事件slot发送相关联的独立接触更新。 B协议可以使用一个ID来标识触点&…

Windows Server 怎么关闭IE增强安全配置(关闭IE弹窗)

首先第一步打开IE浏览器&#xff0c;根据下图所示&#xff0c;访问网页时会弹出警告窗口。 打开【控制面板】图标。查看方式改为小图标&#xff0c;打开【管理工具】 第五步进入【管理工具】页面后&#xff0c;找到并双击【服务器管理器】选项。 第六步在弹出的窗口中&#…

鸿萌数据迁移服务: 企业服务器整机在线热迁移, 实现不停机业务转移

天津鸿萌科贸发展有限公司从事数据安全服务二十余年&#xff0c;致力于为各领域客户提供专业的数据存储、数据恢复、数据备份、数据迁移等解决方案与服务&#xff0c;并针对企业面临的数据安全风险&#xff0c;提供专业的相关数据安全培训。 鸿萌数据迁移业务为众多企业顺利高效…

ChatGPT 新体验:AI 搜索功能与订阅支付指南

就在凌晨&#xff0c;在 ChatGPT 迎来两周岁生日之际&#xff0c;OpenAI 重磅发布了 ChatGPT 的全新人工智能搜索体验。 期待已久的时刻终于到来&#xff0c; ChatGPT 正式转型成为一款革命性的 AI 搜索引擎&#xff01; 先来看看 ChatGPT 搜索&#xff1a;这次不是简单的加个…

[JAVAEE] 面试题(四) - 多线程下使用ArrayList涉及到的线程安全问题及解决

目录 一. 多线程下使用ArrayList 1.1. 自行判断加锁 1.2 使用Collections.synchronizedList()套壳加锁 1.3 CopyOnWriteArrayList类 二. 总结 一. 多线程下使用ArrayList 多线程下使用ArrayList会涉及到线程安全问题, 例如: public static void main(String[] args) thro…

canal1.1.7使用canal-adapter进行mysql同步数据

重要的事情说前面&#xff0c;canal1.1.8需要jdk11以上&#xff0c;大家自行选择&#xff0c;我这由于项目原因只能使用1.1.7兼容版的 文章参考地址&#xff1a; canal 使用详解_canal使用-CSDN博客 使用canal.deployer-1.1.7和canal.adapter-1.1.7实现mysql数据同步_mysql更…

DevExpress中文教程 - 如何使用AI模型检查HTML编辑中的语法?

DevExpress .NET MAUI多平台应用UI组件库提供了用于Android和iOS移动开发的高性能UI组件&#xff0c;该组件库包括数据网格、图表、调度程序、数据编辑器、CollectionView和选项卡组件等。 目前许多开发人员正在寻找多种方法将AI添加到解决方案中&#xff08;这通常比想象的要…

【推荐】iptables学习宝典

链接&#xff1a; IPtables-朱双印博客 学习iptables的抗鼎之作&#xff0c;推荐。

二维数组和数组指针数组的关系

在深入理解指针end中&#xff0c;我在最后写了一长段代码 #include<stdio.h> void test1(int arr[][5], int x, int y) //void test1(int(*p)[5], int x, int y) {for (int i 0; i < x; i){for (int j 0; j < y; j){//printf("%d ", *(*(p i) j));p…

vue+websocket实现即时聊天平台

目录 1 什么是websocket 2 实现步骤 2.1 导入依赖 2.2 编写代码 1 什么是websocket WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议。它主要用于在客户端和服务器之间建立持久的连接&#xff0c;允许实时数据交换。WebSocket 的设计目的是为了提高 Web 应用程序的…

【D3.js in Action 3 精译_038】4.2 D3 折线图的绘制方法及曲线插值处理

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 第一部分 D3.js 基础知识 第一章 D3.js 简介&#xff08;已完结&#xff09; 1.1 何为 D3.js&#xff1f;1.2 D3 生态系统——入门须知1.3 数据可视化最佳实践&#xff08;上&#xff09;1.3 数据可…