图解 LeetCode 算法汇总——双指针

2023-09-18 10:04:34

双指针算法是一种比较常用于搜索链表或数组相关的问题,很多算法的基本的解题思路就是使用暴力搜索法。而双指针是对暴力搜索的一种优化,通过双指针可以减少数据的遍历次数。通常双指针是有两个指针,叫做 light 左指针和 right 右指针,或者叫做快指针和慢指针

  • 作为左右指针的话,一般是在数组的或者链表的头尾两侧,从两遍往中间收缩,获取到符合条件的答案。
  • 作为快慢指针的话,主要应用于解决链表问题。通常快指针移动的比慢指针移动的快,这种方法可以用来检测链表中是否有环、寻找链表的中间结点。

LeetCode 题解

11.盛最多水的容器

题目描述

解题思路

这题可以暴力破解法,双循环遍历出来,查出所有存在的情况,但是时间复杂是O(N²),实现简单,但是比较耗时。而且本题也无需遍历所有的情况,分析一下解题思路:

本题求解的是最大的盛水面积,盛水面积是由宽和高组成,也就是找到横坐标的长度 * 两条竖线中更端的线的乘积。

最开始时候,左右指针分别指向数组的左右两端,获取到容量 min(1,7) * 8。

然后两遍指针往中间收缩,需要往中间移动一个指针,重点是需要移动哪一个?往中间收缩,宽度一定是变短,而高度是由两条竖线中的最短的一条决定。移动更大竖线的指针,面积肯定会减少,因为宽度和高度都在减少,所以需要移动竖线更小的指针,面积才可能增加。上面移动左边的竖线。面积由原来的 min(1,7) * 8 = 8 到 min (8,7) * 7 = 49,面积增多了。

使用 max 变量记录当前最大的面积,和每次搜索的面积做对比,获取到最大值。代码如下所示:

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

        }
        return max;
    }
}

15.三数之和

题目描述

解题思路 for + 双指针

无论是两数之和或者三数之和都可以使用到双指针,但是双指针只有两个指针无法同时记录三个数,所以就需要使用一个 for 循环,for 每次遍历记录一个值,再使用双指针记录左右两个值。

为了减少遍历的次数,需要对数组做一个排序。然后 for 循环定位到第一个元素,左右两个指针的目标值就是 target - for 循环定位元素。左右指针相加得到总和 sum。

  • 如果sum < target,左指针往右移动。
  • 如果sum > target,右指针往左移动。
  • 如果sum = target,符合条件,返回值。

然后定位到第二个元素,继续使用双指针收缩遍历。

代码如下:

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> lists = new ArrayList<>();
        Arrays.sort(nums);
	//for循环 + 双指针
	for (int i = 0; i < nums.length; i++) {
            if (i > 0 && nums[i] == nums[i - 1]) {
                continue;
            }
	    int target = -nums[i];
	    int left = i +1,right = nums.length - 1;
            while (left < right) {
		int sum = nums[left] + nums[right];
		if (sum == target) {
		    List<Integer> list = new ArrayList<>();
	            list.add(nums[i]);
		    list.add(nums[left]);
		    list.add(nums[right]);
		    lists.add(list);
		    break;
		} else if (sum < target) {
		    left++;
		} else {
		    right--;
		}
	}
	}
	return lists;
    }
}

142. 环形链表 II

题目描述

解决方案

这道题在链表那篇文章也有,求解使用了 hash 表,遍历链表,存储在 hash 表中。如果有相同的数据,说明链表是有环了。本题采用快慢指针的办法。

起始的时候,快慢指针都在首节点。

慢指针一次走一步,快指针一次走两步,移动第一次之后。

fast 指针走过链表末端,说明链表无环,此时直接返回 null。如果两个指针遇上,说明链表有环,当两个指针第一次相遇时,fast 走的步数是 slow 的两倍。

从相遇点到入环点的距离加上 n−1 圈的环长,恰好等于从链表头部到入环点的距离。当 slow 和 fast 相遇时,可以在使用一个额外的指针 ptr,开始指向链表头部,随后它和 slow 每次往后移动,最后他们会在入环结点相遇,此时就能获取到入环结点

public class Solution {
    public ListNode detectCycle(ListNode head) {
        if (head == null) {
            return null;
        }
        ListNode slow = head, fast = head;
        while (fast != null) {
            slow = slow.next;
            if (fast.next != null) {
                fast = fast.next.next;
            } else {
                return null;
            }
            if (fast == slow) {
                ListNode ptr = head;
                while (ptr != slow) {
                    ptr = ptr.next;
                    slow = slow.next;
                }
                return ptr;
            }
        }
        return null;
    }
}

总结

双指针算法是一种常用于解决数组或链表相关问题的算法技巧。该算法通常涉及到使用两个指针(索引),它们可以从数组或链表的不同位置出发,根据问题的要求,以一定的方式移动这些指针,从而在数组或链表上执行一些特定的操作。一般使用左右指针和快慢指针两种方式。

更多推荐

mybatis/mp批量插入非自增主键数据

文章目录前言一、mp的批量插入是假的二、真正的批量插入1.利用sql注入器处理2.采用自编码,编写xml批量执行生成内容如下:三问题问题描述问题原因问题解决粘贴一份,兼容集合替换原有文件总结自增与非自增区别:前言mybatis/mp在实际开发中是常用的优秀持久层框架,但是在非自增主键的时候,单条数据插入式可以的,当批量

[pai-diffusion]pai的easynlp的clip模型训练

EasyNLP带你玩转CLIP图文检索-知乎作者:熊兮、章捷、岑鸣、临在导读随着自媒体的不断发展,多种模态数据例如图像、文本、语音、视频等不断增长,创造了互联网上丰富多彩的世界。为了准确建模用户的多模态内容,跨模态检索是跨模态理解的重要任务,…https://zhuanlan.zhihu.com/p/528476134

大型语言模型:SBERT — 句子BERT

了解siameseBERT网络如何准确地将句子转换为嵌入简介Transformer在NLP领域取得了进化性的进步,这已不是什么秘密。基于Transformer,还发展出了许多其他机器学习模型。其中之一是BERT,它主要由几个堆叠的Transformer编码器组成。除了用于一系列不同的问题(例如情感分析或问答)之外,BE

云原生Kubernetes:K8S集群list-watch机制与 pod调度约束

目录一、理论1.K8S的list-watch机制2.亲和性二、实验1.指定调度节点2.节点亲和性3.亲和性和反亲和三、问题1.新生成pod一直为pending2.如何一次性删除pod和deployment3.pod亲和性资源报错4.pod反亲和性资源报错四、总结一、理论1.K8S的list-watch机制(1)概念Ku

前端防抖和节流

前端防抖和节流概述防抖:防止抖动,个人字面理解此处防的不是页面的抖动,而是用户手抖。为了防止用户快速且频繁的触发事件而导致多次执行事件函数,这样的场景有很多,比如监听滚动、鼠标移动事件onmousemove、频繁点击表单的提交按钮等等。节流:节约流量,为了节约流量,页面在一个时间周期内,只触发一次动作。所以节流的目的时

CSS笔记

选择器通配选择器*{}元素选择器元素{}类选择器.类名{}id选择器#id名{}复合选择器交集选择器/*p元素且类名为beauty的元素*/p.beauty{}.rich.beauty{}并集选择器又称分组选择器.rich,.beauty,.navtop,#myIMage{}后代选择器<!--选中后代中所有li--><

python小程序 图书馆图书借阅借还管理系统 mbc21

为设计一个安全便捷,并且使借阅者更好获取本图书借还信息,本文主要有安全、简洁为理念,实现借阅者快捷寻找图书借还信息,从而解决图书借还信息复杂难辨的问题。该系统以django架构技术为基础,采用python语言和MySQL数据库进行开发设计,通过对图书借还管理流程的分析,分析了其功能性和非功能性需求,设计了基于微信小程序

设计模式:桥接模式

目录组成部分代码实现优缺点总结桥接模式是一种软件设计模式,用于将抽象部分与其实现部分分离,使它们可以独立地变化。该模式通过创建一个桥接接口,将抽象类和实现类连接起来,从而使它们可以独立地进行修改和扩展。桥接模式可以提高系统的灵活性和可扩展性,同时也有助于简化系统的设计。组成部分桥接模式的各个组成部分包括:抽象部分(Ab

有了Spring为什么还需要SpringBoot呢

目录一、Spring缺点分析二、什么是SpringBoot三、SpringBoot的核心功能3.1起步依赖3.2自动装配一、Spring缺点分析1.配置文件和依赖太多了!!!spring是一个非常优秀的轻量级框架,以IOC(控制反转)和AOP(面向切面)为思想内核,极大简化了JAVA企业级项目的开发。虽然Spring的

docker - 分享

1.docker介绍:Docker是一种虚拟化技术,它允许你在一台机器上运行多个应用程序,每个应用程序都运行在一个独立的虚拟器中,互相之间不会干扰。这些容器使用了操作系统级别的虚拟化技术,课可以在同一物理机器上运行多个应用程序,同时每个容器又拥有自己独立的文件系统和资源管理。Docker可以让你快速地创建、部署、和复制

开心档之JavaScript 异步编程

JavaScript异步编程目录JavaScript异步编程异步的概念什么时候用异步编程回调函数实例实例实例异步AJAX实例实例异步的概念异步(Asynchronous,async)是与同步(Synchronous,sync)相对的概念。在我们学习的传统单线程编程中,程序的运行是同步的(同步不意味着所有步骤同时运行,而

热文推荐