【算法】双指针-OJ题详解1

news2024/12/22 19:17:48

双指针-OJ题

    • 移动零(点击跳转)
      • 原理讲解
      • 代码实现
    • 复写零(点击跳转)
      • 原理讲解
      • 代码实现
    • 快乐数(点击跳转)
      • 原理讲解
      • 代码实现
    • 盛最多水的容器(点击跳转)
      • 原理讲解
      • 代码实现
    • 有效三角形的个数(点击跳转)
      • 原理讲解
      • 代码实现
    • 查找总价值为目标值的两个商品(点击跳转)
      • 原理讲解
      • 代码实现
    • 三数之和(点击跳转)
      • 原理讲解
      • 代码实现
    • 四数之和(点击跳转)
      • 原理讲解
      • 代码实现

移动零(点击跳转)

在这里插入图片描述

原理讲解

在这里插入图片描述在这里插入图片描述

因此我们定义两个指针:

  • cur:遍历数组
  • dest:已处理的区间内,最后一个非零元素的位置

这样就把数组划分成三块区间:
[0 , dest] 、[dest+1 , cur-1] 、[cur , size-1]
分别对应:
非零元素、零、待处理

具体操作:
cur遍历过程中

  • 遇到0元素
    cur++;
  • 遇到非0元素
    swap(dest+1,cur); //swap数组中下标为dest+1和cur的元素
    dest++;cur++;

代码实现

void moveZeroes(vector<int>& nums) {
    int dest = -1;
    int cur = 0;
    while (cur < nums.size())
    {
        if (nums[cur])
            swap(nums[++dest], nums[cur]);
        cur++;
    }
}

复写零(点击跳转)

在这里插入图片描述

原理讲解

这道题首先要想到从后向前遍历,因为如果从前向后遍历,会覆盖掉后面的值,较难操作,因此:
在这里插入图片描述在这里插入图片描述
第一步找到最后一个数,可以用快慢指针来实现:cur指针、dest指针。前者从前向后遍历,后者根据前者位置的值走1步或2步,具体如下:

  • 先判断cur位置的值
  • 若cur位置的值非0,dest向后移动一步;若cur位置的值为0,dest向后移动2步
  • 判断dest位置是否到了结束的位置
  • cur++

代码实现

void duplicateZeros(vector<int>& arr) {
    int dest = -1;
    int cur = 0;
    //找到最后一个数
    while (cur < arr.size())
    {
        if (arr[cur])
            dest++;
        else
            dest += 2;
        if (dest >= arr.size() - 1)
            break;
        cur++;
    }
//处理特殊情况:cur指向的最后一个数是0,dest越界
    if (dest == arr.size())
    {
        arr[dest - 1] = 0;
        cur--;
        dest -= 2;
    }
//复写
    while (cur >= 0)
    {
        if (arr[cur])
            arr[dest--] = arr[cur--];
        else
        {
            arr[dest--] = 0;
            arr[dest--] = 0;
            cur--;
        }
    }
}

快乐数(点击跳转)

在这里插入图片描述

原理讲解

在这里插入图片描述
最后一定会成环(可以证明,用鸽巢原理)
因此可以通过判断环的起点是否为1,决定返回true还是false

→ 快慢指针
快慢指针有⼀个特性,就是在⼀个圆圈中,快指针总是会追上慢指针的,也就是说他们总会相遇在⼀个位置上。就本题而言,如果相遇位置的值是 1 ,那么这个数⼀定是快乐数;如果相遇位置不是 1 ,那么就不是快乐数。

代码实现

//求x每一位数的平方和
    int SqSum(int x)
    {
        int ret = 0;
        while(x)
        {
            int n = x%10;
            ret += n*n;
            x/=10;
        }
        return ret;
    }

    bool isHappy(int n) {
        int slow = n,fast = SqSum(n);//fast不能设成n,否则进不了循环
        while(slow != fast)
        {
            slow = SqSum(slow);
            fast = SqSum(SqSum(fast));
        }
        return slow==1;
    }

盛最多水的容器(点击跳转)

在这里插入图片描述

原理讲解

我们首先想到的大概率是两层循环暴力枚举,但是复杂度高,这道题不能通关

那应该如何解这题呢?
首先,容器的容积(这道题只考虑面积)是S = h * w
h表示高度,即height[?]
w表示宽度,即两条垂线间隔的距离

为了方便叙述,我们假设左边边界height[left]小于右边边界height[right]

如果此时我们固定⼀个边界,改变另⼀个边界,水的容积会有如下变化形式:

  • 容器的宽度w一定变小。
  • 由于左边界较小,决定了水的高度。①如果改变左边界,新的水面高度不确定,但是一定不会超过右边的柱子高度,因此容器的容积可能会改变(可能变大、变小、不变)。
  • ②如果改变右边界,无论右边界移动到哪里,新的水面的高度一定不会超过左边界,也就是不会超过现在的水面高度,但由于容器的宽度减小,因此容器的容积⼀定会变小的。

所以我们可以舍去情况②,只需要讨论情况①(因为我们的目的是求最大的容积)

因此,我们定义两个指针left和right,然后比较height[left]height[right],移动height小的位置的指针,循环这个过程,期间产生的所有的容积里的最大值,就是要return的最终答案

代码实现

    int maxArea(vector<int>& height) {
        int left = 0;
        int right = height.size()-1;
        int _max = 0;
        while(left < right)
        {
            int tmp = (right-left)*min(height[right],height[left]);
            _max = max(_max,tmp);
            if(height[left] < height[right])
                left++;
            else
                right--; 
        }
        return _max;
    }

有效三角形的个数(点击跳转)

在这里插入图片描述

原理讲解

我们小学五年级都学过,三角形的三条边必须满足:任意两边之和大于第三边、任意两边之差小于第三边~

假设三角形三条边长度分别为:a, b, c

  • 方法① a+b < c ; a+c < b ; b+c < a

  • 方法② 在①的基础上优化:先排序,a ≤ b ≤ c,只需要判断 a+b > c即可

解法一:三层循环暴力枚举 → O(n3)

解法二:利用(排序后)单调性,用双指针算法来解决。 → O(n2)

  • 先固定最大的数
  • 在最大数的左边区间内,用双指针算法,快速统计出符合要求的另外两个数:
    • 如果 nums[left] + nums[right] > nums[max],说明 [left, right - 1] 区间上的所有元素均可以与 nums[right] 构成比nums[max] 大的二元组,此时满足条件的有 right - left 种;此时 right 位置的元素的所有情况相当于全部考虑完毕, right-- ,进入下一轮判断
    • 如果 nums[left] + nums[right] <= nums[max],说明 left 位置的元素是不可能与 [left + 1, right] 位置上的任意元素构成满足条件的二元组,left++ 进入下一轮循环
  • 向左移动最大数,循环上面的过程

代码实现

    int triangleNumber(vector<int>& nums) {
        int left,right;
        int cmax = nums.size() - 1;
        int ret = 0;
        sort(nums.begin(),nums.end());
        while (cmax > 1) {
            left = 0;
            right = cmax-1;
            while (left < right) {
                if (nums[left] + nums[right] > nums[cmax]) {
                    ret+=(right-left);
                    right--;     
                } else {
                    left++;
                }
            }
            cmax--;//向左移动最大数
        }
        return ret;
    }

查找总价值为目标值的两个商品(点击跳转)

在这里插入图片描述

原理讲解

类比上面《有效三角形的个数》,会发现利用单调性同样很香:利用对撞指针优化时间复杂度

  • 初始化 leftright 分别指向数组的左右两端
  • 接下来无非三种情况:
    • price[left] + price[right] == target,说明找到了,跳出循环返回即可
    • price[left] + price[right] > target,说明两数之和大了,right–
    • price[left] + price[right] < target,说明两数之和小了,left++

代码实现

    vector<int> twoSum(vector<int>& price, int target) {
        int left = 0,right = price.size()-1;
        vector<int> ret;
        while(left < right)
        {
            if(price[left] + price[right] == target)
            {
                ret.push_back(price[left]);
                ret.push_back(price[right]);
                break;
            }
            else if(price[left] + price[right] > target)
                right--;
            else
                left++;

        }
        return ret;
    }

或者不需创建vector直接返回:

    vector<int> twoSum(vector<int>& price, int target) {
        int left = 0,right = price.size()-1;
        while(left < right)
        {
            if(price[left] + price[right] == target)
                return {price[left] , price[right]};
            else if(price[left] + price[right] > target)
                right--;
            else
                left++;
        }
        return {-1};//注意这里必须return一个数组,目的是照顾编译器,否则编译不通过
    }

三数之和(点击跳转)

在这里插入图片描述

原理讲解

我们可以利用在两数之和那道题的双指针思想,来对暴力枚举做优化:

  • 先排序
  • 然后固定一个数 a
  • 在这个数后面的区间内,利用双指针快速找到两个数之和等于 -a 即可

但是要注意的是,这道题需要有去重操作~
(除了用set,我们可以自己实现)

  • 找到一个结果之后, left 和 right 指针要跳过重复的元素;
  • 当用完⼀次双指针算法之后,固定的 a 也要跳过重复的元素。

代码实现

    vector<vector<int>> threeSum(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        int i =0,left,right;
        vector<vector<int>> vv;
        while(i < nums.size()-2)
        {
            if(nums[i] > 0)
                break;
            //left+right
            left = i+1;
            right = nums.size()-1;
            while(left < right && right < nums.size())
            {
                if(nums[left] + nums[right] == -nums[i])
                {
                    vv.push_back({nums[i],nums[left],nums[right]});
                    left++;right--;
                    while(left < right && right < nums.size()&&nums[left] == nums[left-1])
                        left++;//left跳过重复元素
                    while(left < right && right < nums.size()&&nums[right] == nums[right+1])
                        right--;//right跳过重复元素
                }
                else if(nums[left] + nums[right] > -nums[i])
                    right--;
                else
                    left++;
            }
            i++;
            while(nums[i] == nums[i-1] && i<nums.size()-2)
                ++i;//“固定”的数跳过重复元素
        }
        return vv;  
    }

四数之和(点击跳转)

在这里插入图片描述

原理讲解

这道题和上面的《三数之和》几乎一模一样,区别在于多了一层

  • 固定一个数a
  • 在这个数 a 的后面区间上,利用「三数之和」找到三个数,使这三个数的和等于 target - a 即可

代码实现

    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        sort(nums.begin(),nums.end());
        vector<vector<int>> vv;
        int i=0,j,left,right;
        while(i<nums.size())
        {
            j = i+1;
            while(j<nums.size())
            {
                left = j+1;
                right = nums.size()-1;
                long long aim =  (long long)target-nums[i]-nums[j];//有些用例不强转会越界
                while(left<right)
                {
                    int sum =nums[left]+nums[right];
                    if(sum == aim)
                    {
                        vv.push_back({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--;
                    }
                    else if(sum < aim)
                        left++;
                    else
                        right--;
                }
                ++j;
                while(j < nums.size() && nums[j] == nums[j-1])
                    ++j;
            }
            ++i;
            while(i<nums.size() && nums[i] == nums[i-1])
                ++i;
        }
        return vv;
    }

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

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

相关文章

模式植物构建orgDb数据库 | 以org.Slycompersicum.eg.db为例

原文链接:模式植物构建orgDb数据库 | 以org.Slycompersicum.eg.db为例 本期教程 一步构建模式植物OrgDb数据库 source("../Set_OrgDb_Database.R")# 使用函数 Set_OrgDb_Database(emapper_file "out.emapper_tomato.csv", ## 输入的eggnog结果文件json_…

使用 MinIO、Langchain 和 Ray Data 构建分布式嵌入式子系统

嵌入子系统是实现检索增强生成所需的四个子系统之一。它将您的自定义语料库转换为可以搜索语义含义的向量数据库。其他子系统是用于创建自定义语料库的数据管道&#xff0c;用于查询向量数据库以向用户查询添加更多上下文的检索器&#xff0c;最后是托管大型语言模型 &#xff…

Stream 33

package Array.collection;import java.util.*; import java.util.stream.Stream;public class stream1 {public static void main(String[] args) {//、如何茯取List集合的Stream流?List<String> names new ArrayList<>();Collections. addAll(names,"方法…

超声波眼镜清洗机哪个品牌好?四款高性能超声波清洗机测评剖析

对于追求高生活质量的用户来说&#xff0c;眼镜的清洁绝对不能马虎。如果不定期清洁眼镜&#xff0c;时间久了&#xff0c;镜片的缝隙中会积累大量的灰尘和细菌&#xff0c;眼镜靠近眼部&#xff0c;对眼部健康有很大影响。在这种情况下&#xff0c;超声波清洗机显得尤为重要。…

现象:程序没问题,compile成功,在load时,提示prg.dll没找到

现象:程序没问题&#xff0c;compile成功&#xff0c;在load时&#xff0c;提示prg.dll没找到 解决方法&#xff1a;使用新的ATE电脑主机&#xff0c;导致的问题&#xff0c;又换回原来的电脑主机&#xff0c;问题解决。

数据结构与算法--【链表1】力扣练习 || 链表 / 移除链表元素

声明&#xff1a;本文参考代码随想录。 一、链表定义 1、概念 将线性表L(a0,a1,……,an-1)中各元素分布在存储器的不同存储块&#xff0c;称为结点&#xff0c;通过地址或指针建立元素之间的联系。 每一个结点由两部分组成&#xff1a;数据域和指针域。结点的data域存放数据…

第三届Apache Flink 极客挑战赛暨AAIG CUP比赛攻略_大浪813团队

关联比赛: 第三届 Apache Flink 极客挑战赛暨AAIG CUP——电商推荐“抱大腿”攻击识别 第三届Apache Flink 极客挑战赛暨AAIG CUP比赛攻略_大浪813团队 第三届Apache Flink 极客挑战赛暨AAIG CUP 自2021年8月17日上线以来已有 4537 个参赛队伍报名。11月09号&#xff0c;大赛…

Android入门之路 - WebView加载数据的几种方式

之前客户端加载H5时遇到了一些问题&#xff0c;我为了方便解决问题&#xff0c;所以将对应场景复刻到了Demo中&#xff0c;从之前的网络加载模拟为了本地加载Html的方式&#xff0c;但是没想到无意被一个基础知识点卡了一些时间&#xff0c;翻看往昔笔记发现未曾记录这种基础场…

C语言初阶(10)

1.野指针 野指针就是指向未知空间的指针&#xff0c;有以下几种情况 &#xff08;1)指针未初始化 int main() {int a0;int*b;return 0; } 上面指针就是没有初始化&#xff0c;形成一种指向一个随机空间的地址的指针&#xff0c;我们可以修改成 int main() {int a0;int*bNU…

甘肃雀舌面:舌尖上的独特韵味

雀舌面&#xff0c;顾名思义&#xff0c;其面条形状如同雀舌般小巧精致。这一独特的形态并非偶然所得&#xff0c;而是源于精湛的手工技艺。制作雀舌面&#xff0c;对面粉的选择和面团的揉制有着极高的要求。经验丰富的师傅会精心挑选优质面粉&#xff0c;加入适量的水&#xf…

嵌入式学习---DAY17:共用体与位运算

链表剩余的一些内容 一、共用体 union 共用体名 名称首字母大写 { 成员表列&#xff1b; }&#xff1b; union Demo {int i;short s;char c; }; int main(void) {union Demo d;d.i 10;d.s 100;d.c 200;printf("%d\n", sizeof(d)); /…

一起学习LeetCode热题100道(24/100)

24.回文链表(学习) 给你一个单链表的头节点 head &#xff0c;请你判断该链表是否为 回文链表 。如果是&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 示例 1&#xff1a; 输入&#xff1a;head [1,2,2,1] 输出&#xff1a;true 示例 2&#xff1a; …

鸿蒙Scroll布局,横向与纵向

注意&#xff0c;当横向scroll时&#xff0c;直接子元素的宽&#xff0c;不能100%&#xff0c; 当纵向scroll时&#xff0c;直接子元素的高&#xff0c;不能100%​​​​​​​ 1、纵向代码&#xff1a; 方法1&#xff1a;用数值计算&#xff0c;来设置中间的高度&#xff1a; …

Django函数视图和类视图

函数视图 1.全局环境的urls.py引入映入应用的urls&#xff0c;避免后期开发路由过多而导致杂乱 from django.contrib import admin from django.urls import path, includeurlpatterns [path(account/, include(account.urls)),#使用include函数引入&#xff0c;表示account…

搜狗爬虫(www.sogou.com)IP及UA,真实采集数据

一、数据来源&#xff1a; 1、这批搜狗爬虫&#xff08;www.sogou.com&#xff09;IP来源于尚贤达猎头网站采集数据&#xff1b; ​ 2、数据采集时间段&#xff1a;2023年10月-2024年7月&#xff1b; 3、判断标准&#xff1a;主要根据用户代理是否包含“www.sogou.com”和IP核实…

我菜单为什么跟着滑动了?!!—— 固定定位为什么会失效?

背景&#xff1a;最近在写一个H5页面项目&#xff0c;头部有个菜单栏&#xff0c;需要固定在顶部不动&#xff0c;但是滑动之后设置并没有生效&#xff0c;之前开发中也遇到过类似的固定失效的情况&#xff0c;就去详细了解了下有哪些可能导致固定定位失效的情况。希望有些场景…

数据库文件管理

数据库文件与普通文件区别: 1.普通文件对数据管理(增删改查)效率低 2.数据库对数据管理效率高,使用方便 常用数据库: 1.关系型数据库&#xff1a; 将复杂的数据结构简化为二维表格形式 大型:Oracle、DB2 中型:MySql、SQLServer 小型:Sqlite 2.非关…

【传知代码】主动学习实现领域自适应语义分割(论文复现)

在当今计算机视觉和人工智能领域&#xff0c;语义分割技术的发展正日益成为实现智能系统感知和理解复杂场景的关键。然而&#xff0c;传统的语义分割模型往往面临着在新领域或不同环境下适应性不足的挑战。为了解决这一问题&#xff0c;主动学习作为一种强大的技术手段&#xf…

每日学术速递8.2

1.A Scalable Quantum Non-local Neural Network for Image Classification 标题&#xff1a; 用于图像分类的可扩展量子非局部神经网络 作者&#xff1a; Sparsh Gupta, Debanjan Konar, Vaneet Aggarwal 文章链接&#xff1a;https://arxiv.org/abs/2407.18906 摘要&#x…

echarts图表--雷达图(参数详解)

这是官网给出的几种雷达图&#xff1a; Examples - Apache ECharts 雷达图&#xff08;Radar Chart&#xff09;是一种用于展示多维数据的图表&#xff0c;尤其适合于比较不同样本或观测在多个相同变量上的表现。在使用ECharts或PyEcharts等可视化库绘制雷达图时&#xff0c;…