超级简单的“反转字符串”问题,以及双指针的运用、按位异或^运算符

本题出自https://leetcode.cn/problems/reverse-string/description/,属于是最最最简单的一档题目了,但也需要一些耐心才能开始算法的学习对吧,千里之行始于足下,不积跬步无以至千里。

编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。

不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。

示例 1:

输入:s = ["h","e","l","l","o"]
输出:["o","l","l","e","h"]

示例 2:

输入:s = ["H","a","n","n","a","h"]
输出:["h","a","n","n","a","H"]

提示:

  • 1 <= s.length <= 105
  • s[i] 都是 ASCII 码表中的可打印字符

 双指针

首先我们排除例如C语言库中reverse这种直接反转的库函数的调用,相信会写这道题的同学还属于刚开始的阶段,所以优先选择例如双指针的用法,更加基础且切合出题者出这道题的意图

        左指针left我们定义为i,这样一来i就代表着左边的数;

        右指针right我们定义为j,这样一来j就代表着右边的数;

数组是s,s[i] 和 s[j] 就代表着数组中的数了


C++的解法

class Solution {
public:
    void reverseString(vector<char>& s) {
         for (int i = 0, j = s.size() - 1; i < s.size()/2; i++, j--) {
            swap(s[i],s[j]);
        }
    }
};

Java的解法

class Solution {
    public void reverseString(char[] s) {
        int i=0;
        int j=s.length-1;
        while (i<j){
            char temp=s[i];
            s[i]=s[j];
            s[j]=temp;
            i++;
            j--;
        } 
    }
}

Python的解法

class Solution(object):
    def reverseString(self, s):
        """
        :type s: List[str]
        :rtype: None Do not return anything, modify s in-place instead.
        """
        i, right = 0, len(s) - 1
        while i < j:
            s[i], s[j] = s[j], s[i]
            i += 1
            j -= 1

双指针的综合运用

双指针技术是一种常用的算法技巧,尤其适用于处理数组和链表问题。通过使用两个指针,可以在一次遍历中完成某些复杂操作,从而提高算法的效率。常见的双指针应用场景包括:

  1. 数组/链表的两两配对:如反转数组、查找数组中的特定元素对等。
  2. 滑动窗口:用于解决子数组或子串问题,如最大子数组和、最小覆盖子串等。
  3. 快慢指针:用于检测链表中的环、寻找链表的中间节点等。

常见的双指针应用场景

1. 反转数组

我们已经在之前的示例中看到了如何使用双指针来反转数组。这里再简单回顾一下:(以下代码为Java代码以作演示)

class Solution {
    public void reverseString(char[] s) {
        int i = 0;
        int j = s.length - 1;
        while (i < j) {
            char temp = s[i];
            s[i] = s[j];
            s[j] = temp;
            i++;
            j--;
        }
    }
}

在这个例子中,ij 分别从数组的两端向中间移动,交换它们所指向的元素,直到 ij 相遇或交错。

2. 查找数组中的特定元素对

例如,给定一个排序数组和一个目标值,找到数组中两个数的和等于目标值的索引。

class Solution {
    public int[] twoSum(int[] numbers, int target) {
        int i = 0;
        int j = numbers.length - 1;
        
        while (i < j) {
            int sum = numbers[i] + numbers[j];
            if (sum == target) {
                return new int[]{i + 1, j + 1}; // 题目要求返回索引从1开始
            } else if (sum < target) {
                i++;
            } else {
                j--;
            }
        }
        
        return new int[]{-1, -1}; // 如果没有找到,返回默认值
    }
}

在这个例子中,ij 分别从数组的两端向中间移动,根据 numbers[i] + numbers[j] 与目标值的比较结果调整指针的位置。

3. 滑动窗口

例如,找到一个数组中最长的连续子数组,使得该子数组的和不超过某个给定的值。

class Solution {
    public int maxSubArrayLen(int[] nums, int k) {
        int maxLength = 0;
        int currentSum = 0;
        int start = 0;
        
        for (int end = 0; end < nums.length; end++) {
            currentSum += nums[end];
            
            while (currentSum > k && start <= end) {
                currentSum -= nums[start];
                start++;
            }
            
            maxLength = Math.max(maxLength, end - start + 1);
        }
        
        return maxLength;
    }
}

在这个例子中,startend 分别表示滑动窗口的左右边界。通过调整窗口的大小,确保窗口内的元素和不超过给定的值 k

4. 快慢指针

例如,检测链表中是否存在环。

class ListNode {
    int val;
    ListNode next;
    ListNode(int x) { val = x; }
}

class Solution {
    public boolean hasCycle(ListNode head) {
        if (head == null || head.next == null) {
            return false;
        }
        
        ListNode slow = head;
        ListNode fast = head.next;
        
        while (slow != fast) {
            if (fast == null || fast.next == null) {
                return false;
            }
            slow = slow.next;
            fast = fast.next.next;
        }
        
        return true;
    }
}

在这个例子中,slow 指针每次移动一步,fast 指针每次移动两步。如果存在环,fast 指针最终会追上 slow 指针。

总结

双指针技术通过使用两个指针来解决问题,可以有效地减少时间和空间复杂度。不同的应用场景需要灵活选择合适的指针移动策略。

本题的一种特殊解法,无需引入变量,按位异或运算

按位异或,^ 是位运算符

在 Java 中,^ 是一个位运算符,称为“按位异或”(Bitwise XOR)。它用于对两个整数类型的值进行逐位比较,当相应位上两个操作数的值不同时结果为1,相同时结果为0。

例如,如果有两个整数 a = 5 (二进制表示为 0101) 和 b = 3 (二进制表示为 0011),那么 a ^ b 的计算过程如下:

  0101 (a)
^ 0011 (b)
------
  0110 (结果为6)

因此,a ^ b 的结果是 6(0110)。

按位异或运算符在多种场景下都有应用,比如数据加密、交换两个变量的值而不需要额外的临时变量等。

解法

int i = 0;
        int j = s.length - 1;
        while (i < j) {
            s[i] ^= s[j];  
            s[j] ^= s[i];  
            s[i] ^= s[j];  
            i++;
            j--;
        }

说明

这三行代码利用了按位异或运算的特性来交换 s[i]s[j] 的值,而不需要额外的临时变量。具体步骤如下:

  • 第一步:s[i] ^= s[j],将 s[i] 和 s[j] 的异或结果存储在 s[i] 中。
  • 第二步:s[j] ^= s[l],将第一步的结果与 s[j] 异或,结果存储在 s[j] 中,此时 s[j] 存储的是原来的 s[i] 的值。
  • 第三步:s[i] ^= s[j],将第二步的结果与 s[j] 异或,结果存储在 s[i] 中,此时 s[i] 存储的是原来的 s[j] 的值。 经过这三步操作后,s[i] 和 s[j] 的值就完成了交换。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值