数组双指针-对撞指针(一)

一、对撞指针

核心思想

  • 利用数组的有序性,通过指针移动快速定位目标值或满足条件的元素。
  • 时间复杂度通常为 O(n) 或 O(n²),远优于暴力枚举的 O(n²) 或 O(n³)。
  • 即用两个指针 leftleft 和 rightright,分别指向序列的首尾,leftleft 向右、rightright 向左移动,直到两指针相遇(left==rightleft==right)或满足特定条件。

java模版

    /**
     * 双指针问题:
     * 对撞指针:来解决有序数组和字符串问题
     *      通过2个指针从两边向中间移动,逐步缩小中间范围。
     */
    @Test
    public void test(int[] array) {
        // 左右指针开始下标
        int left = 0, right = array.length - 1;
        // 使用while循环: 如果左指针<右边指针 则就继续循环 否则就退出循环
        while (left < right) {
            // 根据条件移动指针
            if (/* 条件满足 */) {
                left++;
            } else {
                right--;
            }
        }
    }
167. 两数之和 II - 输入有序数组

167. 两数之和 II - 输入有序数组https://leetcode.cn/problems/two-sum-ii-input-array-is-sorted/description/

    /**
     * 题目名称:Two Sum II - Input array is sorted
     * 题目编号:167. Two Sum II
     * 难度:简单
     * 题目要求:
     * 给你一个下标从 1 开始的整数数组 numbers ,该数组已按 非递减顺序排列  ,请你从数组中找出满足相加之和等于目标数 target 的两个数。如果设这两个数分别是 numbers[index1] 和 numbers[index2] ,则 1 <= index1 < index2 <= numbers.length 。
     * 以长度为 2 的整数数组 [index1, index2] 的形式返回这两个整数的下标 index1 和 index2。
     * 你可以假设每个输入 只对应唯一的答案 ,而且你 不可以 重复使用相同的元素。
     * 使用双指针法
     */
    @DataProvider(name = "test")
    public Object[][] test() {
        return new Object[][] {
                {new int[]{2,7,11,15}, 9},
                {new int[]{2,3,4}, 6},
                {new int[]{-1,0}, -1}
        };
    }
    @Test(dataProvider = "test")
    public void test(int[] array, int target) {
        //双指针对撞
        int left = 0;
        int right = array.length - 1;
        while (left < right) {
            int sum = array[left] + array[right];
            if (sum == target) {
                System.out.println(Arrays.toString(new int[]{left + 1, right + 1}));
                break;
            } else if (sum < target) {
                left++;
            } else {
                right--;
            }
        }
        System.out.println(Arrays.toString(new int[]{-1, -1}));
    }
  • 双指针法
    • 初始化left = 0right = numbers.length - 1
    • 移动规则
      • 若 sum < target,左指针右移(增大和)。
      • 若 sum > target,右指针左移(减小和)。
      • 若 sum == target,返回 1-based 索引。
  • 时间复杂度:O(n),空间复杂度 O(1)。
344. 反转字符串

344. 反转字符串https://leetcode.cn/problems/reverse-string/description/

    /**
     * 题目名称:反转字符串
     * 题目编号:344. Reverse String
     * 难度:简单
     * 题目要求:
     * 编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。
     * 不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。
     */
    @DataProvider(name = "reverseString")
    public Object[][] reverseString() {
        return new Object[][] {
                {"hello"},
                {"h"},
                {""}
        };
    }
    @Test(dataProvider = "reverseString")
    public void testReverseString(String str) {
        //双指针对撞
        //将字符串转成字符数组
        char[] array = str.toCharArray();
        if (array.length == 0) {
            System.out.println(0);
        }
        int left = 0;
        int right = array.length - 1;
        while (left < right) {
            // 将两个指针对应的字符交换
            char temp = array[left];
            array[left] = array[right];
            array[right] = temp;
            left++;
            right--;
        }
        System.out.println(array);
    }

关键步骤解析

  1. 初始化指针

    • left = 0(指向第一个字符)。
    • right = s.length - 1(指向最后一个字符)。
  2. 交换字符

    • 当 left < right 时,交换 s[left] 和 s[right]
  3. 移动指针

    • left++ 向右移动。
    • right-- 向左移动。
  4. 终止条件

    • 当 left >= right 时,字符串已完全反转。
9. 回文数

9. 回文数https://leetcode.cn/problems/palindrome-number/

    /**
     * 题目名称:回文数
     * 题目编号:9. Palindrome Number
     * 难度:简单
     * 题目要求:
     * 给你一个整数 x ,如果 x 是回文整数,返回 true ;否则,返回 false 。
     * 回文数是指正序(从左向右)和倒序(从右向左)读都相同的整数。例如,121 是回文,而 123 不是。
     */
    @DataProvider(name = "isPalindrome")
    public Object[][] isPalindrome() {
        return new Object[][] {
                {121},
                {-121},
                {10}
        };
    }
    @Test(dataProvider = "isPalindrome")
    public void testIsPalindrome(int x) {
        // 双指针对撞
        // 转成字符数组
        char[] array = String.valueOf(x).toCharArray();
        if (array.length == 0){
            System.out.println(false);
            return;
        }
        int left = 0;
        int right = array.length - 1;

        while(left < right){
            if(array[left] != array[right]){
                System.out.println(false);
                return;
            }
            left++;
            right--;
        }
        System.out.println(true);
    }

关键点说明

  • 负数处理:负数不可能是回文数(如 -121 读作 "-121",首尾不等)。
  • 字符数组转换:将数字转为字符数组后,使用对撞指针比较首尾字符。
  • 时间复杂度:O(n),其中 n 是数字的位数。
  • 空间复杂度:O(n)(存储字符数组)。
LCR 018. 验证回文串

LCR 018. 验证回文串https://leetcode.cn/problems/XltzEq/

    /**
     * 题目名称:LCR 018. 验证回文串
     * 题目编号:LCR 018
     * 难度:简单
     * 题目要求:
     * 给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。
     * 说明:本题中,我们将空字符串定义为有效的回文串。
     */
    @DataProvider(name = "isPalindrome2")
    public Object[][] isPalindrome2() {
        return new Object[][] {
                {"A man, a plan, a canal: Panama"},
                {"race a car"},
                {""}
        };
    }
    @Test(dataProvider = "isPalindrome2")
    public void testIsPalindrome2(String string) {
        // 双指针对撞
        // 去掉字符串中的非字母
        StringBuffer sb = new StringBuffer();
        int length = string.length();
        for (int i = 0; i < length; i++) {
            char c = string.charAt(i);
            if (Character.isLetterOrDigit(c)) {
                sb.append(c);
            }
        }

        int left = 0;
        int right = sb.length() - 1;
        while(left < right){
           if (Character.toLowerCase(sb.charAt(left)) != Character.toLowerCase(sb.charAt(right))){
               System.out.println(false);
               return;
           }
           left++;
           right--;
        }
        System.out.println(true);
    }
LCR 027. 回文链表

LCR 027. 回文链表https://leetcode.cn/problems/aMhZSa/

    /**
     * 题目名称:LCR 027. 回文链表
     * 题目编号:LCR 027
     * 难度:简单
     * 题目要求:
     * 请判断一个链表是否为回文链表。
     */
    class ListNode {
        int val;
        ListNode next;
        ListNode() {
        }
        ListNode(int val) {
            this.val = val;
        }
        ListNode(int val, ListNode next) {
            this.val = val;
            this.next = next;
        }
    }
    @DataProvider(name = "isPalindrome3")
    public Object[][] isPalindrome3() {
        return new Object[][] {
                {new ListNode(1, new ListNode(2, new ListNode(2, new ListNode(1))))},
                {new ListNode(1, new ListNode(2))}
        };
    }
    @Test(dataProvider = "isPalindrome3")
    public void testIsPalindrome3(ListNode head) {
        // 将链表的值复制到数组中
        ArrayList<Integer> arrayList = new ArrayList<>();
        while (head != null) {
            arrayList.add(head.val);
            head = head.next;
        }
        // 双指针对撞
        int left = 0;
        int right = arrayList.size() - 1;
        while(left < right){
            if (arrayList.get(left) != arrayList.get(right)){
                System.out.println(false);
                return;
            }
            left++;
            right--;
        }
        System.out.println(true);
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值