数组题目总结 -- 双指针

news2024/11/15 7:21:23

目录

  • ① 快慢指针:
  • 一. 删除有序数组中的重复项
    • 1. 思路和代码
      • I. 博主的做法:
      • II. 东哥的做法:
    • 2. 总结
  • 二. 删除排序链表中的重复元素(扩展)
    • 1. 思路和代码
      • I. 博主的做法:
      • II. 东哥的做法:
    • 2. 总结
  • 三. 移除元素
    • 1. 思路和代码
      • I. 博主的做法:
      • II. 东哥的做法:
    • 2. 总结
  • 四. 移动零
    • 1. 思路和代码
      • I. 博主的做法:
      • II. 东哥的做法:
    • 2. 总结
  • ② 左右指针
  • 一. 二分查找
    • 1. 思路和代码
  • 二. 两数之和 II - 输入有序数组
    • 1. 思路和代码
      • I. 博主的做法:
      • II.东哥的做法:
    • 2. 总结
  • 三. 反转字符串
    • 1. 思路和代码
      • I. 博主的做法:
      • II.东哥的做法:
  • 四. 回文串判断
    • 1. 思路和代码
      • I. 博主的做法:
      • II.东哥的做法:
  • 五. 最长回文子串
    • 1. 思路和代码
      • I. 博主的做法:
      • II.东哥的做法:
    • 2. 总结

① 快慢指针:

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

  • 题目链接:https://leetcode.cn/problems/remove-duplicates-from-sorted-array/

1. 思路和代码

I. 博主的做法:

  • slow充当指针,如果后面的值和slow指向的值不一样,那么就在slow的下一位置保存这个新的值(),同时更新slow指针。这样巧妙的节省了fast这个指针。
class Solution {
    public int removeDuplicates(int[] nums) {

        int slow = 0;

        int length = nums.length;

        for(int i = 1; i < nums.length; i++){
            if(nums[i] == nums[slow])
                length--;
            else
                nums[++slow] = nums[i];
        }
        return length;
    }
}
  • 这里的lenth完全可以不定义,直接返回slow+1就可以,也表示的是数组的长度。
  • 修改后代码为:
class Solution {
    public int removeDuplicates(int[] nums) {
        if(nums.length == 0)
            return 0; 
            
        int slow = 0;

        for(int i = 1; i < nums.length; i++)
            if(nums[i] != nums[slow])
                nums[++slow] = nums[i];

        return slow + 1;
        //return ++slow
    }
}
  • 这里不能return slow++,因为这样slow还没更新,就被返回了。也可以return ++slow。

II. 东哥的做法:

  • 我们让慢指针 slow 走在后面,快指针 fast 走在前面探路,找到一个不重复的元素就赋值给 slow 并让 slow 前进一步。这样,就保证了 nums[0…slow] 都是无重复的元素,当 fast 指针遍历完整个数组 nums 后,nums[0…slow] 就是整个数组去重之后的结果。
  • 这里,fast相当于我思路中循环中的i。
class Solution {
    public int removeDuplicates(int[] nums) {
        if(nums.length == 0)
            return 0; 

        int fast = 0;
        int slow = 0;

        while(fast < nums.length){
            if(nums[fast] != nums[slow])
                nums[++slow] = nums[fast];
            fast++;
        }
        return slow + 1;
    }

}

2. 总结

  • 简单的快慢指针问题,没什么可说的。

二. 删除排序链表中的重复元素(扩展)

  • 题目链接:https://leetcode.cn/problems/remove-duplicates-from-sorted-list/

1. 思路和代码

I. 博主的做法:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        if(head == null)
            return null;
            
        ListNode slow = head;

        for(ListNode i = slow; i != null; i = i.next)
            if(slow.val != i.val){
                slow.next = i;
                slow = slow.next;
            }

        
        slow.next = null;
        return head;
    }
}

II. 东哥的做法:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
   public ListNode deleteDuplicates(ListNode head) {
        if (head == null) return null;
        ListNode slow = head, fast = head;
        while (fast != null) {
            if (fast.val != slow.val) {
                // nums[slow] = nums[fast];
                slow.next = fast;
                // slow++;
                slow = slow.next;
            }
            // fast++
            fast = fast.next;
        }
        // 断开与后面重复元素的连接
        slow.next = null;
        return head;
    }
}
  • 这里可能有读者会问,链表中那些重复的元素并没有被删掉,就让这些节点在链表上挂着,合适吗?

    • 这就要探讨不同语言的特性了,像 Java/Python 这类带有垃圾回收的语言,可以帮我们自动找到并回收这些「悬空」的链表节点的内存,而像 C++ 这类语言没有自动垃圾回收的机制,确实需要我们编写代码时手动释放掉这些节点的内存。

2. 总结

  • 思路和数组的差不多,不再赘述。

三. 移除元素

  • 题目链接:https://leetcode.cn/problems/remove-element/

1. 思路和代码

I. 博主的做法:

  • 若寻找的元素和需要删除的值相等:
    • 并且这个元素是第一个与删除值相等的元素,那么用temp保存此元素下标;否则,直接跳过这个元素。
  • 不相等:
    • 用后面的元素依次覆盖前面的元素,达到删除的目的。
class Solution {
    public int removeElement(int[] nums, int val) {
        if(nums.length == 0)
            return 0;

        int temp = 0;
        boolean flag = false;

        for(int i = 0; i < nums.length; i++){
            if(nums[i] == val){
                if(flag == false){
                    temp = i;
                    flag = true;
                }
                else continue;
            }
            else
                nums[temp++] = nums[i];


        }

        return temp;
    }

}
  • else continue;这一行可以优化掉。
  • 想了想,flag也可以给它优化掉,直接用temp代替,本质上就是初值设定的比较离谱(只要是负数都可以),让temp充当flag的角色。修改后,代码如下:
class Solution {
    public int removeElement(int[] nums, int val) {
        if(nums.length == 0)
            return 0;

        int temp = -1;

        for(int i = 0; i < nums.length; i++){
            if(nums[i] == val && temp == -1)
                temp = i;
            else if(nums[i] != val)
                nums[temp++] = nums[i];
        }

        return temp;
    }

}

II. 东哥的做法:

  • 如果 fast 遇到值为 val 的元素,则直接跳过,否则就赋值给 slow 指针,并让 slow 前进一步。
class Solution {
    public int removeElement(int[] nums, int val) {
        if(nums.length == 0)
            return 0;

        int slow = 0;

        for(int i = 0; i < nums.length; i++){
            if(nums[i] != val)
                nums[slow++] = nums[i];
        }

        return slow;
    }

}
class Solution {
    public int removeElement(int[] nums, int val) {
        if(nums.length == 0)
            return 0;

        int slow = 0;
        int fast = 0;

        while(fast < nums.length){
            if(nums[fast] != val)
                nums[slow++] = nums[fast];
            fast++;
        }

        return slow;
    }

}
  • 注意这里和有序数组去重的解法有一个细节差异,我们这里是先给 nums[slow] 赋值然后再给 slow++,这样可以保证 nums[0…slow-1] 是不包含值为 val 的元素的,最后的结果数组长度就是 slow。

2. 总结

  • 这题和上面那个思路差不多,是我想复杂了。东哥的思路有点像是把原来的数组当成了一个新数组,用fast进行遍历,如果不等于val,就把这个元素添加到这个新的数组中。这样想,就完全不需要知道这个元素是不是第一个等于val的元素。牛逼plus!

四. 移动零

  • 题目链接:https://leetcode.cn/problems/move-zeroes/

1. 思路和代码

I. 博主的做法:

  • 本质上就是删除0,然后在数组的末尾去补0。
class Solution {
    public void moveZeroes(int[] nums) {
        if(nums.length == 0)
            System.out.print(0);
        
        int slow = 0;

        for(int i = 0; i < nums.length; i++){
            if(nums[i] != 0)
                nums[slow++] = nums[i];
        }
        for(int i = slow; i < nums.length; i++)
            nums[i] = 0;
        
        for(int i = 0; i < nums.length; i++){
            if(i == nums.length - 1)
                System.out.print(nums[i]);
            else
                System.out.print(nums[i] + ",");
        }

    }
}
  • 本来想着,是不是可以不改变数组,直接通过打印AC这道题呢,写了如下代码:
 class Solution {
     public static void moveZeroes(int[] nums) {
        if(nums.length == 0)
            System.out.print(0);

        int count = 0;

        for(int i = 0; i < nums.length; i++){
            if(nums[i] != 0){
                count++;

                System.out.print(nums[i]);
                if(count != nums.length)
                    System.out.print(",");
            }

        }

        for(int i = count; i < nums.length; i++){
            System.out.print(0);

            if(i != nums.length - 1)
                System.out.print(",");
        }
    }
}

结果:在这里插入图片描述
看来leetcode的输出,是遍历了这个数组,氧化钙。

II. 东哥的做法:

class Solution {
    public static int removeElement(int[] nums, int val) {
        if(nums.length == 0)
            return 0;

        int slow = 0;
        int fast = 0;

        while(fast < nums.length){
            if(nums[fast] != val)
                nums[slow++] = nums[fast];
            fast++;
        }

        return slow;
    }
    public static void moveZeroes(int[] nums) {
         // 去除 nums 中的所有 0,返回不含 0 的数组长度
        int p = removeElement(nums, 0);
        // 将 nums[p..] 的元素赋值为 0
        for (; p < nums.length; p++) {
            nums[p] = 0;
        }

    }
}

  • 题目让我们将所有 0 移到最后,其实就相当于移除 nums 中的所有 0,然后再把后面的元素都赋值为 0 即可。

2. 总结

  • 东哥的思路和博主是一样的,我的代码中的count就相当于slow指针。要像东哥一样,擅于去进行题目的整合。

② 左右指针

一. 二分查找

  • 无。函数名为:public static int binarySearch(int[] nums, int target);

1. 思路和代码

int binarySearch(int[] nums, int target) {
    // 一左一右两个指针相向而行
    int left = 0, right = nums.length - 1;
    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;
}

  • “=”存在的意义:如果数组元素只有一个的话,就存在left == right的情况

二. 两数之和 II - 输入有序数组

  • 题目链接:https://leetcode.cn/problems/two-sum-ii-input-array-is-sorted/

1. 思路和代码

I. 博主的做法:

  • 先暴力一波,有点像是握手问题。
class Solution {
    public int[] twoSum(int[] numbers, int target) {

        int[] res = new int[2];

        for(int i = 0; i < numbers.length; i++){
            for(int j = i + 1; j < numbers.length; j++){
                if(numbers[i] + numbers[j] == target){
                    res[0] = i + 1;
                    res[1] = j + 1;

                    return res;
                }

            }
        }
        return res;
    }
}

II.东哥的做法:

  • 只要数组有序,就应该想到双指针技巧。这道题的解法有点类似二分查找,通过调节 left 和 right 就可以调整 sum 的大小。
class Solution {
    public int[] twoSum(int[] numbers, int target) {

        int left = 0;
        int right = numbers.length - 1;

        while(left < right){
            if(numbers[left] + numbers[right] == target)
                return new int[]{left + 1, right + 1};
            else if(numbers[left] + numbers[right] > target)
                right--;
            else 
                left++;
        }
        return new int[]{-1, -1};
    }
}
  • 题目给的是一个有序数组,如果两个元素的和 > target,明显是右边的元素太大了,也就是说要缩小一些;如果是两个元素的和 < target,那就是左边的元素太小了,要放大一些。

2. 总结

  • 只要数组有序,就应该想到双指针技巧!!!

三. 反转字符串

  • 题目链接:https://leetcode.cn/problems/reverse-string/

1. 思路和代码

I. 博主的做法:

  • 很简单嘛,就是整一个临时变量temp保存字符,然后互换位置。
class Solution {
    public void reverseString(char[] s) {
        int left = 0;
        int right = s.length - 1;
        char temp;

        while(left < right){
            temp = s[left];
            s[left] = s[right];
            s[right] = temp;

            left++;
            right--;
        }
    }
}

II.东哥的做法:

  • 和博主想的一样。

四. 回文串判断

  • 无。函数名为:public static boolean isPalindrome(String s);

1. 思路和代码

I. 博主的做法:

    public static boolean isPalindrome(String s){
        int left = 0;
        int right = s.length() - 1;

        while(left < right){
            if(s.charAt(left) != s.charAt(right))
                return false;
            left++;
            right--;
        }
        return true;
    }
  • 和上一个交换数组基本上一模一样,换汤不换药。

II.东哥的做法:

  • 和博主想的一样。

五. 最长回文子串

  • 题目链接:https://leetcode.cn/problems/longest-palindromic-substring/

1. 思路和代码

I. 博主的做法:

  • 复用了上面判断是否为回文数的算法。本质上还是暴力,使用substring方法,从最长的长度开始构建字符串。
  • 先固定子字符串的长度,然后不断的向右移动字符串的起点,依次对得到的子字符串进行判断,若是回文串,直接进行输出。
class Solution {
    public static boolean isPalindrome(String s){
        int left = 0;
        int right = s.length() - 1;

        while(left < right){
            if(s.charAt(left) != s.charAt(right))
                return false;
            left++;
            right--;
        }
        return true;
    }
    public String longestPalindrome(String s) {
        int len = s.length();
        String str;

        while(len > 0){
            for(int i = 0; i < s.length(); i++){
                if(i + len <= s.length()){
                    str = s.substring(i, i + len);
                    if(isPalindrome(str) == true)
                        return str;
                }

            }
            len--;
        }
        return "-1";
    }
}

II.东哥的做法:

for 0 <= i < len(s):
    找到以 s[i] 为中心的回文串
    找到以 s[i] 和 s[i+1] 为中心的回文串
    更新答案

class Solution {
    public static String Palindrome(String s, int left, int right){
        while(left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)){
        // 双指针,向两边展开
            left--;
            right++;
        }

        return s.substring(left + 1, right);
    }
    public String longestPalindrome(String s) {
        String res = "";
        
        for(int i = 0; i < s.length(); i++){
        	//以 s[i] 为中心的最长回文子串
            String str1 = Palindrome(s, i, i);
            // 以 s[i] s[i+1] 为中心的最长回文子串
            String str2 = Palindrome(s, i, i + 1);

            res = res.length() < str1.length() ? str1 : res;
            res = res.length() < str2.length() ? str2 : res; 
        }

        return res;
    }
}
  • 先开始,我还以为代码有问题,就是Palindrome()这个函数的返回值为s.substring(left + 1, right);
  • eg:“ 121 ”
    • 以2为中心,扩大回文子串的时候,出了while循环,left < 0 && right == s.length,由于substring的特性,所以应该这么写,完全正确!
    • “ 12 ”这个子串不是回文序列,所以left+1,返回的串就是 “ 2 ”
    • “ 1 ”这个子串是回文序列,出了while,left < 0 越界了,left + 1,返回的子串就是“ 1 ”

2. 总结

  • 东哥这个思路真有意思,回文子串就是从中心向两边进行发散,这个思路可以积累。

参考:https://labuladong.github.io/algo/di-yi-zhan-da78c/shou-ba-sh-48c1d/shuang-zhi-fa4bd/

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

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

相关文章

硬件语言Verilog HDL牛客刷题day08 综合部分

1. Johnson Counter 1.题目&#xff1a; 请用Verilog实现4位约翰逊计数器&#xff08;扭环形计数器&#xff09;&#xff0c;计数器的循环状态如下。 电路的接口如下图所示 2.解题思路 2.1 一个简单的状态机的配置。 2.2 注意 起始状态 是 0000 就行 3.解题代码 timescale …

动态内存管理——C语言【进阶】(下)

作者简介&#xff1a; 辭七七&#xff0c;目前大一&#xff0c;正在学习C/C&#xff0c;Java&#xff0c;Python等 作者主页&#xff1a; 七七的个人主页 文章收录专栏&#xff1a;进阶C语言&#xff0c;本专栏主要讲解数据存储&#xff0c;进阶指针&#xff0c;动态内存管理&a…

前端面试题 - 计算机网络与浏览器相关

系列文章目录 vue常见面试题总结 htmlcss 面试题总结附答案 初级前端面试题总结&#xff08;html, css, js, ajax&#xff0c;http&#xff09; js基础面试题整理(包含ES5&#xff0c;ES6) 文章目录 系列文章目录一、网络协议相关1. 从浏览器地址栏输入url到显示页面的步骤…

蓝桥杯欲伸手CTF?有多远爬多远

注意&#xff1a;网络安全类比赛 或者说 CTF 参赛不会需要任何费用 只有国赛/省赛有可能会收取一定运维费用 其他比赛都不会收费 望周知。 先来看个特离谱的事情 早上起床看到几位师傅的朋友圈一脸懵&#xff0c;再仔细一看&#xff0c;好嘛。。。。。。 先看看探姬的回复 接下…

人工智能大模型多场景应用原理解析

前言 在上篇文章《人工智能大模型之ChatGPT原理解析》中分享了一些大模型之ChatGPT的核心原理后&#xff0c;收到大量读者的反馈&#xff0c;诸如:在了解了核心原理后想进一步了解未来的发展趋势(比如生成式人工智能和元宇宙能擦出什么样的火花&#xff1f;)&#xff0c;大模型…

伪命题之MYSQL分库分表

看到使用分库分表来解决性能问题的时候心里总是不能太理解。 如果同事发生大量请求的时候&#xff0c;损害性能的是硬盘的随机读。那么分库分表也没有对性能的瓶颈进行“分治”啊。 应该的做法是使用一块新的硬盘来创建分库。但是基本的文章都没有提到这点。而且基本上也不会有…

价值导向型研发管理数字化建设方案——易趋亮相CCTI中国研发管理峰会

4月15日-16日&#xff0c;由光环国际举办的2023 CCTI中国研发管理峰会在北京中关村国家自主创新示范区会议中心成功举办。 &#xff08;现场签到处&#xff09; 此次峰会邀请了20余位来自腾讯、华为、网易、阿里云等知名企业的研发管理领域专家&#xff0c;带来最新前沿知识和内…

【网络安全知识体系】外网渗透测试 | 14个小实验

写在前面&#xff1a;视频地址 成功上岸360&#xff01;0基础网络安全 入行 到 入yu、漏洞挖掘-外网渗透测试流程目录 一、导读&#xff1a; 二、汇总&#xff1a; 三、知识导图 四、面试常见问题 五、渗透测试流程 1、简述&#xff1a; 2、寻找测试目标 3、信息收集 …

webgl-简单动画

html <!DOCTYPE html> <head> <style> *{ margin: 0px; padding: 0px; } </style> </head> <body> <canvas id webgl> 您的浏览器不支持HTML5,请更换浏览器 </canvas> <script src"./main.js"></script&g…

程序员必用的6个代码对比神器附下载地址

一、WinMerge WinMerge是一款基于Windows系统下的文件比较和合并工具&#xff0c;使用它可以非常方便地比较多个文档内容&#xff0c;适合程序员或经常需要撰写文稿的朋友使用。WinMerge会将两个文件内容做对比&#xff0c;并在相异之处以高亮度的方式显示&#xff0c;让使用者…

认识网络随机丢包

考虑一根漏水的管子&#xff0c;希望出水口接到和不漏的管子等量的水&#xff0c;要么靠时间&#xff0c;反复将漏掉的水重新注入&#xff0c;直到漏掉的水可忽略&#xff1a; ​ 要么靠空间&#xff0c;在漏的地方将管子加粗&#xff0c;一次性注入更多的水&#xff1a; 不…

什么是HTTP? HTTP和HTTPS的区别?

目录 1、什么是HTTP? 2、HTTP的特点 ① 支持客户/服务器模式 ② 简单快速 ③ 灵活 ④ 无连接 ⑤ 无状态 3、HTTPS 4、HTTP和HTTPS的区别 ① 安全性不同 ② 默认端口不同 ③ 响应速度和资源消耗不同 ④ 网站申请流程不同 ⑤ 对搜索排名的提升不同 1、什么是…

基于OpenCV的硬币面值识别

本项目通过Python与Opencv结合数字图像处理技术对&#xff11;元、&#xff15;角、&#xff11;角三种硬币进行识别。首先通过Canny算子对图像进行边缘检测&#xff0c;然后进一步调用定义的函数去除边缘检测后图像中的孤立点&#xff0c;对处理后的图像进行Hough变换检测圆曲…

涨点技巧: 谷歌强势推出优化器Lion,引入到Yolov5/Yolov7,内存更小、效率更高,秒杀Adam(W)

1.Lion优化器介绍 论文:https://arxiv.org/abs/2302.06675 代码:automl/lion at master google/automl GitHub 1.1 简单、内存高效、运行速度更快 1)与 AdamW 和各种自适应优化器需要同时保存一阶和二阶矩相比,Lion 只需要动量,将额外的内存占用减半; 2)由于 Lion…

港联证券|机器视觉迎重磅利好,5只概念股获两路资金加持

现在我国机器视觉在工业场景中的全体渗透率仍旧在10%以下&#xff0c;比照工业场景巨大的体量而言&#xff0c;机器视觉职业仍有较大开展空间。 近来*ST中潜收到广东证监局行政监管措施决定书。依据深交所的相关要求&#xff0c;公司应当在2023年1月31日前发表公司股票或许被停…

windows安装opencv-python(opencv-python源码安装)

因为我要开启opencv的GStreamer功能&#xff0c;这是和ffmpeg相类似的对视频流操作的一个功能&#xff0c;默认没有开启&#xff0c;需要手动编译。 安装方式链接: opencv-install-with-GStreamer 核心内容如下: git clone --recursive https://github.com/skvark/opencv-py…

数据结构_第十二关:二叉树的基础OJ题练习

目录 1.单值二叉树 2.相同的树 3.另一棵树的子树 4.反转二叉树 5.对称二叉树 6.二叉树的结构及遍历 扩展&#xff1a;如何判断是不是完全二叉树、二叉树的销毁 1&#xff09;判断是不是完全二叉树 2&#xff09;二叉树的销毁 1.单值二叉树 OJ题链接https://leetcode.…

传输协议特点大比拼之UDP

文章目录 前言一.UDP协议端的格式源端口号和目的端口号报文长度校验和 二.UDP的特点无连接面向数据报不可靠缓冲区 前言 本文将比较两种主要的传输协议 ,UDP的特点&#xff0c;以帮助读者更好地理解它们的应用场景和优缺点。 一.UDP协议端的格式 大家先来看一些UDP的报文格式…

企业应用程序单点登录

企业每天都依赖于各种企业应用程序&#xff0c;包括云和本地应用程序。这意味着用户必须经常输入更多密码才能访问这些应用程序并完成他们的工作。为了提高用户的工作效率、减少密码疲劳并使身份管理更有效&#xff0c;您的组织需要部署高效的 SSO 解决方案。 AD360 提供企业 …

[STM32F103C8T6]ADC转换

什么是ADC转换&#xff1f; ADC转换的全称是&#xff1a;Analog-to-Digital Converter&#xff0c;指模拟/数字转换器 ADC的性能指标&#xff1a; ADC分辨率&#xff1a; SSA与VREF-一起接到地&#xff0c;DDA与VREF接到3.3v&#xff0c;所以ADC转换的范围是0---3.3v 所以最后的…