写在前面:解决数组问题有一些常见的思路,下面,在这里,对相应问题进行汇总。
一.定义新的索引
283 remove zeros(将数组中的零元素移到末尾)
Given an array nums
, write a function to move all 0
's to the end of it while maintaining the relative order of the non-zero elements.
For example, given nums = [0, 1, 0, 3, 12]
, after calling your function, nums
should be [1, 3, 12, 0, 0]
.
Note:
- You must do this in-place without making a copy of the array.
- Minimize the total number of operations.
思考细节:
1.如何定义删除?从数组中去除?还是放在数组末尾?
2.剩余元素的排列是否要保证原有的相对顺序?
3.是否有空间复杂度的要求?O(1)
思路:
定义新的索引k,保存删除指定元素后的新的元素的位置。
class Solution {
public:
void moveZeroes(vector<int>& nums) {
int k = 0;
for(int i = 0; i < nums.size(); i++){
if(nums[i]) {
if(i!=k) {
nums[k++] = nums[i];
nums[i] = 0;
}
else k++;
}
}
}
/*
void moveZeroes(vector<int>& nums) {
int k = 0;
for(int i = 0; i < nums.size(); i++){
if(nums[i])nums[k++] = nums[i];
}
for(int i = k; i < nums.size(); i++){
nums[i] = 0;
}
}
*/
};
=============================================================================
27 remove element(将数组中的指定元素移除)
Given an array and a value, remove all instances of that value in-place and return the new length.
Do not allocate extra space for another array, you must do this by modifying the input array in-place with O(1) extra memory.
The order of elements can be changed. It doesn't matter what you leave beyond the new length.
Example:
Given nums = [3,2,2,3], val = 3,
Your function should return length = 2, with the first two elements of nums being 2.
思路同上,将零元素换为指定要删除的元素。
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int k = 0;
for(int i = 0; i < nums.size(); i++){
if(nums[i] != val) {
if(i!=k) {
nums[k++] = nums[i];
nums[i] = val;
}
else k++;
}
}
return k;
}
};
=============================================================================
26 remove duplicated from sorted array(将数组中的重复元素移除)
Given a sorted array, remove the duplicates in-place such that each element appear only once and return the new length.
Do not allocate extra space for another array, you must do this by modifying the input array in-place with O(1) extra memory.
Example:
Given nums = [1,1,2],
Your function should return length = 2, with the first two elements of nums being 1 and 2 respectively.
It doesn't matter what you leave beyond the new length.
思路改动:需要移除的元素不是固定的,需要进行更新。
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
int k = 0;
if(nums.size() == 0) return 0;
int prev = nums[0];
nums[k++] = nums[0];
for(int i = 1; i < nums.size(); i++){
if(nums[i] != prev){
nums[k++] = nums[i];
prev = nums[i];
}
}
return k;
}
};
=============================================================================
80 remove duplicated from sorted arrayII(将数组中元素出现超过两次的次数移除)
Follow up for "Remove Duplicates":
What if duplicates are allowed at most twice?
For example,
Given sorted array nums = [1,1,1,2,2,3]
,
Your function should return length = 5
, with the first five elements of nums being 1
, 1
, 2
, 2
and 3
. It doesn't matter what you leave beyond the new length.
思路改动:与26题相比,新数组中元素可以出现的次数,由一次变为两次。
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
int k = 0;
int count = 0;
if(nums.size() == 0) return 0;
int prev = nums[0];
nums[k++] = nums[0];
for(int i = 1; i < nums.size(); i++){
if(nums[i] != prev){
nums[k++] = nums[i];
prev = nums[i];
count = 0;
}
else if(count == 0){
nums[k++] = nums[i];
prev = nums[i];
count = 1;
}
}
return k;
}
};
总结:可以看到,上面几道题的解决思路都是一样的。即,通过定义新的索引,来保存筛选后的元素。
二.利用基础算法
75 sort colours(对只有0,1,2的数组进行排序)
Given an array with n objects colored red, white or blue, sort them so that objects of the same color are adjacent, with the colors in the order red, white and blue.
Here, we will use the integers 0, 1, and 2 to represent the color red, white, and blue respectively.
Note:
You are not suppose to use the library's sort function for this problem.
class Solution {
public:
//计数排序
/*
void sortColors(vector<int>& nums) {
int counts[3] = {0};
for(int i=0; i<nums.size(); i++){
if(nums[i]==0) counts[0]++;
if(nums[i]==1) counts[1]++;
if(nums[i]==2) counts[2]++;
}
int index=0;
for(int i=0; i<sizeof(counts)/sizeof(counts[0]); i++){
for(int j=0; j<counts[i]; j++)
nums[index++]=i;
}
}*/
//三路快排
void sortColors(vector<int>& nums) {
int zero = -1; //nums[0...zero] == 0
int two = nums.size(); //nums[two...n-1] == 2
for(int i=0; i<two; ){
if(nums[i]==1) i++;
else if(nums[i]==2){
two--;
swap(nums[i], nums[two]);
}
else{
assert(nums[i]==0);
zero++;
swap(nums[zero], nums[i]);
i++;
}
}
}
};
=============================================================================
88 Merge Sorted Array(将nums2的数组合并到nums1中)
Given two sorted integer arrays nums1 and nums2, merge nums2 into nums1 as one sorted array.
Note:
You may assume that nums1 has enough space (size that is greater or equal to m + n) to hold additional elements from nums2. The number of elements initialized in nums1 and nums2 are m and n respectively.
tips:归并排序:将数组分成两组A,B,如果这两组组内的数据都是有序的,那么就可以很方便的将这两组数据进行排序。
如何让这两组组内数据有序?
可以将A,B组各自再分为两组。依次类推,当分出来的小组只有一个数组时,可以认为这个小组组内已经达到了有序。
然后再合并相邻的两个小组就可以了。这样,通过先递归来分解数列,再合并数列,就完成了归并排序。
class Solution {
public:
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
int i = m - 1, j = n - 1;
int t = m + n -1;
while(i >= 0 && j >= 0)
{
if(nums1[i] > nums2[j])
nums1[t--] = nums1[i--];
else
nums1[t--] = nums2[j--];
}
while(i >= 0)
{
nums1[t--] = nums1[i--];
}
while(j >= 0)
{
nums1[t--] = nums2[j--];
}
}
};
=============================================================================
215 kth largest element in an array(找到数组中第k大的元素)
Find the kth largest element in an unsorted array. Note that it is the kth largest element in the sorted order, not the kth distinct element.
For example,
Given [3,2,1,5,6,4]
and k = 2, return 5.
Note:
You may assume k is always valid, 1 ≤ k ≤ array's length.
思路:可利用一般排序算法解决,这里利用快速排序。
//快速排序
class Solution {
public:
void quickSort(vector<int>& nums, int left, int right){
if(left > right) return;
int i = left, j = right, temp = nums[left];
while(i < j){
while(nums[j]>=temp && i < j){
j--;
}
while(nums[i]<=temp && i < j){
i++;
}
if(i<j) swap(nums[i],nums[j]);
}
swap(nums[i],nums[left]);
quickSort(nums, left, i-1);
quickSort(nums, i+1, right);
}
int findKthLargest(vector<int>& nums, int k) {
quickSort(nums, 0, nums.size()-1);
return nums[nums.size()-k];
}
};
总结:对于一些基本的排序问题,可以利用基本排序思路来解决。
三.对撞指针
167 two sum ii -input array is sorted (在排序数组中,找到和为sum的两个元素,返回索引。)
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. Please note that 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.
Input: numbers={2, 7, 11, 15}, target=9
Output: index1=1, index2=2
返回索引
1.如果没有解怎样?保证有解
2.如果有多个解怎样?返回任意解
思路:定义两个索引,分别从数组的起点和终点出发。如果两个索引指向的元素的和<sum,则左边索引+1;如果两个索引指向的元素的和>sum,则右边索引-1。充分利用了数组是有序的这一给定条件。
class Solution {
public:
vector<int> twoSum(vector<int>& numbers, int target) {
int l = 0, r = numbers.size() - 1;
while(l < r) //l与r不能指向同一个元素
{
if(numbers[l] + numbers[r] == target){
int res[2] = {l+1, r+1};
return vector<int>(res, res+2);
}
else if(numbers[l] + numbers[r] < target){
l++;
}
else if(numbers[l] + numbers[r] > target){
r--;
}
}
throw invalid_argument("The input has no solution.");
}
};
如果数组无序,该怎么解决呢?
可以利用map进行解决。
1. Two Sum
Given an array of integers, return indices of the two numbers such that they add up to a specific target.
You may assume that each input would have exactly one solution, and you may not use the same element twice.
Example:
Given nums = [2, 7, 11, 15], target = 9, Because nums[0] + nums[1] = 2 + 7 = 9, return [0, 1].
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int, int> m;
vector<int> res;
for(int i = 0; i < nums.size(); i++){
if(m.find(target-nums[i]) != m.end()){
int res[2] = {i, m[target-nums[i]]};
return vector<int>(res, res+2);
}else{
m[nums[i]] = i;
}
}
return res;
}
};
=============================================================================
125 valid palindrome(判断字符串是否是回文串)
Given a string, determine if it is a palindrome, considering only alphanumeric characters and ignoring cases.
For example,"A man, a plan, a canal: Panama"
is a palindrome."race a car"
is not a palindrome.
Note:
Have you consider that the string might be empty? This is a good question to ask during an interview.
For the purpose of this problem, we define empty string as valid palindrome.字符串问题:
思考细节:空字符串如何看?
1.字符的定义?
2.大小写问题?
思路:同样,也可以定义两个索引,分别从字符串的起点和终点出发。过滤掉非字母数字,判断指针所在的位置,两个元素是否相等。
tip:在过滤的时候,指针移动,需要注意,左指针的索引不能超过右指针。
class Solution {
public:
bool isPalindrome(string s) {
int i = 0, j = s.length()-1;
while(i<j){
while(isalnum(s[i]) == false && i<j){
i++;
}
while(isalnum(s[j]) == false && i<j){
j--;
}
if(toupper(s[i]) != toupper(s[j])) return false;
i++;
j--;
}
return true;
}
};
=============================================================================
344 reverse string
Write a function that takes a string as input and returns the string reversed.
Example:
Given s = "hello", return "olleh".
思路:同样,也可以定义两个索引,分别从字符串的起点和终点出发,交换彼此所在位置的元素。
class Solution {
public:
string reverseString(string s) {
int i = 0, j = s.length()-1;
while(i<j)
{
swap(s[i],s[j]);
i++;
j--;
}
return s;
}
};
=============================================================================
345 reverse vowels of string (对字符串的元音字母进行翻转,辅音不变)
Write a function that takes a string as input and reverse only the vowels of a string.
Example 1:
Given s = "hello", return "holle".
Example 2:
Given s = "leetcode", return "leotcede".
Note:
The vowels does not include the letter "y".
思路改进:过滤掉非元音元素。
class Solution {
public:
string reverseVowels(string s) {
int i = 0, j = s.length()-1;
while(i < j){
while((s[i] == 'a' || s[i] == 'e' || s[i] == 'i' || s[i] == 'o' || s[i] == 'u' || s[i] == 'A' || s[i] == 'E' || s[i] == 'I' || s[i] == 'O' || s[i] == 'U') && (s[j] == 'a' || s[j] == 'e' || s[j] == 'i' || s[j] == 'o' || s[j] == 'u' || s[j] == 'A' || s[j] == 'E' || s[j] == 'I' || s[j] == 'O' || s[j] == 'U') && i < j){
swap(s[i],s[j]);
i++;
j--;
}
while((s[i] != 'a' && s[i] != 'e' && s[i] != 'i' && s[i] != 'o' && s[i] != 'u' && s[i] != 'A' && s[i] != 'E' && s[i] != 'I' && s[i] != 'O' && s[i] != 'U') && i < j){
i++;
}
while((s[j] != 'a' && s[j] != 'e' && s[j] != 'i' && s[j] != 'o' && s[j] != 'u' && s[j] != 'A' && s[j] != 'E' && s[j] != 'I' && s[j] != 'O' && s[j] != 'U') && i < j){
j--;
}
}
return s;
}
};
=============================================================================
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.
Note: You may not slant the container and n is at least 2.
思路:由于该数组是无序的,两个索引向内移动,会导致”容器“宽度降低。那么,向内移动的条件就只能是内部的元素大于外部的元素。
class Solution {
public:
int maxArea(vector<int>& height) {
int i = 0, j = height.size()-1;
int water = -1;
while(i < j){
int h = min(height[i], height[j]);
water = max(water, (j-i) * min(height[i], height[j]));
while(height[i] <= h && i < j) i++;
while(height[j] <= h && i < j) j--;
}
return water;
}
};
总结:当问题可以简化为对数组中的两个元素进行操作的时候,可以尝试使用对撞指针的方法。
四.滑动窗口
滑动窗口需要考虑的关键:初始信息,以及窗口滑动的条件 : 子窗口尚未满足条件,子窗口向右扩展;子窗口已满足条件,子窗口向左推移。
209 minimum size subarray sum(找到满足元素和>=s的最小连续子数组)
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.
For example, given the array [2,3,1,2,4,3]
and s = 7
,
the subarray [4,3]
has the minimal length under the problem constraint.
思考细节:
1.什么叫子数组,连续子数组
2.如果没有解怎么办,返回0
思路:定义滑动窗口,去匹配相应的连续子数组。
//时间复杂度:o(n)
//空间复杂度:o(1)
class Solution {
public:
int minSubArrayLen(int s, vector<int>& nums) {
int l = 0, r = -1; //nums[1...r]为我们的滑动窗口
int sum = 0;
int res = nums.size() + 1;//最小值一定小于nums.size()+1
//滑动窗口左边可以取值,右边也可以取值。
while(l < nums.size())
{
if(r+1 < nums.size() && sum < s){
r++;
sum += nums[r];
}else{
//如果sum>=s,那么可以减小窗口的大小。
sums -= nums[l];
l++;
}
if(sum >= s)
res = min(res, r-l+1);
}
if(res == nums.size() + 1) return 0;
return res;
}
};
=============================================================================
3longest substring without repeating characters(找到没有重复字符的最长子串)
Given a string, find the length of the longest substring without repeating characters.
Examples:
Given "abcabcbb"
, the answer is "abc"
, which the length is 3.
Given "bbbbb"
, the answer is "b"
, with the length of 1.
Given "pwwkew"
, the answer is "wke"
, with the length of 3. Note that the answer must be a substring, "pwke"
is a subsequence and not a substring.
思考细节:寻找没有重复字母的最长子串
1.字符集?只有字母?数字+字母?ascii?
2.大小写是否敏感?
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int freq[256] = {0};
int l = 0, r = -1;//滑动窗口为s[l,r]
int res = 0;
while(l < s.size()){
if(r+1<s.size() && freq[s[r+1]]==0){
r++;
freq[s[r]]++;
}else{
freq[s[l]]--;
l++;
}
res = max(res, r-l+1);
}
return res;
}
};
tip:
C++数组的下标可以是字符的。解释如下:
C++中字符在计算机内存储的是字符的ASCII码;
而ASCII码实质是数字,例如‘a’是97,‘A'是65;
如果用字符作为下标,实质就是用该字符的ASCII码作为下标;
但是在用字符作为下标时没有数字直观,容易引起数组越界,因此不建议这样用。
=============================================================================
438 find all anagrams in a string
Given a string s and a non-empty string p, find all the start indices of p's anagrams in s.
Strings consists of lowercase English letters only and the length of both strings s and p will not be larger than 20,100.
The order of output does not matter.
Example 1:
Input:
s: "cbaebabacd" p: "abc"
Output:
[0, 6]
Explanation:
The substring with start index = 0 is "cba", which is an anagram of "abc".
The substring with start index = 6 is "bac", which is an anagram of "abc".
Example 2:
Input:
s: "abab" p: "ab"
Output:
[0, 1, 2]
Explanation:
The substring with start index = 0 is "ab", which is an anagram of "ab".
The substring with start index = 1 is "ba", which is an anagram of "ab".
The substring with start index = 2 is "ab", which is an anagram of "ab".
思考细节:anagrams:两个字符串包含的字母是相同的,则会互为anagrams。
1.字符集范围?英文小写字母
2.返回的解的顺序?任意
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
int l = 0, r = -1;
int head = l, count = p.size();
int n = s.size();
vector<int> map(128, 0);
vector<int> res;
for(auto c:p) map[c]++;
while(l < n){
if(r+1<n && count>0){
r++;
if(map[s[r]] > 0){
count = count-1;
}
map[s[r]]--;
}else{
if(map[s[l]] == 0){
count = count+1;
}
map[s[l]]++;
l++;
}
if(count == 0){
if(p.size() == r-l+1){
res.push_back(l);
}
}
}
return res;
}
};
=============================================================================
76 minimum window substring
Given a string S and a string T, find the minimum window in S which will contain all the characters in T in complexity O(n).
For example,
S = "ADOBECODEBANC"
T = "ABC"
Minimum window is "BANC"
.
Note:
If there is no such window in S that covers all characters in T, return the empty string ""
.
If there are multiple such windows, you are guaranteed that there will always be only one unique minimum window in S.
思路:用滑动窗口对字符串s进行滑动,如果找到了一个p中的元素,则”寻找指标“-1。
class Solution {
public:
string minWindow(string s, string t) {
int l = 0, r = -1;
int head = l, count = t.size(), n = s.size();
int res = n + 1;
if(count > n) return "";
vector<int> map(128, 0);
for(auto c:t) map[c]++;
while(l < n){
if(r+1 < n && count > 0){
r++;
if(map[s[r]] > 0){
count = count - 1;
}
map[s[r]]--;
}else{
if(map[s[l]] == 0){
count = count + 1;
}
map[s[l]]++;
l++;
}
if(count == 0){
if(res > r - l + 1){
head=l;
res = min(res, r - l + 1);
}
}
}
if(res == n + 1) return "";
return s.substr(head, res);
}
};
总结:对于连续子数组问题,可以尝试用滑动窗口法解决。