算法:双指针题目练习

文章目录

  • 算法:双指针
    • 移动零
    • 复写零
    • 快乐数
    • 盛最多水的容器
    • 有效三角形的个数
    • 查找总价格为目标值的两个商品
    • 三数之和
    • 四数之和
  • 总结


算法:双指针

移动零

在这里插入图片描述

定义两个指针,slow和fast.用这两个指针把整个数组分成三块.
[0,slow]为非零元素,[slow+1,fast-1]为0元素,[fast,num.length]为未处理的元素.
初始情况下slow=-1,fast=0,因为此时还没有对数组进行处理.
用fast遍历整个数组:

  • 当fast指向的元素不为0时,此时让slow++,并交换fast与slow所指向的元素.交换完毕后fast++,继续向后处理元素.
  • 当fast指向的元素为0时,直接fast++.
class Solution {public void moveZeroes(int[] nums) {int slow = -1;int fast = 0;while(fast < nums.length) {if(nums[fast] != 0) {slow++;int tmp = nums[fast];nums[fast] = nums[slow];nums[slow] = tmp;}fast++;}}
}

复写零

在这里插入图片描述
这道题目如果没有要求空间复杂度为O(1)的话,那确实是简单题.直接申请一块数组,遍历,然后放进去,最后把数组的引用交换一下就行了.
但是实际上,这道题还是有点难度的,需要考虑的细节情况很多,自己上手写写就知道了.

解决这道题,最关键的就是找到数组中最后一个要被修改的数,我们可以使用双指针来找.

  • 定义两个指针slow和fast并初始化为0.使用slow向后遍历数组,当slow遇到0时,fast向后移动两步,slow向后移动一步.当slow指向的元素不为零时,fast和slow都向后移动一步.重复上述过程,直到fast大于数组长度.
  • 循环结束后,此时fast和slow多移动了一步,所以要减掉.在这里还需要考虑fast向后移动了两步的情况(slow指向了0元素,而此时fast已经位于数组的最后一个元素了),判断一下fast是否越界,如果越界,修改fast的指向,让fast指向数组的最后一个元素,因为这时slow指向的元素肯定是0,所以我们可以将数组的最后一位修改成0,之后让slow和fast向前移动一位.
  • 当程序运行到这里时,所有的特殊情况我们就都处理完了.接下来就是简单的移动和赋值了,这里就不再赘述了~
class Solution {public void duplicateZeros(int[] arr) {int fast = 0;int slow = 0;while(fast < arr.length) {if(arr[slow] == 0) fast++;fast++;slow++;}--slow;--fast;if(fast == arr.length) {fast = arr.length-1;arr[fast--] = arr[slow--];}while(slow >= 0) {if(arr[slow] == 0) {arr[fast--] = 0;}arr[fast--] = arr[slow--];}}
}

也可以看看宫水三叶大佬写的代码,大佬在力扣上写了题解,可以去看看.

class Solution {public void duplicateZeros(int[] arr) {int n = arr.length, i = 0, j = 0;while (j < n) {if (arr[i] == 0) j++;i++; j++;}i--; j--;while (i >= 0) {if (j < n) arr[j] = arr[i];if (arr[i] == 0 && --j >= 0) arr[j] = 0;i--; j--;}}
}作者:宫水三叶
链接:https://leetcode.cn/problems/duplicate-zeros/solutions/1607062/by-ac_oier-zivq/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

快乐数

在这里插入图片描述
这道题条件给的很全,如果题目没给"重复这个过程直到这个数变为1,也可能是无限循环但始终变不到1"这个条件.
那么就需要好好的想一想了,上面的条件是可以证明出来的.运用到了数学上的思想"鸽巢原理",这个放到最后再写吧,先写题解~

  • "将一个正整数替换为它每个位置上的数字的平方和"这个我们可以把它写成一个方法,方便调用,写法也很简单,这里就不写了.
  • 这道题最核心的地方,在于你怎么判断它无限循环永远到不了1,我们可以使用双指针来帮助我们判断,如果你之前做过环形链表,那么就很容易想到。
  • 定义两个指针slow和fast,并初始化为n,首先让fast先走一步(做一次替换,因为之后的循环内需要判断fast和slow是否相等).
  • 紧接着进入循环,循环条件为fast != 1 || fun(fast) != 1,因为之后fast要一次向后走两步,所以fast的当前值和fast的下一个值都要判断.循环内如果fast与slow相等,说明有环了,直接return false.如果不相等,slow向后走一步,fast向后走两步.
  • 如果fast走着走着跳出了循环(表示fast可以被替换成1),此时直接return true.
class Solution {public static int fun(int n) {int sum = 0;while(n > 0) {int tmp = n%10;sum += tmp*tmp;n/=10;}return sum;}public boolean isHappy(int n) {int slow = n;int fast = fun(n);while(fast != 1 || fun(fast) != 1) {if(fast == slow) {return false;}fast = fun(fun(fast));slow = fun(slow);}return true;}
}

鸽巢原理讲解:
以下是不太严谨的证明~
举个例子,比如说n = 9999999999.
n经过一次变换之后n = 9^2*10 = 810.也就是说变换的区间就在[1,810]这个范围内.
如果我们将n变换810次,最差的情况就是[1,810]内所有的数都出现了一次.如果n在此基础上再次变换,那么这个数肯定就会落在这个区间内,此时就形成了环路.

盛最多水的容器

在这里插入图片描述
写这道题时,我们就要分析分析了.
设盛水容量为v,底部变长为s,高为h.左指针为left指向最左边,右指针为right指向最右边,两个指针向对方移动.
根据题意,我们可得:

  • v = s * h;
  • s = right - left;
  • h = min(left,right);
    当我们移动指向的最高的线的指针时,有以下两种情况:
  • s 减小(两指针之间的距离变短了), 移动后指针的指向的线高于移动前的线,h 不变(因为盛水多少取决于最低的线),那么 v 减小
  • s 减小,移动后指针的指向的线低于移动前的线,h 不变或减小(因为盛水多少取决于最低的线,移动后的指针指向的线可能比另一个指针指向的线短,也可能在移动后仍然比另一个指针指向的线长),那么 v 减小.
    可以看到,当我们移动指向的最高的线的指针时,v并不会增大.

当我们移动指向的最低的线的指针时,有以下两种情况:

  • s 减小(两指针之间的距离变短了), 移动后指针的指向的线高于移动前的线,h 增大(因为盛水多少取决于最低的线),那么 v 的变化不能确定.
  • s 减小,移动后指针的指向的线低于移动前的线,h 减小(因为盛水多少取决于最低的线),那么 v 减小.

在这里插入图片描述

综合上述分析,我们可以得到,只有在移动指向的最低的线的指针,并且移动后,指针的指向的线,高于移动前的线时,v才有可能变大.

有了以上的分析,代码就很好写啦~

class Solution {public int maxArea(int[] height) {int left = 0;int right = height.length - 1;int v = 0;while(left < right) {int s = Math.min(height[left],height[right]);int tmp = s*(right-left);if(tmp > v) {v = tmp; }if(height[left] == s) {left++;} else {right--;}}return v;}
}

有效三角形的个数

在这里插入图片描述
先给数组排个序.
从后向前固定一个i值(循环1),当left小于right时(循环2),让left和right指向的数相加,判断是否大于i指向的数.

  • 大于i,让sum+=right-left; right–;
  • 小于或等于i,让left向右移.
    在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

class Solution {public int triangleNumber(int[] nums) {int left =0;int right = 0;Arrays.sort(nums);int sum = 0;for(int i=nums.length - 1;i >= 2;i--) {right = i-1;left = 0;while(left < right) {if(nums[left]+nums[right] > nums[i]) {sum += right -left;right--;} else {left++;}}}return sum;}
}

查找总价格为目标值的两个商品

在这里插入图片描述
感觉没什么可说的~

class Solution {public int[] twoSum(int[] price, int target) {int left = 0;int right = price.length - 1;while(left < right) {if(price[left] + price[right] > target) {right--;} else if(price[left] + price[right] < target) {left++;} else{return new int[]{price[left],price[right]};}}return null;}
}

三数之和

在这里插入图片描述
自己写的代码,在最开始new ArrayList<List<Integer>>()的时候不会new,忘了.(后来发现不用这么麻烦写e)
还有我最开始是在最内层循环里面使用了list.contains(list2),然后超时了qwq.
后来去看题解,发现还能这样去重!
改了改就成现在这样了,虽然效率还是很低就是了.

class Solution {public List<List<Integer>> threeSum(int[] nums) {List<List<Integer>> list = new ArrayList<List<Integer>>();Arrays.sort(nums);int left = 0;int right = nums.length - 1;for(int i = nums.length-1; i >= 2; i--) {if(i < nums.length - 1 && nums[i] == nums[i+1]) continue;right = i - 1;left = 0;while(left < right) {if(right != i-1 && right >= left +1 && nums[right] == nums[right+1]) {right--;continue;}int tmp = nums[left] + nums[right] + nums[i];if(tmp > 0) {right--;} else if(tmp < 0) {left++;} else {ArrayList<Integer> list2 = new ArrayList<>();list2.add(nums[left]); list2.add(nums[right]); list2.add(nums[i]); list.add(list2);right--;}}}return list;}
}

看了讲解后:

  1. List<List<Integer>> list = new ArrayList<List<Integer>>();其实可以写成
    List<List<Integer>> list = new ArrayList<>(); < >里面可以啥都不写.
  2. 当num[i]<0时,此时最大的数都小于0了,肯定三数之和就不可能等于0了,直接跳过.
  3. 第一次见还能这样写: list.add(new ArrayList(Arrays.asList(nums[left],nums[right],nums[i])));
  4. 当三数和为0时,left和right可以都移动一步(我写的只有right移动一步).
  5. 当三数和为0并且left和right移动后,此时就可以开始去重了,不必在内层循环用if和continue去重(这样多判断了right != i-1),而且我只对right去重了,left还是要经过整个循环emmm,虽然也达到了去重的效果(歪打正着,我当时还在想为啥if和continue不能换成while?被自己蠢哭了qwq).其实没必要,直接同时去重就OK了.

虽然自己第一次写的代码能通过,但是可优化的地方还有很多.没有大佬写的代码优雅~

以下是改后的代码:

class Solution {public List<List<Integer>> threeSum(int[] nums) {List<List<Integer>> list = new ArrayList<>();Arrays.sort(nums);int left = 0;int right = nums.length - 1;for (int i = nums.length - 1; i >= 2; i--) {if (nums[i] < 0)break;if (i < nums.length - 1 && nums[i] == nums[i + 1])continue;right = i - 1;left = 0;while (left < right) {int tmp = nums[left] + nums[right] + nums[i];if (tmp > 0) {right--;} else if (tmp < 0) {left++;} else {list.add(new ArrayList<Integer>(Arrays.asList(nums[left],nums[right],nums[i])));right--;left++;while (right > left && nums[right] == nums[right + 1]) {right--;}while (right > left && nums[left] == nums[left - 1]) {left++;}}}}return list;}
}

四数之和

在这里插入图片描述
自己写出来的,中间改了好几次,终于过了~
跟上一题很像.
自己写的代码:

class Solution {public List<List<Integer>> fourSum(int[] nums, int target) {List<List<Integer>> list = new ArrayList<>();Arrays.sort(nums);if (nums[nums.length - 1] < 0 && target > 0) {return list;}for (int i = 0; i < nums.length - 3; i++) {if (nums[i] > 0 && target <= 0) {break;}if (i != 0) {while (i < nums.length - 3 && nums[i] == nums[i - 1]) {i++;}}for (int j = i + 1; j < nums.length - 2; j++) {if (j != i + 1) {while (j < nums.length - 2 && nums[j] == nums[j - 1]) {j++;}}int left = j + 1;int right = nums.length - 1;while (left < right) {long sum = nums[left] + nums[right] + nums[i] + nums[j];if (sum > target) {right--;} else if (sum < target) {left++;} else {list.add(new ArrayList<>(Arrays.asList(nums[i], nums[j], nums[left], nums[right])));left++;right--;while (left < right && nums[left] == nums[left - 1]) {left++;}while (left < right && nums[right] == nums[right + 1]) {right--;}}}}}return list;}
}

看了讲解后:

  • 去重操作可以放在最后写

改后代码:

class Solution {public List<List<Integer>> fourSum(int[] nums, int target) {List<List<Integer>> list = new ArrayList<>();Arrays.sort(nums);if (nums[nums.length - 1] < 0 && target > 0) {return list;}for (int i = 0; i < nums.length - 3;) {if (nums[i] > 0 && target <= 0) {break;}for (int j = i + 1; j < nums.length - 2;) {int left = j + 1;int right = nums.length - 1;while (left < right) {int sum = nums[left] + nums[right] + nums[i] + nums[j];if (sum > target) {right--;} else if (sum < target) {left++;} else {list.add(new ArrayList<>(Arrays.asList(nums[i], nums[j], nums[left], nums[right])));left++;right--;while (left < right && nums[left] == nums[left - 1]) {left++;}while (left < right && nums[right] == nums[right + 1]) {right--;}}}j++;while (j < nums.length - 2 && nums[j] == nums[j - 1]) {j++;}}i++;while (i < nums.length - 3 && nums[i] == nums[i - 1]) {i++;}}return list;}
}

总结

使用双指针:

  1. 一般是在数组中使用,通常需要对数组进行排序.
  2. 数据要有单调性.
  3. 使用双指针可以把时间复杂度降一维.O(n3)变O(n2);

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

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

相关文章

【Web】御网杯信息安全大赛2024 wp(全)

目录 input_data admin flask 如此多的FLAG 一夜醒来之全国CTF水平提升1000倍&#x1f60b; input_data 访问./.svn后随便翻一翻拿到flag admin dirsearch扫出来 访问./error看出来是java框架 测出来是/admin;/路由打Spring View Manipulation(Java)的SSTI https:/…

基于ECC簇内分组密钥管理算法的无线传感器网络matlab性能仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 5.完整程序 1.程序功能描述 基于ECC簇内分组密钥管理算法的无线传感器网络matlab性能仿真&#xff0c;对比网络通信开销&#xff0c;存活节点数量&#xff0c;网络能耗以及数据通信量四个指标…

【Linux篇】TCP/IP协议(笔记)

目录 一、TCP/IP协议族体系结构 1. 数据链路层 &#xff08;1&#xff09;介绍 &#xff08;2&#xff09;常用协议 ① ARP协议&#xff08;Address Resolve Protocol&#xff0c;地址解析协议&#xff09; ② RARP协议&#xff08;Reverse Address Resolve Protocol&…

华为为什么要做三折叠屏手机?

前些天我做了一条视频&#xff0c;关于讲华W的新的三折叠屏手机。我说我有点失望&#xff0c;结果引起了华W的同事的一些关注。于是&#xff0c;华W几位高管都跑过来&#xff0c;跟我解释为什么会出现这样的一个状态。 我才知道&#xff0c;这款手机他们其实是亏着钱在卖的。因…

C++速通LeetCode中等第1题-字母异位词分组

思路要点&#xff1a;对字符串排序&#xff0c;排序结果存放在map的key中&#xff0c;排序结果相同的字符串存放到map的value中 。 class Solution { public:string keys;vector<vector<string>> groupAnagrams(vector<string>& strs) {vector<vecto…

EECS498 Deep Learning for Computer Vision (一)软件使用指南

#最近开始学习深度学习的相关基础知识&#xff0c;记录一下相关笔记及学习成果# learning&#xff1a;building artificial systems that learn from data and experience deep learning(a set of machine learning): hierarchical learning algorithms with many "laye…

海洋大地测量基准与水下导航系列之二国外海底大地测量基准和海底观测网络发展现状(上)

海底大地控制网建设构想最先由美国斯克里普斯海洋研究所(Scripps Institution of Oceanography,SIO)提出&#xff0c;目前仅有少数发达国家具备相应技术条件。美国、日本、俄罗斯和欧盟等发达国家通过布测先进的海底大地控制网&#xff0c;不断完善海洋大地测量基准基础设施&am…

6、等级保护政策内容

数据来源&#xff1a;6.等级保护政策内容_哔哩哔哩_bilibili 信息安全产品管理与响应 等级管理 对信息系统中使用的信息安全产品实行按等级管理&#xff0c;信息安全事件应分等级响应与处置。 预测评服务由测评公司和咨询公司提供预测评服务&#xff0c;根据技术要求和测评要…

深度学习01-概述

深度学习是机器学习的一个子集。机器学习是实现人工智能的一种途径&#xff0c;而深度学习则是通过多层神经网络模拟人类大脑的方式进行学习和知识提取。 深度学习的关键特点&#xff1a; 1. 自动提取特征&#xff1a;与传统的机器学习方法不同&#xff0c;深度学习不需要手动…

前端工程化4:从0到1构建完整的前端监控平台

前言 一套完整的前端监控系统的主要部分&#xff1a; 数据上报方式数据上送时机性能数据采集错误数据采集用户行为采集定制化指标监控sdk 监控的目的&#xff1a; 一、数据上报方式 本文的方案是&#xff0c;优先navigator.sendBeacon&#xff0c;降级使用1x1像素gif图片…

Python3网络爬虫开发实战(17)爬虫的管理和部署(第一版)

文章目录 一、 Scrapyd 分布式部署1.1 了解 Scrapyd1.2 准备工作1.3 访问 Scrapyd1.4 Scrapyd 的功能1.5 ScrapydAPI 的使用 二、Scrapyd-Client 的使用2.1 准备工作2.2 Scrapyd-Client 的功能2.3 Scrapyd-Client 部署 三、Scrapyd 对接 Docker3.1 准备工作3.2 对接 Docker 四、…

Linux网络工具:用于查询DNS(域名系统)域名解析信息的命令nslookup详解

目录 一、概述 二、基本功能 1、查询域名对应的IP地址 2、查询IP地址对应的主机名 3、查询特定类型的DNS记录 三、用法 1、命令格式 2、常用选项 五、nslookup的安装 1. 打开终端 2. 更新的系统包列表 3. 安装 bind-utils 软件包 &#xff08;1&#xff09;对于Ce…

Vue点击按钮生成pdf文件/Vue点击按钮生成png图片

本次案例是vue的点击生成pdf文件和png格式的图片 一、生成pdf文件案例 看代码之前&#xff0c;我们肯定得需要看看&#xff0c;效果图是什么的啦&#xff0c;这样子才能先看看自己想要实现的效果是不是这样子的&#xff01;上效果图嘿嘿嘿~ A、实现的效果图 这是页面&#…

java intellij idea开发步骤,使用指南,工程创建与背景色字体配置,快捷键

intellij idea2021 配置背景色&#xff0c;字体大小&#xff0c;主题 快捷键

JACM23 - A New Algorithm for Euclidean Shortest Paths in the Plane

前言 如果你对这篇文章感兴趣&#xff0c;可以点击「【访客必读 - 指引页】一文囊括主页内所有高质量博客」&#xff0c;查看完整博客分类与对应链接。 本文关注的问题为计算几何学中的经典问题&#xff0c;即「在平面上给定一组两两不相交的多边形障碍物&#xff0c;寻找两点…

linux设置常见开机自启动命令

本文介绍了三种开机自启的方式&#xff0c;重点介绍使用systemctl的方式自启动的 方式一、修改 /etc/rc.d/rc.local 文件 /etc/rc.d/rc.local 文件会在 Linux 系统各项服务都启动完毕之后再被运行。所以你想要自己的脚本在开机后被运行的话&#xff0c;可以将自己脚本路径加到…

C++——关联式容器(4):set和map

在接触了诸如二叉搜索树、AVL树、红黑树的树形结构之后&#xff0c;我们对树的结构有了大致的了解&#xff0c;现在引入真正的关联式容器。 首先&#xff0c;先明确了关联式容器的概念。我们之前所接触到的如vector、list等容器&#xff0c;我们知道他们实际上都是线性的数据结…

51单片机——矩阵键盘

一、矩阵键盘原理图 我们发现: P17,P16,P15,P14控制行&#xff0c; P13,P12,P11,P10控制列。 所以我们如果要选择第四列&#xff0c;只需要把整个P1先给高电位1&#xff0c;再把P10给低电位0。 二、代码 P10xFF; P100; if(P170){Delay(20);while(P170);Delay(20);KeyNum…

【Linux笔记】虚拟机内Linux内容复制到宿主机的Window文件夹(文件)中

一、共享文件夹 I、Windows宿主机上创建一个文件夹 目录&#xff1a;D:\Centos_iso\shared_files II、在VMware中设置共享文件夹 1、打开VMware Workstation 2、选择需要设置的Linux虚拟机&#xff0c;点击“编辑虚拟机设置”。 3、在“选项”标签页中&#xff0c;选择“共…

【Stm32】从零建立一个工程

这里我们创建“STM32F103”系列的文件&#xff0c;基于“固件库” 1.固件库获取 https://www.st.com.cn/zh/embedded-software/stm32-standard-peripheral-libraries.html 2.使用Keil创建.uvprojx文件 前提是已经下载好了“芯片对应的固件” 3.复制底层驱动代码 将固件库下的…