双指针-Leetcode

3. Longest Substring Without Repeating Characters

思路:假设s[i,j-1]是没有重复字符的子串,那么这个时候只需要s[j]没有在s[i,j-1]中出现过即可,所以可以引入hash表。这里还是用双指针一个记录子串开头,一个记录子串结尾。

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        //时间复杂度O(2n),空间复杂度O(min(m,n))
        if (s.empty()) return 0;
        unordered_set <int> set;
        int len = INT_MIN;
        int i = 0, j = 0;
        while (i < s.size() && j < s.size()) {
            if (!set.count(s[j])) {
                set.insert(s[j++]);
                len = max(len, j-i);
            } 
            else
                set.erase(s[i++]);
        }

        return len;

    }
};

上面每次遇到重复字符的时候,i就开始向后移动一直移到没有重复字符的地方,所以可以考虑用hash_map记录字符和位置,然后直接移到没有重复字符的位置,而不是累加。

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        //时间复杂度O(n),空间复杂度O(min(m,n))
        if (s.empty()) return 0;
        unordered_map <char, int> map;
        int len = INT_MIN;
        int i = 0, j = 0;
        while (i < s.size() && j < s.size()) {
            if (map.find(s[j]) != map.end()) 
               i = max(map[s[j]],i); 
            len = max(len, j - i + 1);
            map[s[j]] = j + 1;
            ++j;
        }
        return len == INT_MIN ? 0 : len;
    }
};

11. Container With Most Water

Given n non-negative integers a1, a2, …, an, where each represents a point at coordinate (i, ai). n vertical lines are drawn such that the two endpoints of line i is at (i, ai) and (i, 0). Find two lines, which together with x-axis forms a container, such that the container contains the most water.

思路:双指针法,短的一边会引起水量减少,设置两个点,一个从开头,一个从结尾,比较两边,把短的一边向中间靠拢。

class Solution {
public:
    int maxArea(vector<int>& height) {
        if (height.empty() || height.size() == 1) 
            return 0;
        int start = 0, end = height.size() - 1;
        int water = 0;
        while (start < end) {
            while (height[start] == 0) ++start;
            while (height[end] == 0) --end;
            water = max(water, (end - start) * min(height[end], height[start]));
            if (height[start] < height[end]) ++start;
            else --end;
        }    
        return water;
    }
};

26. Remove Duplicates from Sorted Array

给定一个有序数组,移除其中的重复元素,保证每个数字只出现过一次,返回移除后的数组长度。
思路:两个指针,一个遍历,一个记录重复元素个数,注意如果要AC必须修改原数组。

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        int cnt = 0;
        if (nums.empty()) return cnt;
        for (int i = 1; i < nums.size(); ++i) {
            if (nums[i] ==  nums[i-1]) cnt++;
            else nums[i-cnt] = nums[i];
        }
        return nums.size() - cnt;
    }
};

209. Minimum Size Subarray Sum

双指针:
Given an array of n positive integers and a positive integer s, find the minimal length of a contiguous subarray of which the sum ≥ s. If there isn’t one, return 0 instead.

class Solution {
public:
    int minSubArrayLen(int s, vector<int>& nums) {
        int numsLen = nums.size();
        int minLen = numsLen + 1;
        int j = 0, sum = 0;
        for (int i = 0; i < numsLen; ++i) {
            while (sum < s && j < numsLen) {
                sum += nums[j];
                j++;
            }
            if (sum < s) break;//防止和不够的时候
            minLen = min(minLen, j - i);//j-i不用+1
            sum -= nums[i];
        }
        return minLen == numsLen + 1 ? 0 : minLen;
    }
};

30. Substring with Concatenation of All Words

题目:给定一个长字符串,再给定几个长度相同的单词,让我们找出串联给定所有单词的子串的起始位置
思路一:用两个哈希表,一个哈希表先把所有的单词存进去,然后从字符串开头开始一个个字符遍历,每次找出给定单词长度的子串,看其是否在第一个哈希表里,如果没有,则break,如果有,则加入第二个哈希表,但相同的词只能出现一次,所以比较两个hash表中子串出现的次数如果多了,也break。如果正好匹配完给定单词集里所有的单词,则把i存入结果中。

class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        vector<int> res;
        if (words.empty() || s.empty() || s.size() < words.size()) return res;
        unordered_map <string, int> hash_words;
        for (int i = 0; i < words.size(); ++i) {
            ++hash_words[words[i]];
        }
        int wNum = words.size(), wSize = words[0].size();
       // int m = (int)s.size() - wNum*wSize;
        for (int i = 0; i <= (int)s.size() - wNum*wSize; ++i) {
            unordered_map<string, int> hash_string;
            int j;//j记录第几个单词
            for(j = 0; j < wNum; ++j) {
                string tmp = s.substr(i + wSize * j, wSize);
                if (hash_words.find(tmp) == hash_words.end())
                    break;
                ++hash_string[tmp];
                if (hash_string[tmp] > hash_words[tmp]) 
                    break;
            }
            if (j == wNum) res.push_back(i);
        }
        return res;
    }
};

思路二:O(n)时间复杂度的方法:不再是一个字符一个字符的遍历,而是一个词一个词的遍历,比如根据题目中的例子,字符串s的长度n为18,words数组中有两个单词(cnt=2),每个单词的长度len均为3,那么遍历的顺序为0,3,6,8,12,15,然后偏移一个字符1,4,7,9,13,16,然后再偏移一个字符2,5,8,10,14,17,这样就可以把所有情况都遍历到,我们还是先用一个哈希表m1来记录words里的所有词,然后我们从0开始遍历,用left来记录左边界的位置,count表示当前已经匹配的单词的个数。然后我们一个单词一个单词的遍历,如果当前遍历的到的单词t在m1中存在,那么我们将其加入另一个哈希表m2中,如果在m2中个数小于等于m1中的个数,那么我们count自增1,如果大于了,说明后面已经不连续了,所以我们要移动左边界left的位置,我们先把第一个词t1=bar取出来,然后将m2[t1]自减1,如果此时m2[t1]

class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        if (s.empty() || words.empty()) return {};
        vector<int> res;
        int n = s.size(), cnt = words.size(), len = words[0].size();
        unordered_map<string, int> m1;
        for (string w : words) ++m1[w];
        for (int i = 0; i < len; ++i) {
            int left = i, count = 0;
            unordered_map<string, int> m2;
            for (int j = i; j <= n - len; j += len) {
                string t = s.substr(j, len);
                if (m1.count(t)) {
                    ++m2[t];
                    if (m2[t] <= m1[t]) {
                        ++count;
                    } else {
                        while (m2[t] > m1[t]) {
                            string t1 = s.substr(left, len);
                            --m2[t1];
                            if (m2[t1] < m1[t1]) --count;
                            left += len;
                        }
                    }
                    if (count == cnt) {
                        res.push_back(left);
                        --m2[s.substr(left, len)];
                        --count;
                        left += len;
                    }
                } else {
                    m2.clear();
                    count = 0;
                    left = j + len;
                }
            }
        }
        return res;
    }
};

参考资料:http://www.cnblogs.com/grandyang/p/4521224.html

33. Search in Rotated Sorted Array

题目:在旋转的有序数组中查找,如果查找失败,返回-1;
(i.e., 0 1 2 4 5 6 7 旋转为 4 5 6 7 0 1 2).
思路:先找到最小数,然后接着查找,主要是二分

class Solution {
public:
    int partition(vector<int>& nums, int target, int i, int j) {
        if (i == j) return nums[i] == target ? i : -1;
        if (nums[j] < target || nums[i] > target || i > j) return -1;
        while (j >= i) {
            int mid = (i + j) / 2;
            if (nums[mid] > target) 
                j = mid - 1;
            else if (nums[mid] < target) 
                i = mid + 1;
            else
                return mid;
        }
        return -1;
    }
    int search(vector<int>& nums, int target) {
        if (nums.empty()) return -1;
        int i = 0;
        int j = nums.size() - 1;
        //注意只有一个元素的情况
        if (i == j) return nums[i] == target ? 0 : -1;
        int mid = i;
        while (nums[i] >= nums[j]) {
            if (j - i == 1)
                {
                    mid = j;
                    break;
                }
            mid = (i + j) / 2;
            if (nums[mid] >= nums[i]) 
                i = mid;
            else if (nums[mid] <= nums[j]) 
                j = mid;
        }

        if (mid == 0) return partition(nums, target, 0, nums.size()-1);
        int prev = partition(nums, target, 0, mid-1);
        int end = partition(nums, target, mid, nums.size()-1);
        return prev == -1 ? end : prev;
    }
};

别人的代码,优雅版:

class Solution {
public:
    int search(int A[], int n, int target) {
        int lo=0,hi=n-1;
        // find the index of the smallest value using binary search.
        // Loop will terminate since mid < hi, and lo or hi will shrink by at least 1.
        // Proof by contradiction that mid < hi: if mid==hi, then lo==hi and loop would have been terminated.
        while(lo<hi){
            int mid=(lo+hi)/2;
            if(A[mid]>A[hi]) lo=mid+1;
            else hi=mid;
        }
        // lo==hi is the index of the smallest value and also the number of places rotated.
        int rot=lo;
        lo=0;hi=n-1;
        // The usual binary search and accounting for rotation.
        while(lo<=hi){
            int mid=(lo+hi)/2;
            //偏移的角度去理解
            int realmid=(mid+rot)%n;
            if(A[realmid]==target)return realmid;
            if(A[realmid]<target)lo=mid+1;
            else hi=mid-1;
        }
        return -1;
    }
};

另外思路:每次在部分有序的数组中进行查找

public class Solution {
public int search(int[] A, int target) {
    int lo = 0;
    int hi = A.length - 1;
    while (lo < hi) {
        int mid = (lo + hi) / 2;
        if (A[mid] == target) return mid;
        //mid前面有序
        if (A[lo] <= A[mid]) {
            if (target >= A[lo] && target < A[mid]) {
                hi = mid - 1;
            } else {
                lo = mid + 1;
            }
        }//mid后面有序 
        else {
            if (target > A[mid] && target <= A[hi]) {
                lo = mid + 1;
            } else {
                hi = mid - 1;
            }
        }
    }
    return A[lo] == target ? lo : -1;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值