经典算法题总结:数组常用技巧(双指针,二分查找和位运算)篇

news2024/9/22 1:45:09

双指针

在处理数组和链表相关问题时,双指针技巧是经常用到的,双指针技巧主要分为两类:左右指针快慢指针。所谓左右指针,就是两个指针相向而行或者相背而行;而所谓快慢指针,就是两个指针同向而行,一快一慢。

15. 三数之和(⭐️⭐️)

思路

两数之和 -> 三数之和 -> N 数之和

代码

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class ThreeSumTarget {

    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        Arrays.sort(nums);
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] > 0) {
                return res;
            }
            if (i > 0 && nums[i] == nums[i - 1]) {
                continue;
            }
            int left = i + 1;
            int right = nums.length - 1;
            while (left < right) {
                int sum = nums[i] + nums[left] + nums[right];
                if (sum < 0) {
                    left++;
                } else if (sum > 0) {
                    right--;
                } else {
                    res.add(Arrays.asList(nums[i], nums[left], nums[right]));
                    while ((left < right) && (nums[right] == nums[right - 1])) {
                        right--;
                    }
                    while ((left < right) && nums[left] == nums[left + 1]) {
                        left++;
                    }
                    right--;
                    left++
                }
            }
        }
        return res;
    }

}
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class NSumTarget {

    public List<List<Integer>> threeSum(int[] nums) {
        Arrays.sort(nums);
        return nSumTarget(nums, 3, 0, 0);
    }

    private List<List<Integer>> nSumTarget(int[] nums, int n, int start, int target) {
        int size = nums.length;
        List<List<Integer>> res = new ArrayList<>();
        if (n < 2 || size < n) {
            return res;
        }
        if (n == 2) {
            int low = start;
            int high = size - 1;
            while (low < high) {
                int sum = nums[low] + nums[high];
                int left = nums[low];
                int right = nums[high];
                if (sum < target) {
                    while (low < high && nums[low] == left) {
                        low++;
                    }
                } else if (sum > target) {
                    while (low < high && nums[high] == right) {
                        high--;
                    }
                } else {
                    res.add(new ArrayList<>(Arrays.asList(left, right)));
                    while (low < high && nums[low] == left) {
                        low++;
                    }
                    while (low < high && nums[high] == right) {
                        high--;
                    }
                }
            }
        } else {
            for (int i = start; i < size; i++) {
                if (i > start && nums[i] == nums[i - 1]) {
                    continue;
                }
                List<List<Integer>> sub = nSumTarget(nums, n - 1, i + 1, target - nums[i]);
                for (List<Integer> arr : sub) {
                    arr.add(nums[i]);
                    res.add(arr);
                }
                while (i < size - 1 && nums[i] == nums[i + 1]) {
                    i++;
                }
            }
        }
        return res;
    }

}

复杂度

  • 时间复杂度:O(N^2)
  • 空间复杂度:O(logN)

5. 最长回文子串

思路

寻找回文串的问题核心思想是:从中间开始向两边扩散来判断回文串,对于最长回文子串,就是这个意思:

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

找回文串的关键技巧是传入两个指针 leftright 向两边扩散,因为这样实现可以同时处理回文串长度为奇数和偶数的情况。

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

代码

public class LongestPalindromicSubstring {

    public String longestPalindrome(String s) {
        String res = "";
        for (int i = 0; i < s.length(); i++) {
            String s1 = palindrome(s, i, i); // 奇数情况
            String s2 = palindrome(s, i, i + 1); // 偶数情况
            res = res.length() > s1.length() ? res : s1;
            res = res.length() > s2.length() ? res : s2;
        }
        return res;
    }

    private 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);
    }

}

复杂度

  • 时间复杂度:O(N^2)
  • 空间复杂度:O(N)

88. 合并两个有序数组(⭐️⭐️)

思路

代码

public class MergeTwoArray {

    // 双指针
    public void merge1(int[] nums1, int m, int[] nums2, int n) {
        int p1 = 0;
        int p2 = 0;
        int[] sorted = new int[m + n];
        int cur = 0;
        int i = 0;
        while (p1 < m && p2 < n) {
            if (nums1[p1] < nums2[p2]) {
                sorted[i++] = nums1[p1++];
            } else {
                sorted[i++] = nums2[p2++];
            }
        }
        while (p1 < m) {
            sorted[i++] = nums1[p1++];
        }
        while (p2 < n) {
            sorted[i++] = nums2[p2++];
        }
        for (i = 0; i < m + n; i++) {
            nums1[i] = sorted[i];
        }
    }

    // 逆向双指针
    public void merge2(int[] nums1, int m, int[] nums2, int n) {
        int i = m - 1;
        int j = n - 1;
        int p = nums1.length - 1;
        while (i >= 0 && j >= 0) {
            if (nums1[i] > nums2[j]) {
                nums1[p] = nums1[i];
                i--;
            } else {
                nums1[p] = nums2[j];
                j--;
            }
            p--;
        }
        while ( j>= 0) {
            nums1[p] = nums2[j];
            j--;
            p--;
        }
    }

}

复杂度

  • 时间复杂度:O(N)
  • 空间复杂度:双指针:O(N),逆向双指针:O(1)

二分查找

int binarySearch(int[] nums, int target) {
    int left = 0, right = nums.length - 1; 
    while(left <= right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] < target) {
            left = mid + 1;
        } else if (nums[mid] > target) {
            right = mid - 1; 
        } else if(nums[mid] == target) {
            // 直接返回
            return mid;
        }
    }
    // 直接返回
    return -1;
}

33. 搜索旋转排序数组(⭐️⭐️)

思路

代码

public class SearchInRotatedSortedArray {

    /*
        nums = [4,5,6,7,0,1,2]
        例如 target = 5, 目标值在左半段,因此在 [4, 5, 6, 7, inf, inf, inf] 这个有序数组里找就行了
        例如 target = 1, 目标值在右半段,因此在 [-inf, -inf, -inf, -inf, 0, 1, 2] 这个有序数组里找就行了
     */
    public int search(int[] nums, int target) {
        int left = 0;
        int right = nums.length - 1;

        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] == target) {
                return mid;
            }
            if (target >= nums[0]) {
                if (nums[mid] < nums[0]) {
                    nums[mid] = Integer.MAX_VALUE;
                }
            } else {
                if (nums[mid] >= nums[0]) {
                    nums[mid] = Integer.MIN_VALUE;
                }
            }
            if (nums[mid] < target) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        return -1;
    }

}

复杂度

  • 时间复杂度:O(logN)
  • 空间复杂度:O(1)

69. x 的平方根(⭐️⭐️)

思路

代码

public class Sqrt {

    public int mySqrt(int x) {
        int left = 0, right = x, res = -1;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if ((long) mid * mid <= x) {
                res = mid;
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        return res;
    }

}

复杂度

  • 时间复杂度:O(N)
  • 空间复杂度:O(1)

240. 搜索二维矩阵 II(⭐️)

思路

代码

public class SearchMatrix {

    public boolean searchMatrix(int[][] matrix, int target) {
        int m = matrix.length, n = matrix[0].length;
        int i = 0, j = n - 1;
        while (i < m && j >= 0) {
            if (matrix[i][j] == target) {
                return true;
            }
            if (matrix[i][j] < target) {
                i++; // 需要大一点,往下移动
            } else {
                j--; // 需要小一点,往左移动
            }
        }
        return false;
    }

}

复杂度

  • 时间复杂度:O(M + N)
  • 空间复杂度:O(1)

34. 在排序数组中查找元素的第一个和最后一个位置(⭐️)

思路

代码

public class SearchRange {

    public int[] searchRange(int[] nums, int target) {
        return new int[]{leftRange(nums, target), rightRange(nums, target)};
    }

    private int leftRange(int[] nums, int target) {
        int left = 0, right = nums.length - 1;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] < target) {
                left = mid + 1;
            } else if (nums[mid] > target) {
                right = mid - 1;
            } else if (nums[mid] == target) {
                right = mid - 1;
            }
        }
        if (left < 0 || left >= nums.length) {
            return -1;
        }
        return nums[left] == target ? left : -1;
    }

    private int rightRange(int[] nums, int target) {
        int left = 0, right = nums.length - 1;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] < target) {
                left = mid + 1;
            } else if (nums[mid] > target) {
                right = mid - 1;
            } else if (nums[mid] == target) {
                left = mid + 1;
            }
        }
        if (right < 0 || right >= nums.length) {
            return -1;
        }
        return nums[right] == target ? right : -1;
    }

}

复杂度

  • 时间复杂度:O(log(N))
  • 空间复杂度:O(1)

34. 在排序数组中查找元素的第一个和最后一个位置(⭐️)

思路

代码

class Solution {
    
    public int[] searchRange(int[] nums, int target) {
        return new int[]{leftBound(nums, target), rightBound(nums, target)};
    }

    private int leftBound(int[] nums, int target) {
        int left = 0, right = nums.length - 1;
        // 搜索区间为 [left, right]
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] < target) {
                // 搜索区间变为 [mid+1, right]
                left = mid + 1;
            } else if (nums[mid] > target) {
                // 搜索区间变为 [left, mid-1]
                right = mid - 1;
            } else if (nums[mid] == target) {
                // 收缩右侧边界
                right = mid - 1;
            }
        }
        // 检查出界情况
        if (left >= nums.length || nums[left] != target) {
            return -1;
        }
        return left;
    }

    private int rightBound(int[] nums, int target) {
        int left = 0, right = nums.length - 1;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] < target) {
                left = mid + 1;
            } else if (nums[mid] > target) {
                right = mid - 1;
            } else if (nums[mid] == target) {
                // 这里改成收缩左侧边界即可
                left = mid + 1;
            }
        }
        // 这里改为检查 right 越界的情况,见下图
        if (right < 0 || nums[right] != target) {
            return -1;
        }
        return right;
    }
    
}

复杂度

  • 时间复杂度:O(log(N))
  • 空间复杂度:O(N)

162. 寻找峰值(⭐️)

思路

代码

public class FindPeakElement {

    public int findPeakElement(int[] nums) {
        int left = 0, right = nums.length - 1;
        while (left < right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] > nums[mid + 1]) {
                right = mid;
            } else {
                left = mid + 1;
            }
        }
        return left;
    }

}

复杂度

  • 时间复杂度:O(logN)
  • 空间复杂度:O(1)

位运算

136. 只出现一次的数字(⭐️)

思路

代码

public class SingleNumber {
    
    public int singleNumber(int[] nums) {
        int res = 0;
        for (int n : nums) {
            res ^= n;
        }
        return res;
    }
    
}

复杂度

  • 时间复杂度:O(N)
  • 空间复杂度:O(1)

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

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

相关文章

夏季养宠难题,浮毛异味难消?别担心,一篇文章帮你解决烦恼

新手铲屎官们最近是不是饱受猫咪浮毛异味困扰&#xff1f;正值夏季&#xff0c;一些猫咪还处于换毛季&#xff0c;疯狂的掉毛加难耐的高温&#xff0c;稍不注意清理&#xff0c;家里就被浮毛异味双重攻击。别担心&#xff0c;那是因为你们少了宠物空气净化器这个好帮手。作为宠…

【机器学习】混淆矩阵(Confusion Matrix)

一、混淆矩阵 True Negative (TN)&#xff1a; 真负类&#xff0c;样本的真实类别是负类&#xff0c;并且模型将其识别为负类&#xff0c;cm[0][0]。False Positive (FP)&#xff1a; 假正类&#xff0c;样本的真实类别是负类&#xff0c;但是模型将其识别为正类&#xff0c;cm…

见合八方发布国产1060nm保偏SOA

天津见合八方光电科技有限公司(以下简称“见合八方”)日前发布新款1060nm保偏半导体光放大SOA蝶形器件。该半导体光放大器SOA具有高饱和输出功率、宽光谱、保偏等特性。主要用于星载空间光通信、空间激光探测、OCT医疗成像等领域。 产品介绍 我司1060nm蝶形器件&#xff0c;具有…

期货的种类之什么是股指期货的类型?

期货可以分为商品期权和股指期货&#xff0c;商品期货的标的物主要是现货&#xff0c;而股指期货的标的物主要是指数。简单来说&#xff0c;就是一种基于股票指数未来的价格变动来进行交易的金融衍生品。它的全称是“股票价格指数期货”&#xff0c;英文简称SPIF。 股指期货怎…

P3957 [NOIP2017 普及组] 跳房子(青春版代码)

[NOIP2017 普及组] 跳房子 - 洛谷 核心思路 单调队列优化dp 顺序 先让合法答案入队 再删去越界答案 判断非空 后 求 答案 一个答案合法 当且仅当 l < dis < r 记 调了n久&#xff0c;找题解调。 竟发现几乎没有用 STL deque 的。 故写了个青春版题解。 AC 代码…

思科RIP动态路由配置3

#路由协议实现# #任务三RIP动态路由配置3# 路由信息协议&#xff08;Routing Information Protocol&#xff0c;RIP&#xff09;是应用较早、使用较普遍的动态路由协议&#xff0c;也是内部网关协议&#xff0c;由于RIP以跳数作为衡量路径的开销&#xff0c;且规定最大跳数为…

zip压缩包的格式不标准导致C++开源unzip.cpp解压失败问题的排查

目录 1、问题描述 2、初步排查 3、查看错误码512对应的含义 4、直接将解压zip包的函数拷贝过来,并将无法解压的zip取来,直接编写测试代码去调试解压过程,最终定位问题 4.1、调试开源unzip.cpp源码的准备工作 4.2、刚解压zip包中最顶层的文件夹就失败了 4.3、是不是zi…

Java如何实现拼音排序?

在我们国家&#xff0c;按拼音进行排序是很常见的需求&#xff0c;比如姓名&#xff0c;那如何按拼音进行排序呢&#xff1f; 假如我们有以下List&#xff1a; List<String> list new ArrayList<>(); list.add("周瑜"); list.add("大都督")…

Redis中缓存穿透、缓存击穿、缓存雪崩的详解

如何理解Redis缓存的穿透、击穿、雪崩问题&#xff1a; 缓存穿透 是指缓存中和数据库中都没有数据&#xff0c;而用户不断访问&#xff0c;导致这个不存在的数据每次请求都要到存储层去查询&#xff0c;这样失去了意义。 缓存穿透的解决方案有哪些? 缓存null值布隆过滤增强…

3D靓图!CEEMDAN-Kmeans-VMD-CNN-BiLSTM-Attention双重分解卷积双向长短期注意力多元时序预测

3D靓图&#xff01;CEEMDAN-Kmeans-VMD-CNN-BiLSTM-Attention双重分解卷积双向长短期注意力多元时序预测 目录 3D靓图&#xff01;CEEMDAN-Kmeans-VMD-CNN-BiLSTM-Attention双重分解卷积双向长短期注意力多元时序预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Ma…

【问题解决】git status中文文件名乱码

问题复现 解决办法 在git bash中直接执行如下命令 git config --global core.quotepath false原因 通过 git config --help 可以查看到以下内容&#xff1a; core.quotePath Commands that output paths (e.g. ls-files, diff), will quote “unusual” characters in the p…

vue3 更改 element-plus el-collapse 折叠面板样式

官网提供 展示效果 样式 更改箭头指向 :deep(.el-collapse-item__arrow){ transform: rotate(-270deg); } :deep(.el-collapse-item__header.is-active .el-collapse-item__arrow) { transform: rotate(270deg); } 二 &#xff1a; 或者隐藏右侧箭头 :deep(.el-collapse-i…

javase入门

最近在学习大数据,学到flume拦截器的时候发现自定义拦截器需要使用java编写,现在开始学一些java入门的东西. 一. java相关组成 path环境变量: 环境变量用于记住程序路径,方便在命令行窗口任意目录启动程序. 二 java中的变量 变量要先定义在使用. int age 15 定义变量要定义其…

8.6.数据库基础技术-数据库的控制

并非控制 事务&#xff1a;由一系列DML操作组成&#xff0c;这些操作&#xff0c;要么全做&#xff0c;要么全不做&#xff0c;它从第一个DML操作开始&#xff0c;rollback、commit或者DDL结束&#xff0c;拥有以下四种特性&#xff0c;详解如下&#xff1a; &#xff08;操作)…

OrangePi AIpro学习5 —— 模型推理程序开发

目录 一、准备工作 1.1 代码裁剪 1.2 测试运行 二、程序讲解 2.1 初始化 2.2 处理模型图片输入 2.3 推理函数 2.4 对输出结果进行处理 前言 本节主要讲解昇腾芯片&#xff0c;例程中使用resnet50推理图像类别的程序。本节讲解的程序&#xff0c;它的环境搭建与使用方法…

考PMP需要多久?一篇文章告诉你如何省心备考PMP

PMP备考需要半年时间吗&#xff1f;....是要满分拿下PMP吗&#xff1f; PMP备考不用半年时间&#xff0c;如果你想的话&#xff0c;可以一个多月就成功上岸哦。如果你不想的话&#xff0c;也可以半年。 其实大家认真去学习的&#xff0c;时间根本不需要那么长的时间&#xff0…

全新Bty分销系统源码v1.0/宝塔分销系统开源版源码/独立后台(附安装教程)

源码简介&#xff1a; 全新Bty分销系统源码v1.0&#xff0c;它作为宝塔分销系统开源版源码&#xff0c;功能强大&#xff0c;它内置了易支付功能&#xff0c;方便多了&#xff01; 这个Bty分销系统开源版&#xff0c;宝塔分销系统开源版。它基于宝塔开放的API底层控制器&…

LoadRunner12添加mysql数据连接驱动

参考链接 lr进行mysql参数化 https://jingyan.baidu.com/article/6d704a13407c4128db51ca2d.html 下载mysql数据库驱动教程 Loadrunner12使用MYSQL5.0参数化数据遇到的问题_loadrunner12 参数化没有效果-CSDN博客 mysql数据驱动下载链接 MySQL :: Download MySQL Connect…

centos 下如何安装openjdk21

文章目录 前言一、下载OpenJDK二、上传OpenJdk三、解压四、编辑环境变量五、重新加载一下配置六、验证是否安装成功 前言 本文章是自己将openjdk下载好&#xff01;手动上传解压的方式进行安装&#xff01; 一、下载OpenJDK OpenJdk官网&#xff1a;点击访问 二、上传OpenJ…

封装Form表单【后台控制表单搜索项的显隐和排序】

概要 为了实现需求&#xff1a;后台控制表单搜索项的显隐和排序&#xff1b; 整体思路流程 表单搜索项统一配置&#xff0c;封装成一个组件&#xff0c;把不同类别再封装成单个的组件&#xff0c;配置项数组由前端控制&#xff08;暂由前端配置&#xff0c;这样虽然代码量多&…