两个双指针 的 “他“和“ 她“会相遇么? —— “双指针“算法 (Java版)

news2024/11/23 13:23:48

本篇会加入个人的所谓鱼式疯言

❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言
而是理解过并总结出来通俗易懂的大白话,
小编会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的.
🤭🤭🤭可能说的不是那么严谨.但小编初心是能让更多人能接受我们这个概念 !!!

在这里插入图片描述

前言

因为小编最近在学习算法, 每次学习新知识,都会有新的体会和心得,就会和小伙伴们一起分享, 今天 带来我们 算法 首篇 ————“双指针” 算法 💥💥💥

这时就会有小伙伴问了,我们的"双指针"算法 不是在 “题海寻offer” 专题中讲解过么 ? 为啥还要重复学习呢 🤔 🤔 🤔

是的, 但这次小编带来的是更重点更详细并且更系统以算法的角度一步一步讲解我们的 “双指针” 算法 💖 💖 💖

目录

  1. 双指针算法初识

  2. 双指针算法的运用

  3. 双指针算法的总结

一. 双指针初识

1. 双指针算法的认识

首先小编在这里得区别一个概念

这里我们提及的 双“指针” 的指针 并不是我们学过 C语言 的 定义 int * p = &a 那个指针 ,

这里我们用的指针其实就是一个 变量

我们通过 变量 来指向一个元素 然后不断移动来影响数据的一个单纯的 变量

双指针 就是定义两个变量来 影响数据

2. 双指针算法的种类

常见的双指针有两种形式:

一种是 对撞指针,一种是 左右指针

对撞指针

对撞指针 从两端向中间移动。一个指针从 最左端 开始,另一个从 最右端 开始,然后逐渐往 中间 逼近。

对撞指针 的终止条件一般是两个指针 相遇=或者 错开(也可能在循环内部找到结果直接跳出循环),也就是:

left == right (两个指针指向同一个位置)

left > right (两个指针错开)

快慢指针

快慢指针:又称为 龟兔赛跑算法,其基本思想就是使用两个移动 速度不同 的指针在数组或链表等序列结构上移动。

这种方法对于处理 环形链表数组 非常有用。

其实不单单是环形链表或者是数组,如果我们要研究的问题出现 循环往复 的情况时,均可考虑使用 快慢指针 的思想。

快慢指针的实现方式有很多种,最常用的一种就是:

在一次循环中,每次让慢的指针向后移动 一位 ,而快的指针往后移动 两位,实现一快一慢

鱼式疯言

总结三句话

  1. 指针本质上是 变量, 只是形式上我们俗称 “指针”

  2. 对撞指针 是 从起点和终点两个 同时 相向 走,会相遇 的两个指针

  3. 快慢指针 是 同向不相遇的两个走的跨度不一样的指针

二. 双指针的算法运用

1. 复写零

1089.复写零题目链接

<1> . 题目描述

在这里插入图片描述

给你一个长度固定的整数数组 arr ,请你将该数组中出现的每个零都复写一遍,并将其余的元素
向右平移。
注意:请不要在超过该数组长度的位置写入元素。请对输入的数组就地进行上述修改,不要从函数返
回任何东西。
示例 1:
输入: arr = [1,0,2,3,0,4,5,0]
输出: [1,0,0,2,3,0,0,4]
解释:
调用函数后,输入的数组将被修改为: [1,0,0,2,3,0,0,4]

题目的 含义

在这个数组中,我们 从左往右 ,每出现一个0 就在右边位置再 写一个 0 ,然后数据一直 挪动 , 原有数据就要一直往右 挪动

<2>. 讲解算法原理

大体思路 分为 :

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

但是 「从后向前」 复写的时候,我们需要找到 `「最后一个复写的数」 `` ,因此我们的大体流程分两
步:

算法流程

  • 先找到最后一个复写的数

请添加图片描述

  1. 先定义一个 cur=0dest= -1 指针来 通向移动
  1. 先让 cur 向前走一步 , 当 cur 位置遇到 值 为 0 时就 dest 就走 两 步

3,否则让 dest 走一步 ,直到 dest 到底 数组下标 >= n- 1 的位置就停止

  • 然后从后向前进行复写操作。

请添加图片描述

  1. cur 遇到 0 时 ,就让 dest 跳跃两次 并 把这两次的值都赋值为 0cur 走一步
  1. cur 不是 0 时 ,就把 cur 的值赋给 dest , 接着让 dest 和 cur 都走一步

<3>. 编写代码

class Solution {
       public static void duplicateZeros(int[] arr) {
        int cur=0;
        int dest=-1;
        int n=arr.length;
       while (cur < n) {
           if (arr[cur]==0) {
               dest +=2;
           } else {
                dest++;
           }
           if (dest >= n-1) {
               break;
           }
           cur++;

       }

       if (dest > n-1) {
           arr[n-1]=0;
           cur--;
           dest-=2;
       }

       while (cur >= 0) {
           if (arr[cur]==0) {
               arr[dest--]=0;
               arr[dest--]=0;
               cur--;
           } else {
               arr[dest--]=arr[cur--];
           }
       }
    }

}

在这里插入图片描述

鱼式疯言

说两句小编关于本题自己的理解和体会

  1. 这里我们用到了 前后指针(快慢指针) , 一个 快指针 模拟 复写零 后的数组的位置 , 而 慢指针 用于 模拟 == 未复写零后的数组的最后一个元素的位置
  1. 细节
   if (dest > n-1) {
       arr[n-1]=0;
       cur--;
       dest-=2;
   }

当 == dest== 的位置跳到 n 的位置时, 说明 0 的位置不够放 , 我们就需要 把最后一个位置 赋值为 0
并让 dest 想左走两步, cur 走一步即可

2. 快乐数

202. 快乐数题目链接

<1>. 题目描述

在这里插入图片描述

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

「快乐数」 定义为:

◦ 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。

◦ 然后重复这个过程直到这个数变为 1,也可能是无限循环但始终变不到 1 。

◦ 如果这个过程 结果为 1 ,那么这个数就是快乐数。

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

示例 1:
输入: n = 19
输出: true
解释:
19 -> 1 * 1 + 9 * 9 = 82
82 -> 8 * 8 + 2 * 2 = 68
68 -> 6 * 6 + 8 * 8 = 100
100 -> 1 * 1 + 0 * 0 + 0 * 0 = 1

示例 2:
输入: n = 2
输出: false
解释:(这里省去计算过程,只列出转换后的数)
2 -> 4 -> 16 -> 37 -> 58 -> 89 -> 145 -> 42 -> 20 -> 4 -> 16

往后就不必再计算了,因为出现了重复的数字,最后结果肯定不会是1

题目含义:

让一个数一直取出每个位置上的数字的 平方算出他们的之和 ,得到这个数后 判断是否为 1

如果为 1 说明是快乐数 返回 true

如果继续循环的按照上面的方法计算知道 得到看是否 最终结果为 1= ,若不是就返回 false

<2>. 讲解算法思想

我们先简单的分析一下

为了方便叙述,将「对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和」这一个
操作记为 x 操作;

题目告诉我们,当我们不断重复 x 操作的时候,计算一定会 「死循环」 ,死的方式有 两种

▪ 情况一:一直在 1死循环 ,即 1 -> 1 -> 1 -> 1…

请添加图片描述

▪ 情况二:在历史的数据中死循环,但始终变不到 1

请添加图片描述

由于上述两种情况只会出现一种

因此,只要我们能确定循环是在 「情况一」 中进行

还是在 「情况二 ] 中进行,就能得到结果。

大体的算法思想

先用一个方法来 获取到每一位上数字的 平方之和

由于我们在循环内查找这个数 (相当于检查一个链表是否成环) , 所以我们第一步考虑的应该是 快慢指针 的思维,

我们进行利用 定义 一个 left 的单位为 1 走一步, 再定义一个 right 单位为 2 走两步 ,因为是环形所以 最终是一定会相遇的

最后一步 我们只需要在相遇的位置判断是否是 等于 1 即可

请添加图片描述

<3>. 编写代码

class Solution {
    public boolean isHappy(int n) {
        int slow=n;
        int fast=happyFunc(n);

        // 一定会成环的
        while(slow != fast) {
                slow = happyFunc(slow);
                fast = happyFunc(happyFunc(fast));
        }

        // 最终相遇时只要为 1 就为快乐数
        if(slow==1) {
            return true;
        }

        return false;
    }


    private int happyFunc(int n) {
        int sum=0;
        while(n != 0) {
            int t = n % 10;
            sum += (t*t);
            n /= 10; 
        }

        return sum;
    }

}

在这里插入图片描述

鱼式疯言

先说几点体会吧

  1. 当我们碰到 一个成环或者循环 的一种结构时, 快慢指针的思路是很有必要去尝试的
  1. 本题 的 特点 最终是会成为一个 特定的值 , 我们只需要循环判断即可
  1. 本题细节
int slow=n;
    int fast=happyFunc(n);

初始化的时候

不可以让 fast 和 slow 同时为 n ,否则就进入不了 循环 .

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

179. 查找总价值为目标值的两个商品题目链接

<1>. 题目描述

在这里插入图片描述

输入一个 递增 排序的数组和一个数字s ,在数组中查找两个数,使得它们的和正好是s 。如果有多
对数字的和等于s ,则输出 任意一对 即可。

示例 1:
输入: nums = [2,7,11,15], target = 9
输出: [2,7] 或者 [7,2]

题目含义 就是找两个和为 s 的数字

<2>. 讲解算法思想

题目分析

如果我们要求两个数的 ,只需要先 固定 一个数,然后再 寻找其他数 判断即可寻找到

暴力枚举

两层 for 循环列出所有两个数字的组合,判断是否等于目标值。

双指针 的思路

当我们看到 一个有序数组的时候, 我们头脑中就得建立两个思路 :

一个 二分查找 算法

一个 双指针 算法

而在本题中更适合用我们的双指针来解决 两数和 的问题

  1. 先定义一个指针为 left = 0 , 另一个指针为 right = n-1
  1. leftright 的 数值之和 > target 时 ,我们就让 较大值 right --

left 和 right 的数组之和 < target 时, 我们就让较小值 left ++ ;

直到最终相遇或者寻找到目标值 target 为止 ,

请添加图片描述

<3>. 编写代码

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

在这里插入图片描述

鱼式疯言

记住一点

有序就要想到用 二分查找算法 或者 双指针算法

4 有效三角形个数

611.有效三角形个数题目链接

<1>. 题目描述

在这里插入图片描述

给定一个包含非负整数的数组 nums ,返回其中可以组成三角形三条边的三元组个数。

示例 1:

输入: nums = [2,2,3,4]
输出: 3
解释:有效的组合是:
2,3,4 (使用第一个 2)
2,3,4 (使用第二个 2)
2,2,3

示例 2:

输入: nums = [4,2,3,4]
输出: 4

题目含义 :

找出所有能满足 组合 三角形三条边 的个数(包含相同数字的边)

<2>. 讲解算法思想

题目分析

如果我们要找三条边, 先 固定两个数 的基础上

再找另外一个数来判断是否满足 三角形三边条件

算法过程

解法一 : 暴力枚举

三个 for= 来查找 是否存在该数据

解法二 : 双指针算法

先将 数组排序

根据「解法一」中的优化思想,我们可以固定一个「最长边」,然后在比这条边小的有序数组中找
出一个二元组,使这个二元组之和大于这个最长边。由于数组是有序的,我们可以利用「对撞指
针」来优化。

设最长边枚举到 i 位置,区间 [left, right]i 位置**左边的区间(**也就是比它小的区
间):

◦ 如果 nums[left] + nums[right] > nums[i]

▪ 说明 [left, right - 1] 区间上的所有元素均可以与 nums[right] 构成比
nums[i] 大的二元组

▪ 满足条件的有 right - left

▪ 此时 right 位置的元素的所有情况相当于全部考虑完毕, right-- ,进入下一轮判断

◦ 如果 nums[left] + nums[right] <= nums[i]

▪ 说明 left 位置的元素是不可能与 [left + 1, right] 位置上的元素构成满足条件
的二元组

left 位置的元素可以舍去left++ 进入下轮循环

请添加图片描述

<3>. 编写代码

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

          return count;  
    }
}

在这里插入图片描述

鱼式疯言

讲两点

  1. 必须 先排序 ,利用本身的 单调性 来进行 两个较小值和最大值进行比较来判断

所以我们得出以下结论

有序 》 单调性 》 双指针

三. 双指针算法的总结

复写零快乐数 时, 用到了(快慢指针).特别在快乐数的时候,我们在循环的特点下

我们建立了 快慢指针的思路

而在 查找两个数之和有效三角形个数 时, 我们用到了 双指针(对撞指针)

而在这里我们巧妙的利用 单调性 的方法来建立 对撞指针 的模型

如果觉得小编写的还不错的咱可支持 三连 下 (定有回访哦) , 不妥当的咱请评论区 指正

希望我的文章能给各位宝子们带来哪怕一点点的收获就是 小编创作 的最大 动力 💖 💖 💖

在这里插入图片描述

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

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

相关文章

如何掌握 Java 正则表达式 的基本语法及在 Java 中的应用

正则表达式是一种用于匹配字符串的模式&#xff0c;在许多编程语言中广泛使用。Java 正则表达式提供了强大的文本处理能力&#xff0c;能够对字符串进行查找、替换、分割等操作。 一、正则表达式的基本语法 正则表达式由普通字符和特殊字符组成。普通字符包括字母、数字和标点…

第二十七章HTML.CSS综合案例(二)

3.菜单栏 效果图如下&#xff1a; 代码图如下&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><…

cnPuTTY 0.81.0.1-JK—PuTTY 0.81中文JK补丁版的简单说明~~

原始补丁网站的链接&#xff1a;PuTTY for win32 storing configuration into file2. 6. 2024 - Update: this modified PuTTY is now based on PuTTY 0.81 (version 0.23.0) 本次官方正式发布的补丁与上一版本的补丁相同&#xff0c;无明显变化。关于JK补丁的信息也可以参考&…

[洛谷] 刷题栈 队列

目录 1.后缀表达式 2.表达式括号匹配 3.表达式求值 4.表达式的转换 5.机器翻译 1.后缀表达式 后缀表达式 - 洛谷 #include<iostream> #include<cstdio> using namespace std;int stk[100]; // 用于存储操作数的栈 int index 0; // 栈顶索引int main() {c…

docker构建java项目镜像

资料参考 参考自黑马教程&#xff1a;10.Docker基础-自定义镜像_哔哩哔哩_bilibili 初步准备 打包好java项目jar包&#xff0c;和Dockerfile文件一起放到指定目录下&#xff0c;后续操作都是在该目录下操作&#xff0c; 我这边是&#xff1a;/usr/local/src/train-ticket/ …

IDEA破解后的配置

以下所有操作都要求进入全局setting而不是某一个项目的setting 进入全局Setting File→close project 进入欢迎页面 低版本 然后点击Setting 关闭自动更新 不关闭有可能会破解失败 Appearance & Behavior->System Settings->Updates下取消Automatically chec…

【网络编程开发】6.UDP通信

6.UDP通信 UDP实现框架 send 函数 原型&#xff1a; #include <sys/socket.h> ssize_t send(int sockfd, const void *buf, size_t len, int flags);功能&#xff1a; send 函数的主要功能是向指定的套接字发送数据。 参数&#xff1a; sockfd&#xff1a;一个有效的套…

【Linux】进程(7):地址空间

大家好&#xff0c;我是苏貝&#xff0c;本篇博客带大家了解Linux进程&#xff08;7&#xff09;&#xff1a;地址空间&#xff0c;如果你觉得我写的还不错的话&#xff0c;可以给我一个赞&#x1f44d;吗&#xff0c;感谢❤️ 目录 &#xff08;A&#xff09; 直接看代码&…

Leetcode3169. 无需开会的工作日

Every day a Leetcode 题目来源&#xff1a;3169. 无需开会的工作日 解法1&#xff1a;排序 遍历 按 LeetCode56.合并区间 的做法&#xff0c;把 meetings 数组中所有重叠的区间合并起来&#xff0c;再统计其中无需开会的工作日个数。 代码&#xff1a; /** lc appleetco…

学习笔记——路由网络基础——缺省(默认)路由

3、缺省(默认)路由 1、定义 缺省路由(默认路由)&#xff1a;是目的地址和掩码都为全0的特殊路由。全0代表任意网络。缺省路由在路由表中的形式为&#xff1a;0.0.0.0/0缺省路由也被叫默认路由。缺省路由优先级比直连路由低 缺省路由是一种特殊的路由&#xff0c;当报文没有在…

cv2.imwrite路径中存在中文时出现乱码问题

cv2.imwrite(path, img) 在写入包含中文的路径的时候&#xff0c;保存的文件名称为乱码。 解决办法&#xff1a; cv2.imwrite(path,image)将上面的代码修改为以下代码&#xff0c;可以避免出现中文乱码。 cv2.imencode(.jpg, image)[1].tofile(path)

嵌入式Linux系统编程 — 2.2 标准I/O库:检查或复位状态

目录 1 检查或复位状态简介 2 feof()函数 2.1 feof()函数简介 2.2 示例程序 3 ferror()函数 4 clearerr()函数 4.1 clearerr()函数简介 4.2 示例程序 1 检查或复位状态简介 调用 fread() 函数读取数据时&#xff0c;如果返回值小于参数 nmemb 所指定的值&#xff0c;这…

python常见数据分析函数

apply DataFrame.apply(func, axis0, broadcastFalse, rawFalse, reduceNone, args(), **kwds) 第一个参数是函数 可以在Series或DataFrame上执行一个函数 支持对行、列或单个值进行处理 import numpy as np import pandas as pdf lambda x: x.max()-x.min()df pd.DataFrame(…

【数据结构】详解堆的基本结构及其实现

文章目录 前言1.堆的相关概念1.1堆的概念1.2堆的分类1.2.1小根堆1.2.2大根堆 1.3堆的特点堆的实用场景 2.堆的实现2.1初始化2.2插入2.3堆的向上调整2.4删除2.5堆的向下调整2.6判空2.7获取堆顶元素2.8销毁 3.堆排序3.1实现3.2堆排序的时间复杂度问题 前言 在上一篇文章中&#…

【数据库】SQL零基础入门学习

人不走空 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌赋&#xff1a;斯是陋室&#xff0c;惟吾德馨 目录 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌…

如何通过 4 种方式备份和恢复Android联系人

毫无疑问&#xff0c;联系人是Android手机上存储的最重要的信息之一。为了保护这些重要数据&#xff0c;明智的做法是对Android手机进行联系人备份。如果您的手机发生任何情况导致数据丢失&#xff0c;例如被盗、系统崩溃或物理损坏&#xff0c;您可以再次将备份中的联系人恢复…

Typecho:简约而强大的开源PHP博客平台

Typecho&#xff1a;让博客写作回归本质- 精选真开源&#xff0c;释放新价值。 概览 Typecho是一个开源的PHP博客平台&#xff0c;以其简洁的界面和强大的功能&#xff0c;为博客作者提供了一个高效、易于管理的写作环境。它是一个轻量级、高性能的解决方案&#xff0c;适用于…

主流数据库的大数据插入对比(mssql[sql server]、oracle、postgresql、mysql、sqlite)

首先申明&#xff0c;做这个对比不代表数据库性能&#xff0c;纯属好奇。勿喷&#xff0c;感谢。 测试连续11次插入数据库&#xff0c;每次100万行数据。 测试环境&#xff1a;单机测试&#xff0c;就是所有数据库都装在本机上。操作系统:windows server 2016&#xff0c;使用…

【YOLOV8】1.开发环境搭建

Yolo8出来一段时间了,包含了目标检测、实例分割、人体姿态预测、旋转目标检测、图像分类等功能,所以想花点时间总结记录一下这几个功能的使用方法和自定义数据集需要注意的一些问题,本篇是第一篇,开发环境的配置。 YOLO(You Only Look Once)是一种流行的物体检测和图像分割…

工控主板分类详解

1.ATX系列 尺寸305*244mm;接口扩展丰富,更多的内存和PCIE插槽; 进一步略小的有MATX,尺寸244*244cm;扩展插槽缩减,但兼容ATX接口,依旧是按照ATX标准 2.ITX系列 尺寸170*170mm;相较于ATX主板更加迷你,功能接口也少一些; 常用于小型计算机或者嵌入式系统 高能计算机推…