LeetCode 常见--双指针--问题,Python和C++实现

双指针问题

1. 有序数组Two Sum

LeetCode 167. Two Sum II - Input arrray is sorted (Easy)

分析:设置left,right两个指针,分别指向数组的最左边和最右边元素。while循环,循环条件为left

# Python
class Solution:
    def twoSum(self, numbers: List[int], target: int) -> List[int]:
        left, right = 0, len(numbers)-1
        while left < right:
            tmp = numbers[left] + numbers[right]
            if tmp < target:
                left += 1
            elif tmp > target:
                right -= 1
            else:
                res = [left+1, right+1]
                break
        return res

# c++
class Solution {
public:
    vector<int> twoSum(vector<int>& numbers, int target) {
        int left = 0, right = numbers.size()-1;
        vector<int> ret;
        while (left < right) {
            int sum = numbers[left] + numbers[right];
            if (sum < target)
                left++;
            else if (sum > target)
                right--;
            else {
                ret.push_back(++left);
                ret.push_back(++right);
                break;
            }
        }
        return ret;
    }
};

2. 两个整数的平方和

LeetCode 633. Sum of Square Numbers (Easy)

本题与问题1具有相同计算流程,区别在于target变为了两数平方和。

Note:这里可以将right直接初始化为 c \sqrt {c} c 。根据根号大小的性质可以减少不必要的操作。

# Python
class Solution:
    def judgeSquareSum(self, c: int) -> bool:
        left, right = 0, int(math.sqrt(c))
        while left <= right:
            if left * left + right * right < c:
                left += 1
            elif left * left + right * right > c:
                right -= 1
            else:
                return True
        return False

# C++
class Solution {
public:
    bool judgeSquareSum(int c) {
        long int left = 0, right = sqrt(c);
        while (left <= right) {
            if (left * left + right * right < c)
                left++;
            else if (left * left + right * right > c)
                right--;
            else {
                return true;
            }
        }
        return false;
    }
};

3. 翻转字符串中的元音字符

LeetCode 345. Reverse Vowels of a String (Easy)

依然使用的是双指针从两边到中间的收缩过程。当left和right都指向一个元音字符的时候进行交换操作即可。

# Python
class Solution:
    def reverseVowels(self, s: str) -> str:
        left, right = 0, len(s)-1
        mySet = ('a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U')
        res = list(s)
        
        while left < right:
            if not res[left] in mySet:
                left += 1
            if not res[right] in mySet:
                right -= 1
            if res[left] in mySet and res[right] in mySet:
                tmp = res[left]
                res[left] = res[right]
                res[right] = tmp
                left += 1
                right -= 1
        return ''.join(res)
    
# C++
# 目前对C++的STL还不是很熟悉,这可能不是最好的方法。还有一种思路是直接使用数组存储,判断
# 的时候只要判断字符ASCII值对应索引即可。
class Solution {
public:
    string reverseVowels(string s) {
        int left = 0, right = s.size()-1;
        map<char, char> myMap = {{'a', 1}, {'e', 1}, {'i', 1}, {'o', 1}, {'u', 1}, {'A', 1}, {'E', 1}, {'I', 1}, {'O', 1}, {'U', 1}};
        
        while (left < right) {
            map<char, char>::iterator findLeft = myMap.find(s[left]);
            map<char, char>::iterator findRight = myMap.find(s[right]);
            if (findLeft == myMap.end())
                left++;
            if (findRight == myMap.end())
                right--;
            if (findLeft != myMap.end() && findRight != myMap.end()) {
                char tmp = s[left];
                s[left] = s[right];
                s[right] = tmp;
                left++;
                right--;
            }
        }
        return s;
    }
};

4. 回文字符串

LeetCode 680. Valid Palindrome II (Easy)

双指针判断回文串实现非常简单。本题是删除最多一个元素能否形成回文串。那么如果本身就是回文串,不需要进行删除操作,直接返回True即可。当判断过程中遇见不是回文的情况,需要作出如下判断。

有串如:[a, b, c, d, a]

当left指向b,right指向d的时候,while循环第一次遇见不相等的情况,此时只需要判断[b, c][c, d]两个子串,是不是回文串,即可,如果是,那么删除其中一个字符剩下的字符就是回文串,如果两个都不是回文串,那么整个字符串仅仅删除一个字符不可能构成回文串。

# Python
class Solution:
    def validPalindrome(self, s: str) -> bool:
        left, right, count = 0, len(s)-1, 0
        
        while left < right:
            if s[left] == s[right]:
                left += 1
                right -= 1
            else:
                return (self.isPalindrome(s[left:right]) or 
                        self.isPalindrome(s[left+1:right+1]))
        return True
    
    def isPalindrome(self, s):
        left, right = 0, len(s)-1
        while left < right:
            if s[left] == s[right]:
                left += 1
                right -= 1
            else:
                return False
        return True
# C++
class Solution {
public:
    bool validPalindrome(string s) {
        int left = 0, right = s.size() - 1;
        while (left < right) {
            if (s[left] == s[right]) {
                left++;
                right--;
            } else
                return (isPalindrome(s.substr(left, right-left)) ||
                        isPalindrome(s.substr(left+1, right-left)));  
        }
        return true;
    }
    
    bool isPalindrome(string s) {
        int left = 0, right = s.size() - 1;
        while (left < right) {
            if (s[left] == s[right]) {
                left++;
                right--;
            } else
                return false;
        }
        return true;
    }
};

5. 归并两个有序数组

LeetCode 88. Merge Sorted Array (Easy)

本题思路交简单,但是两种实现方法时间复杂度不一样

方法一:(时间复杂度 O ( n 2 ) O(n^2) O(n2)

类似于归并两个链表的操作,从前往后依次把nums2中的数,插入到nums1中去。这里需要注意,每次插入数据,后面的数据都要向后移动一位。这样造成一个双重循环。(由于链表每次修改数据,不需要修改后面的数据,所以时间复杂度为 O ( 1 ) O(1) O(1),但是数组需要后面每一位都修改,因此这样归并的时间消耗大大增加。)

方法二:(时间复杂度 O ( m + n ) O(m+n) O(m+n)

如果从后向前归并数据,两个指针分别指向nums1和nums2的末尾m-1, n-1处,他们的结果比较大小,最后归并到nums1的m+n-1处,每次归并,并不需要修改其他的数据。

# Python
class Solution:
    def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
        """
        Do not return anything, modify nums1 in-place instead.
        """
        up, middle, down = m - 1, m + n - 1 ,n - 1
        
        while up >= 0 and down >= 0:
            if nums1[up] >= nums2[down]:
                nums1[middle] = nums1[up]
                up -= 1
                middle -= 1
            else:
                nums1[middle] = nums2[down]
                down -= 1
                middle -= 1
        while down >= 0:
                nums1[middle] = nums2[down]
                down -= 1
                middle -= 1
# C++
class Solution {
public:
    void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
        int up = m - 1, down = n - 1, middle = m + n - 1;
        
        while (up >= 0 && down >= 0) {
            if (nums1[up] >= nums2[down]) {
                nums1[middle] = nums1[up];
                up--;
                middle--;
            } else {
                nums1[middle] = nums2[down];
                down--;
                middle--;
            }
        }
        while (down >= 0) {
            nums1[middle] = nums2[down];
            down--;
            middle--;
        }
    }
};

6. 判断链表是否存在环

LeetCode 141. Linked List Cycle (Easy)

思路:如果一个链表有环,遍历链表一定无法退出这个环。设置两个指针,一个快(每次走两步)一个慢(每次走一步),那么这两个指针一定会相遇。如果两个指针都走到了终点,那么该链表一定没有环。

# Python
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def hasCycle(self, head: ListNode) -> bool:
        if not head: return False
        slow, fast = head, head.next
        while slow and fast and fast.next:
            if slow == fast:
                return True
            else:
                slow = slow.next
                fast = fast.next.next
        return False
# C++
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    bool hasCycle(ListNode *head) {
        if (!head) return false;
        ListNode *slow = head, *fast = head -> next;
        while (slow && fast && fast -> next) {
            if (slow == fast)
                return true;
            slow = slow -> next;
            fast = fast -> next -> next;
        }
        return false;
    }
};

7. 带限制条件的最长子序列

LeetCode 524. Longest Word in Dictionary through Deleting (Medium)

分析:本题看似繁琐,思路其实很清晰。

如果a字符串删除部分字符可以得到b字符串,那么可以理解为b是a的一个“带限制条件的子串”。设置两个指针i, j分别指向a的0索引出和b的0索引处。while循环遍历两个字符串,循环结束时,如果j的大小刚好为b串的长度,那么符合题目条件。

这时候,判断b串的长度以及字典序是不是最长,最小即可。(字典序:按照字符串每个字符的大小进行比较)

# Python
class Solution:
    def findLongestWord(self, s: str, d: List[str]) -> str:
        res = ''
        for subs in d:
            if len(res) > len(subs): continue
            i, j = 0, 0
            while i < len(s) and j < len(subs):
                if s[i] == subs[j]:
                    i += 1
                    j += 1
                else:
                    i += 1
            if j == len(subs):
                if len(subs) > len(res):
                    res = subs
                if len(subs) == len(res):
                    if subs < res:
                        res = subs
        return res

# C++
//留坑

你可能感兴趣的:(LeetCode,算法,leetcode,算法)