双指针在数组遍历中的应用

简介: 文章深入探讨了双指针技术在数组遍历中的应用,通过实战例子详细解释了快慢指针和首尾指针的不同用法,并提供了解决LeetCode相关问题的Java代码实现。

算法是计算机软件的基础,今年打算深入学习一些算法,记录一些算法理论以及最佳实践,希望可以坚持下去。

截屏2024-01-11 23.09.34.png

一、前言

数组是最基础的数据结构之一,数组遍历是基本功,本文目的是深入学习双指针法来对数组遍历以及双指针遍历数组算法的实战。

二、双指针介绍

我们一般遍历数组使用一个下标从前往后遍历就可以实现遍历完数组,这整个过程只需要一个下标

截屏2024-01-11 23.26.00.png

那么双指针是怎么样呢? 其实就是使用两个下标对数组进行遍历。

截屏2024-01-11 23.30.31.png

为什么需要双指针呢? 主要为了弥补一些通过单指针遍历数组解决不了的场景。

比如原地移除数组元素。 下面例子需要原地移除元素里的9,通过双指针可以解决,快指针遍历数组元素值,慢指针记录需要留下的元素。但是单指针解决就麻烦。

截屏2024-01-11 23.34.10.png

再比如反转字符串,我们可以通过首尾双指针,通过两个指针同时往中间遍历,交换元素值实现反转功能。

截屏2024-01-11 23.43.00.png

因此双指针有两种类型,一种是前后双指针也可以叫做快慢双指针,一种是首尾双指针

快慢双指针用来从前往后遍历数组。

首尾双指针用来由两端往中间遍历数组。

三、双指针实战

罗列下部分可以通过双指针解决的leetcode题目:

27. 移除元素

344. 反转字符串

151.翻转字符串里的单词

第15题. 三数之和

977.有序数组的平方

本文分析下有序数组的平方

截屏2024-01-11 23.57.01.png

我们可以借助首尾双指针解决此题,因为平方后的数一定在两端。这样我们可以遍历最大,次最大,次次最大,最后是平方值最小的。以此类推。

截屏2024-01-11 23.59.49.png

class Solution {
   
   
    public int[] sortedSquares(int[] nums) {
   
   

        int start = 0; //首指针
        int end = nums.length-1; //尾指针

        int[] result = new int[nums.length];
        int index = nums.length - 1;

        while(start <= end) {
   
   
            if(nums[start] * nums[start] > nums[end] * nums[end]) {
   
   
                result[index--] = nums[start] * nums[start];
                start++;
            } else {
   
   
                result[index--] = nums[end] * nums[end];
                end--;
            }
        }

        return result;

    }
}

leetcode18. 四数之和

class Solution {
   
   
    public List<List<Integer>> fourSum(int[] nums, int target) {
   
   

        List<List<Integer>> result = new ArrayList<>();
        Arrays.sort(nums);
        for(int i=0; i<nums.length; i++) {
   
   

            int a = nums[i];

            if (nums[i] > 0 && nums[i] > target) {
   
   
                return result;
            }

            //需要去重a 2 2 2 2 2 不能用while 死循环了
            if(i > 0 && nums[i-1] == nums[i]) {
   
   
                continue;
            }

            for(int j = i+1; j<nums.length; j++) {
   
   

                int b = nums[j];

                //需要去重b
                if(j > i+1 && nums[j-1] == nums[j]) {
   
   
                    continue;
                }

                int left = j+1;
                int right = nums.length-1;

                //System.out.println(k+"==="+l);

                while(right>left) {
   
   

                    int c = nums[left];
                    int d = nums[right];

                    long sum = (long) nums[i] + nums[j] + nums[left] + nums[right];

                    if(sum == target) {
   
   
                        List<Integer> subResult = new ArrayList<>();
                        subResult.add(a);
                        subResult.add(b);
                        subResult.add(c);
                        subResult.add(d);
                        result.add(subResult);

                        System.out.println(a+"="+b+"="+c+"="+d);

                        while(right>left && nums[right-1] == nums[right]) {
   
   
                            right--;
                        }

                        while(right>left && nums[left+1] == nums[left]) {
   
   
                            left++;
                        }

                        left++;
                        right--;

                    } else if(sum > target) {
   
   
                         right--; 
                         //System.out.println(k+"="+l);  
                    } else if(sum < target) {
   
   
                         left++;
                    }
                }
            }

        }

        return result;

    }
}

四数之和这道题通过双指针法从两端往中间来遍历c,d两个元素。

15. 三数之和

class Solution {
   
   
    //思路 定义一个固定节点 两个起始节点 移动起始节点确定有没有等于0的情况
    public List<List<Integer>> threeSum(int[] nums) {
   
   

        int size = nums.length;
        Arrays.sort(nums);
        List<List<Integer>> result = new ArrayList<>();
        for(int i =0; i<size; i++) {
   
   

            if(nums[i] >0) {
   
   
                return result;
            }
            //去重
            if(i>0 && nums[i] == nums[i-1]) {
   
   
                continue;
            }

            int start = i+1;
            int end = size-1;

            while(start < end) {
   
   

                int r = (nums[start] + nums[end] + nums[i]);
                if(r == 0) {
   
   
                    //找到了符合和等于0的,记录下来
                    List<Integer> subResult = new ArrayList<>();
                    subResult.add(nums[i]);
                    subResult.add(nums[start]);
                    subResult.add(nums[end]);
                    result.add(subResult);
                    //去重复
                    while(start< end && nums[start] == nums[start+1]) {
   
   
                        start = start +1;
                    }
                    //去除重复
                    while(start < end && nums[end] == nums[end-1]) {
   
   
                        end = end-1;
                    }
                    start++;
                    end--;
                    continue;
                } 
                //和大于0,右边的数太大,因此右边的索引要往左移
                if( r > 0) {
   
   
                    end--;
                    continue;
                }
                //和小于0,左边的数太小,因此左边的索引往右移
                if( r < 0) {
   
   
                   start++;
                }
            }
        }
        return result;
    }
}

三数之和和四数之和是一样的技巧,通过从两端往中间遍历确定b,d的值。

四、总结

本文主要分析了有两种双指针形态,首尾指针快慢指针。双指针遍历数组可以提高遍历数组算法性能提升。

把双指针算法常见形态和场景记录下来,我相信有输入就需要有输出,希望对以后的编码有帮助。

关注我一起学习算法技巧吧。

相关文章
|
1月前
使用指针访问数组元素
【10月更文挑战第30天】使用指针访问数组元素。
37 3
|
23天前
|
存储 程序员 编译器
C 语言数组与指针的深度剖析与应用
在C语言中,数组与指针是核心概念,二者既独立又紧密相连。数组是在连续内存中存储相同类型数据的结构,而指针则存储内存地址,二者结合可在数据处理、函数传参等方面发挥巨大作用。掌握它们的特性和关系,对于优化程序性能、灵活处理数据结构至关重要。
|
25天前
|
自然语言处理 前端开发 JavaScript
深入理解前端中的 “this” 指针:从基础概念到复杂应用
本文全面解析前端开发中“this”指针的运用,从基本概念入手,逐步探讨其在不同场景下的表现与应用技巧,帮助开发者深入理解并灵活掌握“this”的使用。
|
27天前
|
存储 C语言 计算机视觉
在C语言中指针数组和数组指针在动态内存分配中的应用
在C语言中,指针数组和数组指针均可用于动态内存分配。指针数组是数组的每个元素都是指针,可用于指向多个动态分配的内存块;数组指针则指向一个数组,可动态分配和管理大型数据结构。两者结合使用,灵活高效地管理内存。
|
27天前
|
容器
在使用指针数组进行动态内存分配时,如何避免内存泄漏
在使用指针数组进行动态内存分配时,避免内存泄漏的关键在于确保每个分配的内存块都能被正确释放。具体做法包括:1. 分配后立即检查是否成功;2. 使用完成后及时释放内存;3. 避免重复释放同一内存地址;4. 尽量使用智能指针或容器类管理内存。
|
27天前
|
存储 NoSQL 编译器
C 语言中指针数组与数组指针的辨析与应用
在C语言中,指针数组和数组指针是两个容易混淆但用途不同的概念。指针数组是一个数组,其元素是指针类型;而数组指针是指向数组的指针。两者在声明、使用及内存布局上各有特点,正确理解它们有助于更高效地编程。
|
1月前
|
存储 人工智能 算法
数据结构实验之C 语言的函数数组指针结构体知识
本实验旨在复习C语言中的函数、数组、指针、结构体与共用体等核心概念,并通过具体编程任务加深理解。任务包括输出100以内所有素数、逆序排列一维数组、查找二维数组中的鞍点、利用指针输出二维数组元素,以及使用结构体和共用体处理教师与学生信息。每个任务不仅强化了基本语法的应用,还涉及到了算法逻辑的设计与优化。实验结果显示,学生能够有效掌握并运用这些知识完成指定任务。
51 4
|
1月前
使用指针访问数组元素
【10月更文挑战第31天】使用指针访问数组元素。
48 2
|
1月前
|
算法 索引
单链表题+数组题(快慢指针和左右指针)
单链表题+数组题(快慢指针和左右指针)
39 1
|
1月前
|
存储 C语言
C语言如何使用结构体和指针来操作动态分配的内存
在C语言中,通过定义结构体并使用指向该结构体的指针,可以对动态分配的内存进行操作。首先利用 `malloc` 或 `calloc` 分配内存,然后通过指针访问和修改结构体成员,最后用 `free` 释放内存,实现资源的有效管理。
101 13