Tag Archives: 数组

LeetCode Jump Game

55. Jump Game

Given an array of non-negative integers, you are initially positioned at the first index of the array.

Each element in the array represents your maximum jump length at that position.

Determine if you are able to reach the last index.

Example 1:

Input: [2,3,1,1,4]
Output: true
Explanation: Jump 1 step from index 0 to 1, then 3 steps to the last index.

Example 2:

Input: [3,2,1,0,4]
Output: false
Explanation: You will always arrive at index 3 no matter what. Its maximum
             jump length is 0, which makes it impossible to reach the last index.

给定一个数组,每个数字A[i]表示站在i位置最多能跳的步数,初始时站在数组左边,问能否跳到数组右边。
使用贪心策略,维护一个当前能跳到的最远位置maxpos。从左往右遍历数组,如果位置i>maxpos,说明根据之前的数字,没办法跳到位置i,直接break;否则更新maxpos为max(maxpos,A[i]+i)。最后检查maxpos>=n-1。完整代码如下:

class Solution {
public:
    bool canJump(vector<int>& nums)
    {
        int maxpos = 0;
        for (int i = 0; i < nums.size(); ++i) {
            if (i > maxpos)
                break;
            maxpos = max(maxpos, i + nums[i]);
        }
        return maxpos >= nums.size() – 1;
    }
};

本代码提交AC,用时12MS。

二刷。是LeetCode Jump Game II的简化版,依然将问题转化为BFS,如果在BFS某一层时,最远距离没有更新,则表示没法再往前跳了。最后看看最远距离能否到达末尾即可。完整代码如下:

class Solution {
public:
	bool canJump(vector<int>& nums) {
		int n = nums.size(), i = 0;
		int cur_fastest = 0, next_fastest = 0;
		while (i < n) {
			while (i < n&&i <= cur_fastest) {
				next_fastest = max(next_fastest, i + nums[i]);
				++i;
			}
			if (next_fastest >= n - 1)return true;
			if (cur_fastest == next_fastest)return false;
			cur_fastest = next_fastest;
		}
		return false;
	}
};

本代码提交AC,用时4MS。

三刷。在每个位置,我们可以算到该位置能跳到的最远位置maxindex,如果当前位置i在maxindex范围内,说明可以到达i,且能从i进行新的起跳,所以可更新maxindex。如果i超过maxindex,说明无法到达i,也就无法在i的基础上进行新的起跳了。最后看看maxindex能否到达末尾即可。

完整代码如下:

class Solution {
public:
	bool canJump(vector<int>& nums) {
		int maxindex = 0, n = nums.size();
		for (int i = 0; i < n; ++i) {
			if (i > maxindex)break;
			maxindex = max(maxindex, i + nums[i]);
		}
		return maxindex >= n - 1;
	}
};

本代码提交AC,用时8MS。

LeetCode Search for a Range

LeetCode Search for a Range Given an array of integers sorted in ascending order, find the starting and ending position of a given target value. Your algorithm’s runtime complexity must be in the order of O(log n). If the target is not found in the array, return [-1, -1]. For example, Given [5, 7, 7, 8, 8, 10] and target value 8, return [3, 4].


本题要在一个有序数组中求一个数出现的下标区间,如果该数不在数组中,返回[-1,-1],要求用O(lgn)时间复杂度。 很明显用两次二分搜索找到该数的上下区间即可,可以认为是二分搜索的综合题。下界就是第一个不小于target的位置,所以当找到中间值和target相等时,我们要继续往左找。上界就是最后一个不大于target的位置,所以当找到中间值和target相等时,我们要继续往右找。理清了这个思路之后就很好写代码了,如下: [cpp] class Solution { private: int lowerbound(vector<int>& nums, int target) { int l = 0, r = nums.size() – 1; while (l <= r) { int m = l + (r – l) / 2; if (nums[m] < target)l = m + 1; else if (nums[m] >= target)r = m – 1; } return l; } int upperbound(vector<int>& nums, int target) { int l = 0, r = nums.size() – 1; while (l <= r) { int m = l + (r – l) / 2; if (nums[m] <= target)l = m + 1; else if (nums[m] > target)r = m – 1; } return r; } public: vector<int> searchRange(vector<int>& nums, int target) { int lower = lowerbound(nums, target), upper = upperbound(nums, target); if (lower <= upper)return vector<int>({ lower, upper }); else return vector<int>({ -1,-1 }); } }; [/cpp] 本代码提交AC,用时9MS。 注意一点,为啥lowerbound是返回l,而upperbound是返回r呢,对于lowerbound,我们想象一下当中值等于target时,我们不停的往左走,即r=m-1,直到l<=r不再满足,即此时r已经小于l了,也就是说r往左走过头了,使得r对应的值已经小于target了,但此时l对应的值还是等于target,所以根据lowerbound的定义,我们返回l。对于uppperbound也类似,当中值等于target时,不停往右走,即l=m+1,直到不满足l<=r,此时l对应的值已经大于target,而r对应的值还是等于target的,根据upperbound的定义,我们返回r。 其实,STL中已经实现了lower_boundupper_bound其lower_bound和上文的lowerbound含义是一致的,upper_bound稍有不同,STL中的upper_bound是返回第一个大于target的位置,比上文的upperbound要大一个位置。所以如果借助STL的算法,需要对upper_bound返回的位置减1。代码如下: [cpp] class Solution { public: vector<int> searchRange(vector<int>& nums, int target) { if (nums.size() == 0)return vector<int>({ -1,-1 }); vector<int>::iterator lower = lower_bound(nums.begin(), nums.end(), target), upper = upper_bound(nums.begin(), nums.end(), target) – 1; if (lower <= upper)return vector<int>({ lower – nums.begin(), upper – nums.begin() }); else return vector<int>({ -1,-1 }); } }; [/cpp] 本代码提交AC,用时16MS。貌似STL中的实现也是O(lgn)的,看来性能还是有所差别。]]>

LeetCode Teemo Attacking

LeetCode Teemo Attacking In LLP world, there is a hero called Teemo and his attacking can make his enemy Ashe be in poisoned condition. Now, given the Teemo’s attacking ascending time series towards Ashe and the poisoning time duration per Teemo’s attacking, you need to output the total time that Ashe is in poisoned condition. You may assume that Teemo attacks at the very beginning of a specific time point, and makes Ashe be in poisoned condition immediately. Example 1:

Input: [1,4], 2
Output: 4
Explanation: At time point 1, Teemo starts attacking Ashe and makes Ashe be poisoned immediately.
This poisoned status will last 2 seconds until the end of time point 2.
And at time point 4, Teemo attacks Ashe again, and causes Ashe to be in poisoned status for another 2 seconds.
So you finally need to output 4.
Example 2:
Input: [1,2], 2
Output: 3
Explanation: At time point 1, Teemo starts attacking Ashe and makes Ashe be poisoned.
This poisoned status will last 2 seconds until the end of time point 2.
However, at the beginning of time point 2, Teemo attacks Ashe again who is already in poisoned status.
Since the poisoned status won't add up together, though the second poisoning attack will still work at time point 2, it will stop at the end of time point 3.
So you finally need to output 3.
Note:
  1. You may assume the length of given time series array won’t exceed 10000.
  2. You may assume the numbers in the Teemo’s attacking time series and his poisoning time duration per attacking are non-negative integers, which won’t exceed 10,000,000.

这个题目好长,但是很简单。因为数组是递增的,我们把每段的结束时间减去开始时间就是这个数对应的持续时间,结束时间好算,就是timeSeries[i]+duration,但是开始时间要从上一段攻击的结束时间和当前开始时间求最大值,因为有可能上一段攻击还没结束,这一段攻击就开始了。 另外例子中每秒是有持续的时间的,比如第2秒有开始时间和结束时间,不用管它,按正常的第1秒开始攻击,持续2秒,就到第3秒了(题目中是第1秒开始到第1秒结束是1秒,然后第2秒开始到第2秒结束又是1秒,所以持续2秒的话,是在第2秒结束的时候结束的)。 完整代码如下: [cpp] class Solution { public: int findPoisonedDuration(vector<int>& timeSeries, int duration) { int ans = 0, start = 0, end = 0; for (int i = 0; i < timeSeries.size(); ++i) { start = max(start, timeSeries[i]); end = timeSeries[i] + duration; ans += end – start; start = end; } return ans; } }; [/cpp] 本代码提交AC,用时62MS。]]>

LeetCode Insert Delete GetRandom O(1) – Duplicates allowed

LeetCode Insert Delete GetRandom O(1) – Duplicates allowed Design a data structure that supports all following operations in average O(1) time. Note: Duplicate elements are allowed.

  1. insert(val): Inserts an item val to the collection.
  2. remove(val): Removes an item val from the collection if present.
  3. getRandom: Returns a random element from current collection of elements. The probability of each element being returned is linearly related to the number of same value the collection contains.
Example:
// Init an empty collection.
RandomizedCollection collection = new RandomizedCollection();
// Inserts 1 to the collection. Returns true as the collection did not contain 1.
collection.insert(1);
// Inserts another 1 to the collection. Returns false as the collection contained 1. Collection now contains [1,1].
collection.insert(1);
// Inserts 2 to the collection, returns true. Collection now contains [1,1,2].
collection.insert(2);
// getRandom should return 1 with the probability 2/3, and returns 2 with the probability 1/3.
collection.getRandom();
// Removes 1 from the collection, returns true. Collection now contains [1,2].
collection.remove(1);
// getRandom should return 1 and 2 both equally likely.
collection.getRandom();

这一题在LeetCode Insert Delete GetRandom O(1)的基础上,增加了有重复元素的约束。还是用数组+Hash实现,但是Hash表中不再只是存储单个元素的下标了,而是存储所有相同元素的下标的集合,采用最大堆(priority_queue)来存储相同元素的下标集合。 插入时,数值插入到数组末尾,下标插入到hash表中该数值对应的最大堆中,如果堆原来为空的话,说明这是第一次插入该数值。删除时,取出val在数组中的最大下标(priority_queue.top()即为堆中最大值),如果不是数组末尾,则和数组末尾的数值交换,同时更新原数组末尾数值的下标最大堆,最后删掉数组末尾的数。产生随机数同样好办,直接rand下标。 完整代码如下: [cpp] class RandomizedCollection { private: unordered_map<int, priority_queue<int>> hash; vector<int> nums; public: /** Initialize your data structure here. */ RandomizedCollection() { } /** Inserts a value to the collection. Returns true if the collection did not already contain the specified element. */ bool insert(int val) { hash[val].push(nums.size()); nums.push_back(val); return hash[val].size() == 1; } /** Removes a value from the collection. Returns true if the collection contained the specified element. */ bool remove(int val) { if (hash[val].empty())return false; int pos = hash[val].top(); hash[val].pop(); if (pos != nums.size() – 1) { nums[pos] = nums[nums.size() – 1]; hash[nums[pos]].pop(); hash[nums[pos]].push(pos); } nums.pop_back(); return true; } /** Get a random element from the collection. */ int getRandom() { return nums[rand() % nums.size()]; } }; [/cpp] 本代码提交AC,用时85MS。 但是为什么要用最大堆来存储相同数值的下标集合呢,而且最大堆的插入和删除效率不是O(1)的呀,难道是因为相同的数比较少?所以每个数值的下标堆很小,所以插入和删除的复杂度近似为O(1)? 那么能否用数组来存储相同数值的下标集合呢,即用unordered_map<int, vector> hash,答案是不可以。举例如下: 假设经过了6次插入之后,数组为[10,10,20,20,30,30],此时不同数值的下标集合为:
  • 10:[0,1]
  • 20:[2,3]
  • 30:[4,5]
此时删除10,则会把第二个10删掉,同时把末尾的30放到第二个10的位置,数组变为[10,30,20,20,30],此时下标集合为:
  • 10:[0]
  • 20:[2,3]
  • 30:[4,1]
如果此时再次删除10,则会把第一个10删掉,同时把末尾的30放到第一个10的位置,数组变为[30,30,20,20],但是此时的下标集合变为了:
  • 10:[]
  • 20:[2,3]
  • 30:[4,0]
即30的下标集合出现了错误,因为存储下标集合的数据结构是vector,O(1)的删除只能是pop_back(),但是这样并不能保证删掉最大的下标,本来这次我们是要删掉最大的下标4的,但是结果却删了0,依然保存了已不存在的下标4。所以用最大堆的目的就是为了每次删除都删掉最大的下标。 但是最大堆的插入和删除毕竟不是O(1),再次使用Hash存储下标才是真正的O(1)做法,即用unordered_map> hash,这样的话,在上面的例子中,我可以用O(1)的时间删除下标4。 [cpp] class RandomizedCollection { private: unordered_map<int, unordered_set<int>> hash; vector<int> nums; public: /** Initialize your data structure here. */ RandomizedCollection() { } /** Inserts a value to the collection. Returns true if the collection did not already contain the specified element. */ bool insert(int val) { hash[val].insert(nums.size()); nums.push_back(val); return hash[val].size() == 1; } /** Removes a value from the collection. Returns true if the collection contained the specified element. */ bool remove(int val) { if (hash[val].empty())return false; int pos = *hash[val].begin(); hash[val].erase(pos); if (pos != nums.size() – 1) { nums[pos] = nums[nums.size() – 1]; hash[nums[pos]].erase(nums.size() – 1); hash[nums[pos]].insert(pos); } nums.pop_back(); return true; } /** Get a random element from the collection. */ int getRandom() { return nums[rand() % nums.size()]; } }; [/cpp] 本代码提交AC,用时79MS。]]>

LeetCode Insert Delete GetRandom O(1)

LeetCode Insert Delete GetRandom O(1) Design a data structure that supports all following operations in average O(1) time.

  1. insert(val): Inserts an item val to the set if not already present.
  2. remove(val): Removes an item val from the set if present.
  3. getRandom: Returns a random element from current set of elements. Each element must have the same probability of being returned.
Example:
// Init an empty set.
RandomizedSet randomSet = new RandomizedSet();
// Inserts 1 to the set. Returns true as 1 was inserted successfully.
randomSet.insert(1);
// Returns false as 2 does not exist in the set.
randomSet.remove(2);
// Inserts 2 to the set, returns true. Set now contains [1,2].
randomSet.insert(2);
// getRandom should return either 1 or 2 randomly.
randomSet.getRandom();
// Removes 1 from the set, returns true. Set now contains [2].
randomSet.remove(1);
// 2 was already in the set, so return false.
randomSet.insert(2);
// Since 2 is the only number in the set, getRandom always return 2.
randomSet.getRandom();

本题要求实现这样一种数据结构,插入、删除和产生集合中的一个随机数的时间复杂度都是O(1)。 插入和删除要是O(1),可以借助Hash,但Hash表不能以O(1)时间生成随机数。如果把数存在数组中,则能以O(1)的时间生成随机数,但数组的删除不能O(1)。所以可以把Hash和数组结合起来。 数字存储在数组中,Hash表中存储数字在数组中的下标。插入时,插入到数组末尾,同时更新Hash表中的下标。删除时,把数字和数组末尾的数字交换,这样删除数组末尾元素可以用O(1)时间完成,同时也要把Hash表中的下标抹掉,并更新原数组最后一个元素的下标。产生随机数就好办了,知道数组长度,rand下标就好。 完整代码如下: [cpp] class RandomizedSet { private: vector<int> nums; unordered_map<int, int> hash; public: /** Initialize your data structure here. */ RandomizedSet() { } /** Inserts a value to the set. Returns true if the set did not already contain the specified element. */ bool insert(int val) { if (hash.find(val) != hash.end())return false; hash[val] = nums.size(); nums.push_back(val); return true; } /** Removes a value from the set. Returns true if the set contained the specified element. */ bool remove(int val) { if (hash.find(val) == hash.end())return false; int pos = hash[val]; swap(nums[pos], nums[nums.size() – 1]); //下面两句顺序不能乱,因为有可能删除的就是最后一个元素 hash[nums[pos]] = pos; hash.erase(val); nums.pop_back(); return true; } /** Get a random element from the set. */ int getRandom() { return nums[rand() % nums.size()]; } }; [/cpp] 本代码提交AC,用时59MS。]]>

LeetCode Two Sum II – Input array is sorted

167. Two Sum II – Input array is sorted

Given an array of integers that is already sorted in ascending order, find two numbers such that they add up to a specific target number.

The function twoSum should return indices of the two numbers such that they add up to the target, where index1 must be less than index2.

Note:

  • Your returned answers (both index1 and index2) are not zero-based.
  • You may assume that each input would have exactly one solution and you may not use the same element twice.

Example:

Input: numbers = [2,7,11,15], target = 9
Output: [1,2]
Explanation: The sum of 2 and 7 is 9. Therefore index1 = 1, index2 = 2.

本题要在一个已排序数组中找两个数,使得这两个数的和等于一个目标target。这应该是LeetCode Two Sum的简化版吧,因为之前那个题有一种解法就是先对数组排序,然后首尾指针夹逼,现在这个题都排好序了,那还有什么好说的呢,直接借用之前的方法。 完整代码如下:

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

本代码提交AC,用时9MS。

LeetCode Find All Duplicates in an Array

LeetCode Find All Duplicates in an Array Given an array of integers, 1 ≤ a[i] ≤ n (n = size of array), some elements appear twice and others appear once. Find all the elements that appear twice in this array. Could you do it without extra space and in O(n) runtime? Example:

Input:
[4,3,2,7,8,2,3,1]
Output:
[2,3]

长度为n的数组中存储了1~n的若干个数,有的出现了两次,有的只出现了一次,所以还会有一些没有出现。现在要找出所有出现两次的数字。因为不能使用额外的空间,时间复杂度也只能O(n),所以方法和LeetCode Find All Numbers Disappeared in an Array几乎一样。 第一种方法是取相反数,如果发现nums[idx]已经是负数了,说明nums[i]在之前出现过了。完整代码如下: [cpp] class Solution { public: vector<int> findDuplicates(vector<int>& nums) { vector<int> ans; for (int i = 0; i < nums.size(); ++i) { int idx = abs(nums[i]) – 1; if (nums[idx] > 0) { nums[idx] = -nums[idx]; } else { ans.push_back(abs(nums[i])); } } return ans; } }; [/cpp] 本代码提交AC,用时188MS。 第二种方法是把每个数放到它正确的位置,第二遍扫描的时候,如果发现数字和下标对不上,则存储的数字就是出现两次的数字,对应的下标+1就是没出现过的数字(LeetCode Find All Numbers Disappeared in an Array解法2)。完整代码如下: [cpp] class Solution { public: vector<int> findDuplicates(vector<int>& nums) { vector<int> ans; for (int i = 0; i < nums.size(); ++i) { if (nums[nums[i] – 1] != nums[i]) { swap(nums[nums[i] – 1], nums[i]); –i; } } for (int i = 0; i < nums.size(); ++i) { if (nums[i] != i + 1) { ans.push_back(nums[i]); } } return ans; } }; [/cpp] 本代码提交AC,用时129MS。]]>

LeetCode Find All Numbers Disappeared in an Array

LeetCode Find All Numbers Disappeared in an Array Given an array of integers where 1 ≤ a[i] ≤ n (n = size of array), some elements appear twice and others appear once. Find all the elements of [1, n] inclusive that do not appear in this array. Could you do it without extra space and in O(n) runtime? You may assume the returned list does not count as extra space. Example:

Input:
[4,3,2,7,8,2,3,1]
Output:
[5,6]

一个长度为n的数组,保存了1~n中的若干个数,有的出现了两次,有的出现了一次,有的数没有出现,现在要找出那些没有出现的数。要求线性时间复杂度和常数空间复杂度。 这一题有点意思,要求不使用额外的空间,那么唯一的办法就是借助原数组进行操作了。我们可以把数组中的数字都放到它们正确的位置上,然后扫描一遍数组,如果某个位置存储的数字和下标对不上,则这个位置存储的是出现两次的数字,同时说明下标对应那个数字没有出现在原数组中。 这种解法的代码如下: [cpp] class Solution { public: vector<int> findDisappearedNumbers(vector<int>& nums) { vector<int> ans; for (int i = 0; i < nums.size(); ++i) { if (nums[i] != nums[nums[i] – 1]) { swap(nums[i], nums[nums[i] – 1]); –i; } } for (int i = 0; i < nums.size(); ++i) { if (nums[i] – 1 != i) { ans.push_back(i + 1); } } return ans; } }; [/cpp] 本代码提交AC,用时159MS。 还有一种解法是,对于每一个数字nums[i],如果它对应的nums[nums[i]-1]是正数,则把nums[nums[i]-1]赋值为自身的相反数;否则不变。最后重新扫描一遍数组,如果是负数,则说明对应数字存在,否则下标对应数字不存在。这个不难理解,比如题目中的例子,nums[0]=4,则把nums[nums[0]-1]赋值为-nums[nums[0]-1]=-7,这样第二遍扫描nums数组时,发现下标为3的数字是负数,则说明数字4存在于原始数组中。 这种解法的代码如下: [cpp] class Solution { public: vector<int> findDisappearedNumbers(vector<int>& nums) { vector<int> ans; for (int i = 0; i < nums.size(); ++i) { int idx = abs(nums[i]) – 1; nums[idx] = nums[idx]>0 ? (-nums[idx]) : nums[idx]; } for (int i = 0; i < nums.size(); ++i) { if (nums[i] > 0)ans.push_back(i + 1); } return ans; } }; [/cpp] 本代码提交AC,用时142MS。]]>

LeetCode Max Consecutive Ones

LeetCode Max Consecutive Ones Given a binary array, find the maximum number of consecutive 1s in this array. Example 1:

Input: [1,1,0,1,1,1]
Output: 3
Explanation: The first two digits or the last three digits are consecutive 1s.
    The maximum number of consecutive 1s is 3.
Note:
  • The input array will only contain 0 and 1.
  • The length of input array is a positive integer and will not exceed 10,000

求一个数组中最长连续的1的个数。简单题,直接计数,遇到0和1切换的时候记得更新一下结果就好了。完整代码如下: [cpp] class Solution { public: int findMaxConsecutiveOnes(vector<int>& nums) { int ans = 0, cnt = 0; for (int i = 0; i < nums.size(); ++i) { if (nums[i] == 1) { if (i == 0 || nums[i – 1] == 1)cnt++; else if (nums[i – 1] == 0) { ans = max(ans, cnt); cnt = 1; } } } return max(ans, cnt); } }; [/cpp] 本代码提交AC,用时49MS。]]>