五月训练 Day5

本文介绍了如何利用双指针技巧解决LeetCode上的字符串处理问题,包括917题的字母反转、167题的两数之和、165题的版本号比较以及443题的字符串压缩。通过分析与解答,展示了双指针在优化算法时间复杂度方面的应用。

0. Leetcode 917. 仅仅反转字母

给你一个字符串 s ,根据下述规则反转字符串:
所有非英文字母保留在原有位置。
所有英文字母(小写或大写)位置反转。
返回反转后的 s 。

分析与解答

借用双指针的思想,要调换单词中所有字母的顺序,只需要首尾配对找到相应字母位置即可。因此令 i 从头开始,j 从尾开始寻找字母,找到后进行调换,直至 i >= j,表明已交换单词中的所有字母。

class Solution {
public:
    string reverseOnlyLetters(string s) {
        for (int i = 0, j = s.size() - 1; i < j;) {
            while (!(tolower(s[i]) >= 'a' && tolower(s[i]) <= 'z') && i < j) {
                i++;
            }

            while (!(tolower(s[j]) >= 'a' && tolower(s[j]) <= 'z') && i < j) {
                j--;
            }

            if (i >= j) {
                break;
            } else {
                char tmp = s[i];
                s[i] = s[j];
                s[j] = tmp;
            }

            // 交换完后将指针移到下一个待判断字符
            i++;
            j--;
        }

        return s;
    }
};

算法时间复杂度为 O ( N ) O(N) O(N)

1. Leetcode 167. 两数之和 II - 输入有序数组

给你一个下标从 1 开始的整数数组 numbers ,该数组已按 非递减顺序排列 ,请你从数组中找出满足相加之和等于目标数 target 的两个数。如果设这两个数分别是 numbers[index1] 和 numbers[index2] ,则 1 <= index1 < index2 <= numbers.length 。
以长度为 2 的整数数组 [index1, index2] 的形式返回这两个整数的下标 index1 和 index2。
你可以假设每个输入 只对应唯一的答案 ,而且你 不可以 重复使用相同的元素。
你所设计的解决方案必须只使用常量级的额外空间。

分析与解答

解法1:二分查找
对于该问题,从头至尾遍历数组,对每个数 num,二分查找 target - num 在数组中是否存在。

class Solution {
public:
    int findPos(vector<int>& numbers, int low, int high, int target) {
        int l(low), r(high);
        while (l <= r) {
            int mid = l + ((r - l) >> 1);
            if (numbers[mid] < target) {
                l = mid + 1;
            } else if (numbers[mid] > target) {
                r = mid - 1;
            } else {
                return mid;
            }
        }
        
        return -1;
    }

    vector<int> twoSum(vector<int>& numbers, int target) {
        vector<int> result;
        bool find(false);
        for (int i = 0; i < numbers.size(); ++i) {
            int idx = findPos(numbers, i+1, numbers.size() - 1, target - numbers[i]);
            if (idx > 0) {
                result.push_back(i+1);
                result.push_back(idx+1);
                break;
            }
        }
        return result;
    }
};

算法时间复杂度为 O ( N l o g N ) O(NlogN) O(NlogN)
解法2:双指针
令两个指针分别指向首数字与尾数字,若两数和大于 target,则尾指针前移;若两数和大于 target,则首指针后移。

class Solution {
public:
    vector<int> twoSum(vector<int>& numbers, int target) {
        vector<int> result;
        int i(0), j(numbers.size() - 1);
        while (i < j) {
            if (numbers[i] + numbers[j] < target) {
                i++;
            } else if (numbers[i] + numbers[j] > target) {
                j--;
            } else {
                result.push_back(i + 1);
                result.push_back(j + 1);
                break;
            }
        }
        
        return result;
    }
};

算法时间复杂度为 O ( N ) O(N) O(N)

2. Leetcode 165. 比较版本号

给你两个版本号 version1 和 version2 ,请你比较它们。
版本号由一个或多个修订号组成,各修订号由一个 ‘.’ 连接。每个修订号由 多位数字 组成,可能包含 前导零 。每个版本号至少包含一个字符。修订号从左到右编号,下标从 0 开始,最左边的修订号下标为 0 ,下一个修订号下标为 1 ,以此类推。例如,2.5.33 和 0.1 都是有效的版本号。
比较版本号时,请按从左到右的顺序依次比较它们的修订号。比较修订号时,只需比较 忽略任何前导零后的整数值 。也就是说,修订号 1 和修订号 001 相等 。如果版本号没有指定某个下标处的修订号,则该修订号视为 0 。例如,版本 1.0 小于版本 1.1 ,因为它们下标为 0 的修订号相同,而下标为 1 的修订号分别为 0 和 1 ,0 < 1 。
返回规则如下:
如果 version1 > version2 返回 1,
如果 version1 < version2 返回 -1,
除此之外返回 0。

分析与解答

令两个指针指向字符串头,后移指针 r,直至找到字符 .,此时从指针间取出字串,进行比较。重复这一过程直至遍历完成。

class Solution {
public:
    int compareVersion(string version1, string version2) {
        int l0(0), r0(0);
        int l1(0), r1(0);
        int result(0);
        
        version1 += ".";
        version2 += ".";
        while (r0 < version1.size() || r1 < version2.size()) {
            string str1 = "0";
            string str2 = "0";
            if (r0 < version1.size()) {
                while (version1[r0] != '.') {
                    r0++;
                }
                while (version1[l0] == '0') {
                    l0++;
                }
                if (r0 - l0 > 0) {
                    str1 = version1.substr(l0, r0 - l0);
                }
            }
            
            if (r1 < version2.size()) {
                while (version2[r1] != '.') {
                    r1++;
                }
                while (version2[l1] == '0') {
                    l1++;
                }
                if (r1 - l1 > 0) {
                    str2 = version2.substr(l1, r1 - l1);
                }
            }
            
            if (stoi(str1) < stoi(str2)) {
                result = -1;
                break;
            } else if (stoi(str1) > stoi(str2)) {
                result = 1;
                break;
            }
            
            r0++;
            l0 = r0;
            r1++;
            l1 = r1;
        }
        
        return result;
    }
};

算法时间复杂度为 O ( N ) O(N) O(N)

3. Leetcode 443. 压缩字符串

给你一个字符数组 chars ,请使用下述算法压缩:
从一个空字符串 s 开始。对于 chars 中的每组 连续重复字符 :
如果这一组长度为 1 ,则将字符追加到 s 中。
否则,需要向 s 追加字符,后跟这一组的长度。
压缩后得到的字符串 s 不应该直接返回 ,需要转储到字符数组 chars 中。需要注意的是,如果组长度为 10 或 10 以上,则在 chars 数组中会被拆分为多个字符。
请在 修改完输入数组后 ,返回该数组的新长度。
你必须设计并实现一个只使用常量额外空间的算法来解决此问题。

分析与解答

令指针 i 作为写指针,记录写字符的位置;指针 j 作为读指针,记录读取字符的位置,后移指针 j 直至新字符与前一字符不同,此时用指针 i 写入。重复该过程直至遍历完成。

class Solution {
public:
    int compress(vector<char>& chars) {
        int i(0), j(1);
        char preChar = chars[0];
        int count = 1;
        string cstr;
        while (j < chars.size()) {
            if (preChar != chars[j]) {
                chars[i++] = preChar;
                if (count > 1) {
                    cstr = to_string(count);
                    for (int k = 0; k < cstr.size(); ++k) {
                        chars[i++] = cstr[k];
                    }
                }
                preChar = chars[j];
                count = 1;
            } else {
                count++;
            }
            j++;
        }
        chars[i++] = preChar;
        if (count > 1) {
            cstr = to_string(count);
            for (int k = 0; k < cstr.size(); ++k) {
                chars[i++] = cstr[k];
            }
        }

        return i;
    }
};

算法时间复杂度为 O ( N ) O(N) O(N)

总结

用好双指针能够有效降低时间复杂度,简化问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值