算法通关村第3关【白银】| 双指针思想

news2024/12/26 10:44:22

1. 双指针思想

双指针不仅指两个指针,也可以是两个变量,指向两个值。

有三种类型:

  • 快慢型:一前一后
  • 对撞型:从两端向中间靠拢
  • 背向型:从中间向两端分开

2. 删除元素专题

2.1原地移除元素

(1)快慢指针

思路:每次找到等于val就移动数组当val值比较多的时候时间复杂度太高,使用fast指针扫描等于val的值连续删除,当找到一段连续val中第一个不等于val的值的时候就覆盖实现批量删除。

class Solution {
    public int removeElement(int[] nums, int val) {
        int slow = 0;
        int fast = 0;
        int len = nums.length;
        while(fast < nums.length){
            if(nums[fast] == val){
                fast++;
                len--;
            }else{
                nums[slow++] = nums[fast++];
            }
        }
        return len;
    }
}

 (2)对撞指针

思路:两个指针一左一右,题目没有规定元素顺序,故当左指针指向要删除的值的时候就将右指针的值替换上来,右指针左移一位,实现删除一位的效果。

class Solution {
    public int removeElement(int[] nums, int val) {
        int right = nums.length - 1;
        int left = 0;
        int len = nums.length;
        while(left <= right){
            if(nums[left] != val){
                left++;
            }else{
                nums[left] = nums[right--];
                len--;
            }
        }
        return len;
    }
}

2. 2删除有序数组中的重复项

 思路:双指针,当一直重复fast指针就一直往后扫,直到扫到不重复的值,然后将不重复的值覆盖slow指向的重复的值,注意slow初始化为1,这样第一次进循环一定相等。

两个关键点:slow保留指针什么时候移动、fast指向怎么算符合条件的值

此处slow指针当元素开始不重复,即fast指向新值的时候移动

nums[fast] != nums[fast- 1]代表指向新值

class Solution {
    public int removeDuplicates(int[] nums) {
        int slow = 1;
        int fast = 1;
        while(fast < nums.length){
            if(nums[fast] != nums[fast- 1]){
                nums[slow++] = nums[fast];
            }
            fast++;
           
        }
        return slow;
    }
}

当删除使元素出现k个重复:

前k个不需要管,slow表示需要保留的值

当且仅当 nums[slow−k]=nums[fast]时,当前待检查元素 nums[fast]不应该被保留

(因为此时必然有 nums[slow−k]=nums[slow−(k-1)]=...=nums[fast])

例如:int[] arr = new int[]{1,1,1,2, 2,3,4,5,5,6,7,8,8,8,8,9};

此处slow指针当重复的元素不超过k个时移动

nums[fast] != nums[slow - k]代表fast指向的值在重复的k个元素范围之内

public static int removeDuplicatesk(int[] nums,int k) {
        int slow = k;
        int fast = k;
        while(fast < nums.length){
            if(nums[fast] != nums[slow - k]){
                nums[slow] = nums[fast];
                slow++;
            }
            fast++;
        }
        return slow;
    }

扩展:出现重复的元素都不要

此处slow指针当前后元素都只出现一次时移动

nums[fast] == nums[fast -1]判断元素是否重复了

public static int removeDuplicates0(int[] nums,int k) {
        int slow = 0;
        int fast = 1;
        int count = 0;//用来判断元素是否出现重复
        while(fast < nums.length){
            if (nums[fast] == nums[fast -1]){
                count++;
            } else if(nums[fast] != nums[fast -1]&&count!=0){
                //当元素出现不重复并且fast指向的是新值
                nums[slow] = nums[fast];
                //开始新的值重置
                count = 0;
            } else if (nums[fast] != nums[fast -1]&&count == 0){
                //当元素不重复,并且fast指向前也是不重复的值
                //这时slow保留指针才移动
                nums[++slow] = nums[fast];
            }
            fast++;
        }
        return count == 0 ? slow+1 : slow;
    }

3.元素奇偶移动

思路:对撞型双指针,当left指到奇数,right指到偶数就相互交换。需要注意一些边界处理细节,避免数组越界情况。

class Solution {
    public int[] sortArrayByParity(int[] nums) {
        int left = 0;
        int right = nums.length - 1;
        while(left<right){
            while(left<right && nums[left] % 2 == 0){
                left++;
            }
            while(left<right && nums[right] % 2 != 0){
                right--;
            }
            if(left<right){
                int t = nums[left];
                nums[left] = nums[right];
                nums[right] = t;
                left++;
                right--;
            }
            
        }
        return nums;
    }
}

4.数组轮转

思路:

方法一、暴力解法,找到开始轮转的数,然后剪切拼接到新的数组。需要注意k可能比数组长度大得取模

class Solution {
    public void rotate(int[] nums, int k) {
        int[] res = new int[nums.length];
        int left = 0;
        int right = nums.length;
        int kk = k;
        while( k % nums.length>0){
            right--;
            k--;
        }
        int i = 0;
        for(;i<kk%nums.length;i++){
            res[i] = nums[right%nums.length];
            right++;
        }
        for(;i<nums.length;i++){
            res[i] = nums[left++];
        }
        System.arraycopy(res, 0, nums, 0, nums.length);
    }
}

 优化:i+k将当前元素往右移动k,取模避免数组越界

class Solution {
    public void rotate(int[] nums, int k) {
        int n = nums.length;
        int[] newArr = new int[n];
        for (int i = 0; i < n; ++i) {
            newArr[(i + k) % n] = nums[i];
        }
        System.arraycopy(newArr, 0, nums, 0, n);
    }
}

方法二、翻转,往右轮转结果就是数组后面的值移动到了前面,进行一次整体翻转,再在k处分成左右两部分再次翻转就是往右轮转的最终效果了。

class Solution {
    public void rotate(int[] nums, int k) {
        revers(nums,0,nums.length-1);
        revers(nums,0,(k%nums.length)-1);
        revers(nums,(k%nums.length),nums.length-1);
    }

    public void revers(int[] nums,int l,int r){
        while(l<r){
            int t = nums[l];
            nums[l] = nums[r];
            nums[r] = t;
            l++;
            r--;
        }
    }
}

5.数组的区间

 思路:题目的意思是说不连续递增的就断开输出

双指针,slow指向区间起始,fast负责寻找到区间结束

class Solution {
    public List<String> summaryRanges(int[] nums) {
        List<String> res = new ArrayList<>();
        int slow = 0;
        int fast = 0;
        while(fast<nums.length){
            if(fast + 1 == nums.length || nums[fast] + 1 != nums[fast+1]){
                if(slow == fast){
                    res.add("" + nums[slow]);
                }else{
                    res.add(nums[slow] + "->" + nums[fast]);
                }
                slow = fast+1;
            }
            fast++;
        }
        return res;
    }
}

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

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

相关文章

我的创作纪念日(128天)

机缘 CSDN账号创建已有3年了&#xff0c;本篇是第一篇纪念文。。。有点偷懒的感觉了。。。 从第一篇文章的发布&#xff0c;到现在已经过了128天了&#xff0c;回想起当时发布文章的原因&#xff0c;仅仅只是因为找不到合适的云笔记&#xff0c;鬼使神差的想到了CSDN&#xff…

第十一课:Qt 快捷键大全

功能描述&#xff1a;Qt 中的快捷键查看方式和自定义快捷键 一、快捷键查看/自定义 Qt Creator 中提供了各种快捷键&#xff0c;如需查看或自定义快捷键&#xff0c;选择菜单栏“工具” -> “选项” -> “环境” -> “键盘”。 快捷键按类别列出&#xff0c;可以在过…

Windows 11 + Ubuntu20.04 双系统 坑里爬起来

ThinkPad x390 安装双系统&#xff0c;原有的磁盘太小&#xff0c;扩充了磁盘重新装系统&#xff0c;出现的问题&#xff0c;加以记录。 1. windows和ubuntu谁先安装&#xff0c;两个都可以&#xff0c;一般建议先安装windows&#xff0c;后安装ubuntu 2. 安装windows后&…

小O网兜0231新版 -- 用户入门指南

本文介绍小O网兜入门功能&#xff0c;通过本文用户能够掌握数据采集的基本操作&#xff0c;使用软件提供的模板任务采集指定页面的数据。 基本概念 任务文件&#xff1a;新建任务文件&#xff0c;扩展名为 xop&#xff0c;任务的配置、采集数据等信息保存在该文件中&#xff…

Android进阶之路 - 去除EditText内边距

正如题名&#xff0c;在Android中的EditText是自带内边距的&#xff0c;常规而言设置背景为null即可&#xff0c;但是因为使用了并不熟悉的声明式框架&#xff0c;本是几分钟解决的事儿&#xff0c;却花费了小半天~ 其实这只是一个很简单的小需求&#xff0c;不想却遇到了一些小…

WIN+ALT+R无法开始录制

winr打开注册表regedit 依次展开 计算机\HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\GameDVR 修改AppCaptureEnabled数值为1 wing打开 Xbox Game Bar点击捕获 WINALTR开始录制

Zabbix技术分享——Proxy加密代理:共享密钥(PSK)加密与证书加密

一、加密介绍 Zabbix版本从3.0之后&#xff0c;开始支持Zabbix server, Zabbix proxy, Zabbix agent, zabbix_sender and zabbix_get之间的通信加密&#xff0c;加密方式有预共享密钥(PSK)和证书加密&#xff0c;加密配置是可选项&#xff0c;一些proxy和agent可以使用证书认证…

PHP-MD5注入

0x00 前言 有些零散的知识未曾关注过&#xff0c;偶然捡起反而更加欢喜。 0x01 md5 注入绕过 md5函数有两个参数&#xff0c;第一个参数是要进行md5的值&#xff0c;第二个值默认为false&#xff0c;如果为true则返回16位原始二进制格式的字符串。意思就是会将md5后的结果当…

网络

mcq Java 传输层&#xff1a;拆分和组装&#xff0c;完成端到端的消息传递&#xff0c;流量控制&#xff0c;差错控制等 网络层&#xff1a; 寻址、路由&#xff0c;复用&#xff0c;拥塞控制&#xff0c;完成源到宿的传递。 显然A选项是错误的&#xff0c;有流量控制的是传输层…

谷歌推出首款量子弹性 FIDO2 安全密钥

谷歌在本周二宣布推出首个量子弹性 FIDO2 安全密钥&#xff0c;作为其 OpenSK 安全密钥计划的一部分。 Elie Bursztein和Fabian Kaczmarczyck表示&#xff1a;这一开源硬件优化的实现采用了一种新颖的ECC/Dilithium混合签名模式&#xff0c;它结合了ECC抵御标准攻击的安全性和…

MySQL的安装以及卸载

下载官网 https://www.mysql.com/ 切到下载tab页 找到 MySQL Community Server 或者 MySQL Community (GPL) Downloads --> MySQL Community Server 点击download按钮&#xff1a; 点击download进入下载页面选择No thanks, just start my download就可以开始下载了。 下…

sqlserver数据库导出到mysql

爱到分才显珍贵&#xff0c;很多人都不懂珍惜拥有&#xff0c;只到失去才看到&#xff0c;其实那最熟悉的才最珍贵的。 这里只介绍一种方式&#xff0c;有很多的方式。 1.使用Navicat 安装 下载 2.工具 数据传输 3.选择源和目标 然后开始 4.最好导入前备份一下库

C++学习系列之动态库报错问题

C学习系列之动态库报错问题 啰嗦问题解决总结 啰嗦 动态库已建&#xff0c;C文件一加&#xff0c;全是报错&#xff0c;一片红。 问题 解决 解决办法就是加标头 总结 小问题&#xff0c;记录一下。

海外网红营销:指标选择与ROI评估,量化推广效果的关键

随着互联网的快速发展&#xff0c;海外网红营销已经成为了品牌推广和营销的重要策略之一。网红作为社交媒体平台上备受关注的个体&#xff0c;拥有庞大的粉丝群体和影响力&#xff0c;成为了品牌推广的有力助手。然而&#xff0c;如何科学地衡量海外网红营销的效果以及投入产出…

GraphQL strawberry的使用回顾和体会

GraphQL vs RESTful 简单来说GraphQL 比起 RESTful 集成额外一些功能 出入参校验、序列化 (简化后端编程)自由可选的返回数据字段 (简化一些多余接口开发和沟通联调成本) 这些都是优点了。 开发效率在项目初期是很重要的&#xff0c;需要快速原型化。 但是后期稳定后&#…

vscode | 开发神器vscode必会快捷键

目录 一、全局二、注释三、折叠四、光标相关五、选择六、行操作七、格式化八、放大/缩小九、代码缩进十、调整字符大小写十一、窗口和侧边栏十二、快速跳转&#xff08;文件、行、符号&#xff09;十三、其他配置项设置十四、自定义快捷键十五、快捷键大全图 Visual Studio Cod…

Kotlin 基础教程二

constructor 构造器一般情况下可以简化为主构造器 即: class A constructor(参数) : 父类 (参数) 也可以在构造器上直接声明属性constructor ( var name) 这样可以全局访问 init { } 将和成员变量一起初始化 data class 可以简化一些bean类 比如get / set ,自动生成copy 函数…

设计模式之门面模式(Facade)的C++实现

1、门面模式提出 在组件的开发过程中&#xff0c;某些接口之间的依赖是比较紧密的&#xff0c;如果某个接口发生变化&#xff0c;其他的接口也会跟着发生变化&#xff0c;这样的代码违背了代码的设计原则。门面设计模式是在外部客户程序和系统程序之间添加了一层中间接口&…

novnc 和 vnc server 如何实现通信?原理?

参考&#xff1a;https://www.codenong.com/js0f3b351a156c/

Vue-7.命令创建Vue项目

使用预设默认配置创建Vue项目 创建一个简单的 Vue 项目需要使用 Vue CLI&#xff08;命令行界面&#xff09;。Vue CLI 是一个用于快速构建 Vue.js 项目的工具&#xff0c;它可以帮助你设置项目的基本结构、配置以及开发环境。 以下是创建一个简单的 Vue 项目的步骤&#xff…