算法技巧-双指针

news2024/11/27 0:15:24

欢迎关 Android茶话会pdf阿里&字节经典面试题、Android、算法、Java等系列武功秘籍

在技术学习、个人成长的道路上,让我们一起前进!

前言

双指针技巧在算法题中算是常用技巧了,让我们省去for循环,降低复杂度,通常双指针技巧可以分为2大类

  • 快慢指针
  • 左右指针

前者主要解决链表中的问题,比如链表是否有环,删除倒数第N个节点等,后者主要解决数组、字符串中的问题,比如二分查找、翻转数组等,下面将详细介绍下

快慢指针

下面结合实例来介绍快慢指针在实际中的用途

判断链表是否有环

单链表的特点是每个节点只知道下一个节点,所以一个指针的话无法判断链表中是否含有环的。如果链表中不含环,那么这个指针最终会遇到空指针 null 表示链表到头了,这还好说,可以判断该链表不含环,如果链表含有环,那么单独一个指针就会陷入死循环。
这时候双指针的作用就体现了,我们使用2个指针,一个跑的快,一个跑的慢。如果没有环的话,跑得快的会遇到null,如果有环的话,快指针最终会和慢指针相遇

boolean hasCycle(ListNode head) {
    ListNode fast, slow;
    fast = slow = head;
    while (fast != null && fast.next != null) {
        fast = fast.next.next;
        slow = slow.next;
        if (fast == slow) return true;
    }
    return false;
}

链表是否有环,有的话返回这个环的起始位置

image.png
这个题目多了一些数学计算

  1. 先找相遇点 判断是否有环
  2. 有环的话重置其中一个指针到起点,相同速度前进,再次相遇就是节点位置开始的位置
public ListNode detectCycle(ListNode head) {
    ListNode slow,fast;
    slow = fast = head;
    boolean hasRecycle = false;
    //快慢指针找到交点
    while(fast!=null && fast.next!=null){
        slow = slow.next;
        fast = fast.next.next;
        if(slow == fast) {
            hasRecycle = true;
            break;
        }
    }   
    if(!hasRecycle) {
        return null;
    }
    
    //将其中一个重置到起点,再一起移动,下次相交就是相遇点
    slow = head;
    //int index = 0;
    while(slow!=fast){
        slow = slow.next;
        fast = fast.next;
        //index++;
        if(slow == fast) break;
    }
    return slow;
}

解释下原理
可以看到,当快慢指针相遇时,让其中任一个指针指向头节点,然后让它俩以相同速度前进,再次相遇时所在的节点位置就是环开始的位置。这是为什么呢?

第一次相遇时,假设慢指针 slow 走了 k 步,那么快指针 fast 一定走了 2k 步,也就是说比 slow 多走了 k 步(也就是环的长度)
假设慢指针回到环的起点,设相遇点距环的起点距离为m,slow和fast再次相遇之后行走距离为k,,那么距头结点head的距离为k-m, 此时快指针距离相遇点也是k-m,因此慢指针回到节点之后两指针同速前进,k-m步就会相遇,相遇的地方就是环的起点了
image.png

寻找链表的倒数K个元素

让快指针先走 k 步,然后快慢指针开始同速前进。这样当快指针走到链表末尾 null 时,慢指针所在的位置就是倒数第 k 个链表节点(为了简化,假设 k 不会超过链表长度)

ListNode slow, fast;
slow = fast = head;
while (k-- > 0) 
    fast = fast.next;
while (fast != null) {
    slow = slow.next;
    fast = fast.next;
}
return slow;

移除元素

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。

这里我们就可以使用快慢指针,在[0,slow)中的元素都是值不等于val=target的元素,fast指针用于扫描整个数组

public int removeElement(int[] nums, int val) {
    // 慢指针slow 区间[0,slow)内的元素为值不等于val的元素
    int slow = 0;
    for(int fast = 0; fast < nums.length; fast++) {
        // 快指针fast所指向的元素值不等于val=3
        // 将其值赋值于慢指针所在位置
        if (nums[fast] != val) {
            nums[slow] = nums[fast];
            // 赋值完毕之后,慢指针右移一位,等待下一次赋值
            slow++;
        }
    }
    return slow;
}

左右指针

左右指针通常是使用两边索引值,一般初始化为 left = 0, right = nums.length - 1

二分查找

二分查找是经典的左右指针,(需要注意的细节是开闭区间),本题使用闭区间 [0,nums.length-1]

int binarySearch(int[] nums, int target) {
    int left = 0; 
    int right = nums.length - 1;
    if(nums[(left+right)/2] == target) return (left+right)/2;
    while(left <= right) {
        int mid = (right + left) / 2;
        if(nums[mid] == target)
            return mid; 
        else if (nums[mid] < target)
            left = mid + 1; 
        else if (nums[mid] > target)
            right = mid - 1;
    }
    return -1;
}

反转字数组(字符串)

字符串也是同理

void reverse(int[] nums) {
    int left = 0;
    int right = nums.length - 1;
    while (left < right) {
        // swap(nums[left], nums[right])
        int temp = nums[left];
        nums[left] = nums[right];
        nums[right] = temp;
        left++; right--;
    }
}

两数之和

image.png
通常通过hash法来解,这里是有序数组,因此也可以通过双指针来解决,类似二分查找变种,双指针处理有序数组还是很棒的,代码如下

int[] twoSum(int[] nums, int target) {
    int left = 0, right = nums.length - 1;
    while (left < right) {
        int sum = nums[left] + nums[right];
        if (sum == target) {
            // 题目要求的索引是从 1 开始的
            return new int[]{left + 1, right + 1};
        } else if (sum < target) {
            left++; // 让 sum 大一点
        } else if (sum > target) {
            right--; // 让 sum 小一点
        }
    }
    return new int[]{-1, -1};
}

滑动窗口

稍微复杂一点的双指针使用,核心就是维护一个窗口,调整窗口的大小,重点把握的是

  • 窗口内是什么
  • 窗口起始位置如何移动
  • 窗口结束位置如何移动

伪代码如下

int left = 0, right = 0;

while (right < s.size()) {
    // 增大窗口
    window.add(s[right]);
    right++;
    //窗口数据更新……
    
    while (window needs shrink) {
        // 缩小窗口
        window.remove(s[left]);
        left++;
    }
}

image.png

看个经典算法题

209.长度最小的子数组(mid)-滑动窗口
给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0,如:
输入:s = 7, nums = [2,3,1,2,4,3] 输出:2 解释:子数组 [4,3] 是该条件下的长度最小的子数组。

//滑动窗口
fun minSubArrayLen(target: Int, nums: IntArray): Int {
    var sum = 0
    var subLength = 0
    var result = Int.MAX_VALUE

    //滑动窗口起始位置
    var left = 0

    for(right in nums.indices){
        //开始累加
        sum += nums[right]
        //滑动窗口
        while(sum >= target){
            subLength = right - left +1
            result = if(result < subLength) result else subLength
            //移动窗口左边界
            sum-=nums[left++]
        }
    }
    return if(result == Int.MAX_VALUE) 0 else result
}

关于滑动窗口 B站也有讲解视频 https://www.bilibili.com/video/BV1V44y1s7zJ?spm_id_from=333.1007.top_right_bar_window_history.content.click&vd_source=f9b305274daffa5db46d0a6df9e6bdfa

欢迎关 Android茶话会pdf阿里&字节经典面试题、Android、算法、Java等系列武功秘籍

在技术学习、个人成长的道路上,让我们一起前进!

您的 点赞、评论,是对我的巨大鼓励!

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

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

相关文章

第二十章_Redis分布式锁Redlock算法和底层源码分析

当前代码为8.0版接上一步 自研一把分布式锁&#xff0c;面试中回答的主要考点 按照JUC里面java.util.concurrent.locks.Lock接口规范编写 lock()加锁关键逻辑 加锁的Lua脚本&#xff0c;通过redis里面的hash数据模型&#xff0c;加锁和可重入性都要保证 加锁不成&#xff0c;…

NLP面经集结 | 达摩院、腾讯、微软、美团、百度

作者 | Codle 整理 | NewBeeNLP 面试锦囊之面经分享系列&#xff0c;持续更新中 赶紧后台回复"面试"加入讨论组交流吧 写在前面 本人情况&#xff1a;双非本末流985研二&#xff0c;爱奇艺NLP日常实习经历&#xff0c;无论文&#xff0c;投的都是 NLP 算法岗。 目前…

Ansible基础5——条件语句、循环语句、handlers、任务失败处理

文章目录 一、 循环语句1.1 单量循环1.2 多量循环1.3 老版本用法1.4 loopregister 二、条件判断2.1 根据变量状态判断2.2 根据变量是否存在判断2.3 根据事实判断2.4 多条件判断2.4.1 and用法2.4.2 or用法 2.5 循环判断2.6 根据上个任务结果判断 三、handlers处理程序四、任务失…

CDGA 认证:第十二章 元数据管理(重点章节)习题集解析

1. 以下内容不属于元数据综合解决方案功能需求点的是 ( ) A 历史信息 B 存储容量 C 运维要求 D 安全要求 【答案解析】DAMA-DMBOK2 P334 2. 关于 ISO/IEC 11179 元数据注册标准的说法正确的是:( ) A 该标准由 6 个部分组成 B 该标准用于定义元数据注册的框架 C 该标准包含…

Express+vue.js+nodejs银行排队取号系统

开发语言 node.js 框架&#xff1a;Express 前端:Vue.js 数据库&#xff1a;mysql 数据库工具&#xff1a;Navicat 开发软件&#xff1a;VScode 系统主要包括系统用户中心、显示管理、用户管理、排队管理、服务业务管理、用户评价管理、等候区管理等功能模块。 (a) 管理员&am…

00后求你善良,不要这么卷了...

前几天我们公司一下子也来了几个新人&#xff0c;这些年前人是真能熬啊&#xff0c;本来我们几个老油子都是每天稍微加会班就打算走了&#xff0c;这几个新人一直不走&#xff0c;搞得我们也不好走。 2023年春招结束了&#xff0c;最近内卷严重&#xff0c;各种跳槽裁员&#x…

Studio One6新生代DAW数字音频工作站

提到编曲软件&#xff0c;就不得不说这款水果编曲软件。它对新手和老手都比较友好&#xff0c;是一款较为经典的编曲软件。 这款软件提供了强大而全面的音符、音效编辑器&#xff0c;可以在其中插入各种乐器声音&#xff0c;如果内置乐器无法满足编曲需求&#xff0c;还可以外…

数据结构-递归

递归 概述 定义 计算机科学中&#xff0c;递归是一种解决计算问题的方法&#xff0c;其中解决方案取决于同一类问题的更小子集 In computer science, recursion is a method of solving a computational problem where the solution depends on solutions to smaller instan…

Web大前端时代之:HTML5+CSS3入门系列

Old: 联系源码&#xff1a;GitHub - dunitian/LoTHTML5: LoT家族的一员~HTML5系列 文档下载&#xff1a;https://github.com/dunitian/LoTDotNet/ 思维导图&#xff08;不断更新&#xff09;&#xff1a; 图片&#xff1a;http://dnt.dkill.net/DNT/HTML5/index.jpg 源码&#…

超级全面的100个思维导图模板

思维导图是一款有效提升生活以及工作的效率工具。使用思维导图有非常多的好处。 思维导图可以用在我们生活的方方面面&#xff0c;以及各种各样的学科中。 接下来给大家分享不同的思维导图模板&#xff0c;希望可以帮助到大家。 一、读书笔记类 1、西游记 2、《战胜拖拉》读…

RabbitMQ - 简单案例

RabbitMQ - 简单案例 Hello worldWork Queues轮训分发消息消息应答自动应答手动消息应答的方法消息自动重新入队消息手动应答代码 RabbitMQ 持久化不公平分发 预取值分发 Hello world 我们将用 Java 编写两个程序。发送单个消息的生产者和接收消息并打印出来的消费者 在下图中…

Java实现Mqtt收发消息

Java实现Mqtt收发消息 文章目录 Java实现Mqtt收发消息windows mqtt 平台服务搭建mqtt 客户端工具&#xff1a;mqttbox整体代码结构mqtt基础参数配置类mqtt客户端连接mqtt接收的消息处理类对应的MqttService注解和MqttTopic注解 MqttGateway 发送消息指定topic接收处理方法 java…

基于Hive的数据应用实践总结

百分位数(percentile)计算 百分位数含义&#xff1a;统计学术语&#xff0c;如果将一组数据从小到大排序&#xff0c;并计算相应的累计百分位&#xff0c;则某一百分位所对应数据的值就称为这一百分位的百分位数。可表示为&#xff1a;一组n个观测值按数值大小排列。如&#x…

如何实现Java类隔离加载

一 什么是类隔离技术 只要你 Java 代码写的足够多&#xff0c;就一定会出现这种情况&#xff1a;系统新引入了一个中间件的 jar 包&#xff0c;编译的时候一切正常&#xff0c;一运行就报错&#xff1a;java.lang.NoSuchMethodError&#xff0c;然后就哼哧哼哧的开始找解决方法…

弹性盒子中的flex

flex属性是flex-grow&#xff0c;flex-shrink和flex-basis的缩写 flex是用在盒子中的子组件的&#xff0c;充分体现了弹性盒子的弹性二字。 例如现在的情况是&#xff1a; <div class"container"><div class"item1">Item1</div><d…

微信小程序码生成,扫码携带参数进入指定页面

一、准备工作 &#xff08;1&#xff09;微信小程序后台获取小程序的appId和secret 小程序后台管理&#xff08;开发管理➡开发设置&#xff09; &#xff08;2&#xff09;扫码跳转的页面在app.json中已经注册 注册的路径与传过去的路径一致 &#xff08;3&#xff09;小程序…

同步模式之犹豫模式Balking

tip: 作为程序员一定学习编程之道&#xff0c;一定要对代码的编写有追求&#xff0c;不能实现就完事了。我们应该让自己写的代码更加优雅&#xff0c;即使这会费时费力。 文章目录 一、同步模式之犹豫模式Balking二、代码样例三、优缺点 一、同步模式之犹豫模式Balking 同步模…

挤出泡沫、脱虚向实,AI大模型正在回归价值投资?

商品推荐、交通管理、生成文章、代码编程、电影特效制作……自ChatGPT横空出世以来&#xff0c;AIGC浪潮席卷全球&#xff0c;上下游产业链也因此大放异彩。 市场行情的高景气直观反映在股价上&#xff0c;无论AI公司是否盈利&#xff0c;其股价多呈上升趋势。一些与AI概念有所…

测试:用例篇

上一章讲述的是测试的基本概念。在我们开始做了一段时间基础测试&#xff0c;熟悉了业务之后&#xff0c;往往会 分配来写测试用例&#xff0c;并且在日常测试中&#xff0c;有时也需要补充测试用例到现有的案例库中 在开始之前先讲讲测试中经典的测试方法&#xff1a;黑盒测试…

【dc-dc】DC-DC恒流电源 车灯方案的应用

1,信息来源&#xff1a;深圳市世微半导体有限公司 Augus 2,产品描述 AP5103 是一款效率高&#xff0c;稳定可靠的 LED 灯恒流驱动控制芯片&#xff0c;内置高精度比较器&#xff0c;固定关断时间控制电路&#xff0c;恒流驱动电路等&#xff0c;特别适合大功率 LED 恒流驱动。…