leetcode经典题目(1)--双指针

1. 两数之和 II - 输入有序数组(NO.167)

题目描述:给定一个已按照升序排列 的有序数组,找到两个数使得它们相加之和等于目标数。函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2。
解题思路:使用双指针,一个指针指向值较小的元素,一个指针指向值较大的元素。指向较小元素的指针从头向尾遍历,指向较大元素的指针从尾向头遍历。
如果两个指针指向元素的和 sum == target,那么得到要求的结果;
如果 sum > target,移动较大的元素(前移),使 sum 变小一些;
如果 sum < target,移动较小的元素(后移),使 sum 变大一些。
数组中的元素最多遍历一次,时间复杂度为 O(N)。只使用了两个额外变量,空间复杂度为 O(1)。

class Solution {
public:
    vector<int> twoSum(vector<int>& numbers, int target) {
        int left = 0, right = numbers.size() - 1;
        vector<int> result(2,0);
        while(left < right){
            int sum = numbers[left] + numbers[right];
            if (sum < target)
                left++;
            else if (sum > target)
                right--;
            else{
                result[0] = left+1;
                result[1] = right+1;
                break;
            }
        }
        return result;       
    }
};
2. 反转字符串中的元音字母(NO.345)

题目描述:编写一个函数,以字符串作为输入,反转该字符串中的元音字母。
解题思路:使用双指针,一个指针从头向尾遍历,一个指针从尾到头遍历,当两个指针都遍历到元音字符时,交换这两个元音字符。
时间复杂度为 O(N):只需要遍历所有元素一次;空间复杂度 O(1):只需要使用两个额外变量。

class Solution {
public:
    string reverseVowels(string s) {
        int left = 0;
        int right = s.size() - 1;
        while (left < right){//特别注意下面的两个while语句中要包含left
            while (left < right && s[left] != 'a' && s[left] != 'e' && s[left] != 'i' 
                    && s[left] != 'o' && s[left] != 'u' &&s[left] != 'A' 
                    && s[left] != 'E' && s[left] != 'I' && s[left] != 'O' && s[left] != 'U'){
                left++;
            }
            while (left < right && s[right] != 'a' && s[right] != 'e' && s[right] != 'i' 
                    && s[right] != 'o' && s[right] != 'u' && s[right] != 'A' && s[right] != 'E'
                    && s[right] != 'I' && s[right] != 'O' && s[right] != 'U'){
                right--;
            }
            char temp;
            temp = s[left];
            s[left] = s[right];
            s[right] = temp;
            left++;
            right--;
        }
        return s;
    }
};
3. 验证回文字符串 Ⅱ(NO.680)

题目描述:给定一个非空字符串 s,最多删除一个字符。判断是否能成为回文字符串。
解题思路:所谓的回文字符串,是指具有左右对称特点的字符串,例如 “abcba” 就是一个回文字符串。
使用双指针可以很容易判断一个字符串是否是回文字符串:令一个指针从左到右遍历,一个指针从右到左遍历,这两个指针同时移动一个位置,每次都判断两个指针指向的字符是否相同,如果都相同,字符串才是具有左右对称性质的回文字符串。(判断对称的思路)
本题的关键是处理删除一个字符。在使用双指针遍历字符串时,如果出现两个指针指向的字符不相等的情况,我们就试着删除一个字符,再判断删除完之后的字符串是否是回文字符串。

class Solution {
public:
    bool validPalindrome(string s) {
        for (int i = 0, j = s.size() - 1; i < j; i++, j--){
            if (s[i] != s[j])
                return isPalindrome(s,i,j-1) || isPalindrome(s,i+1,j);
        }
        return true;
    }
private:
    bool isPalindrome(string s, int i, int j){//判断是否为回文字符串
        while(i < j){
            if (s[i++] != s[j--])
                return false;
        }
        return true;
    }
};
4. 合并两个有序数组(NO.88)

题目描述:给你两个有序整数数组 nums1 和 nums2,请你将 nums2 合并到 nums1 中,使 nums1 成为一个有序数组。
说明:初始化 nums1 和 nums2 的元素数量分别为 m 和 n 。你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。
解题思路:由于要把归并结果存到第一个数组上,需要从尾开始遍历,否则在 nums1 上归并得到的值会覆盖还未进行归并比较的值。

class Solution {
public:
    void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
        int i = m-1, j = n-1, k = m+n-1;
        while (i >= 0 && j >= 0){
            if (nums1[i] <= nums2[j]){
                nums1[k--] = nums2[j--];
            }
            else{
                nums1[k--] = nums1[i--];
            }
        }
        while (i >= 0){
            nums1[k--] = nums1[i--];
        }
        while (j >= 0){
            nums1[k--] = nums2[j--];
        }
    }
};
5. 环形链表(NO.141)

题目描述:给定一个链表,判断链表中是否有环。
解题思路:使用双指针,一个指针每次移动一个节点,一个指针每次移动两个节点,如果存在环,那么这两个指针一定会相遇。

class Solution {
public:
    bool hasCycle(ListNode *head) {
        if (head == nullptr)
            return false;
        ListNode* l1 = head;
        ListNode* l2 = head->next;
        while (l1 != nullptr && l2 != nullptr && l2->next != nullptr){
            if (l1 == l2)
                return true;
            l1 = l1->next;
            l2 = l2->next->next;
        }
        return false;
    }
};
6. 最长子序列(NO.524)

题目描述:给定一个字符串和一个字符串字典,找到字典里面最长的字符串,该字符串可以通过删除给定字符串的某些字符来得到(即该字符串是给定字符串的子序列)。如果答案不止一个,返回长度最长且字典顺序最小的字符串。如果答案不存在,则返回空字符串。
解题思路:通过删除字符串 s 中的一个字符能得到字符串 t,可以认为 t 是 s 的子序列,我们可以使用双指针来判断一个字符串是否为另一个字符串的子序列。

class Solution {
public:
    string findLongestWord(string s, vector<string>& d) {
        int max = 0;
        string result;
        for (int i = 0; i < d.size(); i++){
            if (isSubstr(s,d[i]) && d[i].size() >= max){
                if (d[i].size() == max && d[i] > result);
                else{
                    result = d[i];
                    max = d[i].size();
                } 
            }
        }
        return result;
    }
private:
    bool isSubstr(string s, string target){//判断target是不是s的子序列
        int i = 0, j = 0;
        while(i < s.size() && j < target.size()){
            if (s[i] == target[j])
                j++;
            i++;
        }
        return j == target.size();
    }
};
7. 去除字符串中的所有空格
void DeleteSpace(char str[]){
    if (str == nullptr)
        return;
    int i = 0, j = 0;
    while (str[i] != '\0'){
        if (str[i] != ' ')
            str[j++] = str[i++];
        else
            i++;
    }
    str[j] = '\0';
}

你可能感兴趣的:(LeetCode)