【算法一周目】双指针(1)

news2024/12/23 13:18:07

目录

1.双指针介绍

2.移动零

解题思路

 C++代码实现

3.复写零

解题思路

C++代码实现

4.快乐数

解题思路

C++代码实现

5.盛水最多的容器

解题思路

C++代码实现


1.双指针介绍

常见的双指针有两种形式,一种是对撞指针,一种是快慢指针。

对撞指针:一般用于顺序结构中,也称左右指针。

  • 对撞指针从两端向中间移动。⼀个指针从最左端开始,另⼀个从最右端开始,然后逐渐往中间逼近。
  • 对撞指针的终止条件⼀般是两个指针相遇或者错开(也可能在循环内部找到结果直接跳出循环),也就是:

left == right (两个指针指向同⼀个位置);
left > right (两个指针错开);

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

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

快慢指针的实现方式有很多种,最常用的一种是:一次循环中,每次让慢指针向后移动一位,而快的指针向后移动两位,实现一快一慢。

2.移动零

题目链接:283. 移动零
题目描述:给定一个数组nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

解题思路

这题我们使用双指针解决,需要一个cur指针和一个dest指针,将数组划分成3个区间,[0, dest]的元素全部是非0元素[dest + 1, cur - 1]的元素全是0[cur -1, n - 1]的元素是待处理的

具体流程如下:

1.初始化cur = 0(用于遍历数组),dest = -1(指向非0元素的最后一个位置,由于刚开始我们不知道最后一个非0元素在什么位置,因此初始化为-1

2.cur依次往后遍历每个元素。

  • 当cur遇到0时,cur++
  • 当cur遇到非0时,dest++,交换dest的位置与cur位置的值

 C++代码实现

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        for (int cur = 0, dest = -1; cur < nums.size(); cur++) {
            if (nums[cur] != 0 && ++dest!= cur) {
                swap(nums[dest], nums[cur]);
            }
        }
    }
};

时间复杂度:O(n)

空间复杂度:O(1)

3.复写零

题目链接:1089. 复写零
题目描述:给定一个固定长度的整数数组arr,在遇到每个零时,将其右移并插入一个零,同时保持数组长度不变。

解题思路

因为数组的0需要复写2次有可能会把后面的非0元素覆盖掉,所以从前往后复写是不可取的,所以我们采取从后往前复写。

具体过程如下:

1.找到最后一个需要复写的元素,这一步需要双指针来解决。

  • 初始化两个指针cur = 0, dest = -1
  • cur的位置为0时,dest += 2非0时dest++
  • 判断dest是否大于等于n - 1(数组最后一个位置),若满足则退出循环
  • cur++,继续循环,直到找到最后一个复写的元素。

2.处理越界情况,当dest == n时,说明cur在数组n - 2位置且元素是0,这时候就要特殊处理,dest--,将arr[dest]置0,dest--,cur--。

3.从后往前复写将arr[cur]复写到arr[dest]若arr[cur]为非0,复写一次,若arr[cur]为0,则复写两次,记得分情况更新cur和dest,直到复写结束。

C++代码实现

class Solution {
public:
    void duplicateZeros(vector<int>& arr) {
        int cur = 0, dest = -1, n = arr.size();
        //1.寻找最后一个需要复写的数
        while(cur < n)
        {
            if(arr[cur])
                dest++;
            else
                dest += 2;
            
            if(dest >= n - 1) break;
            cur++;
        }                                                                                                                                                                  
        //2.处理越界情况
        if(dest == n)
        {
            dest--;
            arr[dest--] = 0;
            cur--;
        }

        //3.从后往前复写元素
        while(cur >= 0)
        {
            if(arr[cur])
            {
                arr[dest--] = arr[cur--];
            }
            else{
                arr[dest--] = 0;
                arr[dest--] = 0;
                cur--;
            }
        }
    }
};

时间复杂度:O(n)

空间复杂度:O(1)

4.快乐数

题目链接:202. 快乐数

题目描述:编写一个算法来判断一个数 n 是不是快乐数。

对于n = 2,2 -> 4 -> 16 -> 37 -> 58 -> 89 -> 145 -> 42 -> 20 -> 4 -> 16,进入到循环了。

解题思路

为了方便分析,将“对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和”这一操作记为f操作

由题目可知,当我们对一个数不断进行f操作时,最后一定会进入死循环死循环也分为两种第一种就是一直在1循环,是快乐数第二种就是在历史数据中死循环,但始终变不到1。

由于两种情况只会出现一种,所以我们只需判断是否一直在1循环,就可知此数是不是快乐数。

证明

  • 对于INT_MAX(2^31-1=2147483647),取一个更大的数9999999999,进行f操作后得到最大值810,可以得到f操作得到的值范围是[1, 810]
  • 根据“鸽巢原理”,对一个数进行f操作811次后,必定进入循环。

对此,我们可以得知,无论是什么数,对其进行多次f操作后,必定会形成一个环,所以我们可以使用快慢指针来解决。

具体过程:

1.编写f操作的函数bitsum,将该数替换为它每个位置上的数字的平方和。

2 .初始化快慢指针,slow = n,fast = bitsum(n)

3.当slow != fast时slow向前走一步,fast先前走两步,直到循环结束。

4.判断slow是否等于1,若等于则该数是快乐数。

C++代码实现

class Solution {
public:

    int bitsum(int n)
    {
        int ret = 0;
        while(n)
        {
            ret += pow(n % 10, 2);
            n /= 10;
        }
        return ret;
    }

    bool isHappy(int n) {
        int slow = n, fast = bitsum(n);
        while(slow != fast)
        {
            slow = bitsum(slow);
            fast = bitsum(bitsum(fast));
        }
        return slow == 1;
    }
};

时间复杂度:O(log n) 在快慢指针法中,求平方和的时间复杂度为对数级别。

空间复杂度:O(1)

5.盛水最多的容器

题目链接:11. 盛最多水的容器

题目描述:给定一个长度为n的整数数组height,有n条垂线,第i条线的两个端点是(i, 0) 和(i, height[i])。
找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
返回容器可以储存的最大水量。

说明:你不能倾斜容器。

示例 1

  • 输入:[1,8,6,2,5,4,8,3,7]
  • 输出:49
  • 解释:数组中的垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在这个情况下,能够容纳水(表示为蓝色部分)的最大值是49。

解题思路

对于该题,我们很容易就想到使用暴力解法,用两个for循环暴力枚举能构成的所有容器找出其中容积最大的值。

容器计算公式:设两个指针i,j,分别指向容器的最左端和最右端,此时容器的宽度为j - 1,由于容器的高度由较短的高度决定,可得容器公式。

v = (j - i) * min(height[i], height[j])

暴力解法的代码

class Solution {
public:
    int maxArea(vector<int>& height) {
        int n = height.size();
        int ret = 0;
        // 两层 for 循环枚举出所有可能出现的情况
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                // 计算容积,找出最大的那一个
                ret = max(ret, min(height[i], height[j]) * (j - i));
            }
        }
        return ret;
    }
};

但是暴力解法需要遍历所有可能的组合,故时间复杂度是O(n ^ 2),如果提交到leetcode上是会超时,所以我们要寻求最优的解法。

利用双指针来解决,具体过程如下:

1.使用两个指针left和right分别指向容器的左右两个端点,初始化left = 0, right = n - 1。

2.通过左边界height[leftt]右边界height[right]计算出容器的体积v

3.判断左右边界的大小情况,舍去小的边界,若左边界 < 右边界left++反之则right--

4.通过重复上述过程,可以舍去大量不必要的枚举过程直到left与right相遇,整个过程中更新出容器体积的最大值。

证明以上的第三点,为什么不用小的边界去枚举剩下的数,而是直接将其舍去

C++代码实现

class Solution {
public:
    int maxArea(vector<int>& height) {
        int left = 0, right = height.size() - 1;
        int ret = 0;
        while(left < right)
        {
            int v = min(height[left], height[right]) * (right - left);
            ret = max(v, ret);
            height[left] < height[right] ? left++ : right--;
        }
        return ret;
    }
};

时间复杂度:O(n)

空间复杂度:O(1)


拜拜,下期再见😏

摸鱼ing😴✨🎞

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

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

相关文章

VS2022项目配置笔记

文章目录 $(ProjectDir&#xff09;与 $(SolutionDir) 宏附加包含目录VC目录和C/C的区别 $(ProjectDir&#xff09;与 $(SolutionDir) 宏 假设有一个解决方案 MySolution&#xff0c;其中包含两个项目 ProjectA 和 ProjectB&#xff0c;目录结构如下&#xff1a; C:\Projects\…

万字长文解读机器学习——决策树

&#x1f33a;历史文章列表&#x1f33a; 机器学习——损失函数、代价函数、KL散度机器学习——特征工程、正则化、强化学习机器学习——常见算法汇总机器学习——感知机、MLP、SVM机器学习——KNN机器学习——贝叶斯机器学习——决策树机器学习——随机森林、Bagging、Boostin…

Kotlin约束泛型参数必须继承自某个父类

Kotlin约束泛型参数必须继承自某个父类 open class SuperData { }class DataA : SuperData {constructor() {println("DataA constructor")} }class DataB : SuperData {constructor() {println("DataB constructor")} }fun <T : SuperData> myfun(p…

Spring挖掘: (事务篇)

谈到事务,我们就绕不开事务的ACID四大特性,我们先来简单介绍一下何为事务 一. 概念 事务是数据库操作的最小工作单元&#xff0c;作为单个逻辑工作单元执行的一系列操作。这些操作作为一个整体一起向系统提交&#xff0c;要么都执行、要么都不执行。事务是一组不可再分割的操作…

Information Server 中共享开源服务中 kafka 的__consumer_offsets目录过大清理

1,11.7新版本的 IBM InfoSphere Information Server 将 Apache Kafka、Solr 和 ZooKeeper 安装在<ISHOME>服务层的 /shared-open-source 目录中。 默认情况下&#xff0c;共享开源中的 Kafka 对 __consumer_offsets 主题使用“压缩”日志清理策略&#xff0c;这可能会导…

Linux系统程序设计--2. 文件I/O

文件I/O 标准C的I/O FILE结构体 下面只列出了5个成员 可以观察到&#xff0c;有些函数没有FILE类型的结构体指针例如printf主要是一些标准输出&#xff0c;因为其内部用到了stdin&#xff0c;stdout&#xff0c;stderr查找文件所在的位置:find \ -name stat.h查找头文件所…

linux网络的基本设置

1、查看网络接口信息 ip a/ip addr #简略的查看网络接口信息 ifconfig #只显示当前活跃的设备 ifconfig -a #实现当前主机的所有网络设备&#xff0c;包括未运行的设备 rootubuntu1:~# ifconfig ens33:flags4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 设备名…

利用AI制作《职业生涯规划PPT》,10分钟完成

职业生涯规划是大学生活中非常重要的一环。通过制定职业规划&#xff0c;你能够明确未来的职业目标、认清自身的优劣势&#xff0c;进而制定切实可行的计划&#xff0c;以便顺利踏上职业发展的道路。而制作一份精美的职业生涯规划PPT&#xff0c;能有效帮助你在面试、职业规划报…

【数据结构与算法】希尔排序(直接插入排序)

大家好&#xff0c;我是小卡皮巴拉 文章目录 目录 引言 一.直接插入排序的基本思想 二. 直接插入排序算法解析 详细版本的算法思想解析 算法思想提炼 实现代码 画图刨析 三. 直接插入排序的特性 复杂度分析 稳定性分析 四. 希尔排序的基本思想 五. 希尔排序算法解…

RK3568 Android12跳过认证 预置谷歌服务GMS

在Rom开发中需要发布海外版本时基本都需要内置google服务,而规范方式集成的话都需要设备进行认证,获取google应用签名等非常复杂的一套流程,一般大厂才有这些资质和资源,这里介绍一种非常规方式集成GMS,跳过设置认证流程,在RK3568 android12环境亲测有效。 谷歌全家桶中…

深度学习之卷积问题

1 卷积在图像中有什么直观作用 ​ 在卷积神经网络中&#xff0c;卷积常用来提取图像的特征&#xff0c;但不同层次的卷积操作提取到的特征类型是不相同的&#xff0c;特征类型粗分如表1所示。 ​ 表1 卷积提取的特征类型 卷积层次特征类型浅层卷积边缘特征中层卷积局部特征深…

Go语言的内置容器

文章目录 一、数组数组的定义数组声明数组特点数组元素修改 二、切片切片声明基于数组创建切片使用make()函数构造切片使用append()为切片动态添加元素\使用copy()复制新的切片数组与切片相互转换 三、Map映射Map定义使用make()函数创建map用切片作为map的值使用delete()函数删…

二叉树的各种操作补充

二叉树的各种操作补充 求二叉树的结点数求二叉树的叶结点数求二叉树的高度求二叉树的第k层结点数查找指定结点层序遍历判断二叉树是否是完全二叉树 我们任然沿用二叉树的基本信息&#xff1a; typedef char BTDataType; typedef struct BinaryTreeNode {BTDataType _data;struc…

Go语言的常用内置函数

文章目录 一、Strings包字符串处理包定义Strings包的基本用法Strconv包中常用函数 二、Time包三、Math包math包概述使用math包 四、随机数包&#xff08;rand&#xff09; 一、Strings包 字符串处理包定义 Strings包简介&#xff1a; 一般编程语言包含的字符串处理库功能区别…

Perfetto中如何使用SQL语句

在使用 Perfetto 分析 Android 性能时&#xff0c;可以通过 Perfetto 提供的内置 SQL 查询来提取和分析不同的性能数据。Perfetto 允许你在 UI 界面或命令行中运行 SQL 查询&#xff0c;提取出 Trace 数据中包含的各种性能信息&#xff0c;比如 CPU 使用率、线程状态、内存分配…

QML项目实战:自定义TextField

目录 一.添加模块 import QtQuick.Controls 1.2 import QtQuick.Controls.Styles 1.4 import QtGraphicalEffects 1.15 二.自定义TextField 1.属性设置 2.输入框设置 3.按钮开关 三.效果 1.readonly为false 2.readonly为true 四.代码 一.添加模块 import QtQuick.…

【进阶】Stable Diffusion 插件 Controlnet 安装使用教程(图像精准控制)

Stable Diffusion WebUI 的绘画插件 Controlnet 最近更新了 V1.1 版本&#xff0c;发布了 14 个优化模型&#xff0c;并新增了多个预处理器&#xff0c;让它的功能比之前更加好用了&#xff0c;最近几天又连续更新了 3 个新 Reference 预处理器&#xff0c;可以直接根据图像生产…

DAF-FM DA与NO反应后,生成的产物能够发出强烈的绿色荧光,254109-22-3

一、基本信息 产品名称&#xff1a;DAF-FM DA&#xff08;一氧化氮NO荧光探针DAF-FM&#xff09; 英文名称&#xff1a;DAF-FM DA&#xff0c;DAF-FM diacetate CAS号&#xff1a;254109-22-3 分子式&#xff1a;C25H18F2N2O7 供应商&#xff1a;陕西新研博美生物科技 分…

在 Mac 和 Windows 系统中快速部署 OceanBase

OceanBase 是一款分布式数据库&#xff0c;具备出色的性能和高扩展性&#xff0c;可以为企业用户构建稳定可靠、灵活扩展性能的数据库服务。本文以开发者们普遍熟悉的Windows 或 Mac 环境为例&#xff0c;介绍如何快速上手并体验OceanBase。 一、环境准备 1. 硬件准备 OceanB…

使用Ant Design的Layout布局不能撑满整个屏幕问题解决方法

代码示例&#xff1a; import React, { useState } from react import {LaptopOutlined,NotificationOutlined,UserOutlined, } from ant-design/icons import type { MenuProps } from antd import { Layout, Menu, theme } from antd import routes from ./routes/index imp…