数组相关题目总结以及算法思想
一、移动元素类型
此类问题的相关的解决思路是引入一个索引变量,不同的数将其保存到数组中,并将其索引加1,此次可以实现空间复杂度为O(1)的方法。相关的题目如下
/**
* 283.移动0
* 给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序
* */
class Solution {
public:
void moveZeroes(vector<int>& nums) {
int index = -1;
for( int i = 0; i < nums.size();i++){
if(nums[i] != 0 )
nums[++index] = nums[i];
}
for( int i = index+1 ; i < nums.size();i++)
nums[i] = 0;
}
};
/**
* 27.移除元素
* 给定一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素,返回移除后数组的新长度。
* */
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
if(nums.size() == 0)
return 0;
int index = -1;
for(int i = 0; i < nums.size();i++){
if(nums[i] != val)
nums[++index] = nums[i];
}
return index+1;
}
};
/**
* 26.删除排序数组的重复选项
* 给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
* 不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
* */
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
if( nums.empty())
return 0;
int index = 0;
for(int i = 0; i < nums.size(); i++){
if(nums[index] != nums[i])
nums[++index] = nums[i];
}
return index+1;
}
};
/**
* 80.删除排序数组中的重复项二
*给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素最多出现两次,返回移除后数组的新长度。
* 不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
* */
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
if( nums.size()<=2)
return nums.size();
int index = 2;
for(int i = 2 ;i < nums.size(); i++){
if(nums[i]!= nums[index-2])
nums[index++]= nums[i];
}
return index;
}
};
二、与排序思想相结合的问题
应用比较广泛的几种排序思想主要有:快速排序(荷兰国旗问题、三路快排),计数排序(适用于数据比较少的状况,比如荷兰国旗问题)、归并排序(引申出多路归并问题,原来的归并为二路归并)
/**
* 88.合并另个有序数组,归并排序的一部分
* 此处即为归并排序的归并部分
* */
void merge(vector<int> &arr,int &L,int &mid, int &R) {
/*
* 关于此函数遇到的问题
* 数组下表赋值必须提前分配数组空间,如果没有提前分配空间就必须使用push_back
* 这是因为push_back会自动new,new出来的会保存在堆上,否自在全栈上
* */
vector<int> help(R - L + 1);
//vector<int>help;//此时的数组大小为空 因此help[i]没有任何意义
int k = 0;
int i = L ;
int j = mid +1;
while (i <= mid&&j <= R)
{
help[k++] = arr[i] < arr[j] ? arr[i++] : arr[j++];
}
while (i <= mid) {
help[k++] = arr[i++];
}
while (j <= R) {
help[k++] = arr[j++];
}
for (k = 0; k < help.size(); k++) {
arr[L + k] = help[k];
}
}
三、双索引
双索引主要用与遍历数组,两个指针指向指向不同的元素,从而协同完成任务。一般情况主要是一指针指向左一指针在最右,向里面对撞(快排也用到了这样的思想)
/**
* 167.有序数组的两数之和
* 给定一个已按照升序排列 的有序数组,找到两个数使得它们相加之和等于目标数。
* 函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2。
* tip:解法一:暴力解法,没用到有序这条件
* 解法二:有序问题应该想到二分搜索
* 解法三:双索引
* */
class Solution {
public:
vector<int> twoSum(vector<int>& numbers, int target) {
int i = 0;
int j = numbers.size()-1;
while( i<j){
if( numbers[i] + numbers[j] > target)
j--;
else if(numbers[i] + numbers[j] < target)
i++;
else
{
vector<int> res = {i+1,j+1};
return res;
}
}
}
};
/**
* 11.盛水最多的容器
* 给定 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) 。
* 在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两
* 条线,使得它们与 x 轴共同构成的容器可以容纳最多的水
* */
class Solution {
public:
int maxArea(vector<int>& height) {
int max = 0;
//i,j两个对撞指针从两边移动
for(int i = 0, j = height.size() - 1; i < j ; ){
int minHeight = height[i] < height[j] ? height[i ++] : height[j --];
max = max(max, (j - i + 1) * minHeight);
}
}
};
四、滑动窗口技术
滑动窗口技术通常适用于求满足一定条件连续子集的问题,一般通过窗口[left,right]移动已达到满足的条件。
/**
* 209.长度最小的子数组
* 给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的连续子数组。
* 如果不存在符合条件的连续子数组,返回 0。
* */
class Solution {
public:
int minSubArrayLen(int s, vector<int>& nums) {
int l = 0;
int r = -1;//nums[l..r]滑动窗口,此时滑动窗口中不应该保留任何值
int sum = 0;
int length = nums.size()+1;
while( l<nums.size())
{
if(r+1 <nums.size() && sum< s)
sum +=nums[++r];
else
sum -= nums[l++];
if(sum >= s)
length = min(length,r-l+1);
}
if( length == nums.size()+1)
return 0;
return length;
}
};
/**
* 3.无重复字符的最长子串
* 给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
* */
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int freq[256] = {0};
int l = 0, r = -1;
int res = 0;
int len = s.length();
while (l < len) {
if ( r+1 <len && freq[s[r+1]] == 0) {
r++;
freq[s[r]] ++;
} else {
freq[s[l]] --;
l++;
}
res = max(res, r-l+1);
}
return res;
}
};
与滑动窗口相关的leetcode题目还有438题与76题
/**
* 438.找到字符串中所有字母异位词
* 给定一个字符串 s 和一个非空字符串 p,找到 s 中所有是 p 的字母异位词的子串,返回这些子串的起始索引。
* 字符串只包含小写英文字母,并且字符串 s 和 p 的长度都不超过 20100。
* */
/*我写的超时了,扎心
* class Solution {
public:
vector<int> findAnagrams(string s, string p) {
vector<int> res;
if(s.length() == 0 || p.length() == 0)
{ return res; }
unordered_map<char,int> mymap;
for(int i = 0; i < p.size(); i++)
mymap[q[i]]++;
int l = 0;
int r = p.size()-1;
int count = 0;
while( r < s.size()){
for(int i = l; i <= r; i++){
if(mymap.find(s[i])!= mymap.end()){
count++;
}
else{
break;
}
if(count == p.size()){
res.push_back(l);
l++;
r++;
} else{
r++;
l++;
}
}
}
return res;
}
};*/
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
vector<int> res;
if(s.empty() || s.size() < p.size())
return res;
int left = 0;
int right = p.size() - 1;
int freqS[26] = { 0 }; //只包含小写字母a~z,数组中保存的是S的子串儿中每个字符出现的次数
int freqP[26] = { 0 };
for (int i = 0; i < p.size(); i++) {
freqS[(int)s[i] - (int)'a']++;
freqP[(int)p[i] - (int)'a']++;
}
while (right < s.size()) {
int i = 0;
for ( ; i < 26; i++) {
if (freqS[i] != freqP[i])
break;
}
if(i == 26)
res.push_back(left);
if(right + 1 == s.size())
return res;
freqS[(int)s[left] - (int)'a']--;
freqS[(int)s[right + 1] - (int)'a']++;
left++;
right++;
}
return res;
}
};
//哈希表实现
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
int plen=p.size(),slen=s.size();
vector<int> res;
if(slen < plen) {return res;}
map<char,int> mp;
for(auto c:p) {
mp[c]++;
}
for(int i=0;i<slen-plen+1;i++){
map<char,int> temp;
string substri=s.substr(i,plen);
for(auto c:substri) temp[c]++;
if(mapequal(temp,mp))
res.push_back(i);
}
return res;
}
bool mapequal(map<char,int>& a,map<char,int>& b){
if(a.size()!=b.size()) return false;
map<char,int>::iterator iter;
for(iter=a.begin(); iter!=a.end(); iter++){
if(b.count(iter->first)==0||b[iter->first]!=iter->second)
return false;
}
return true;
}
};
/**
* 76.最小覆盖子串
* 给定一个字符串 S 和一个字符串 T,请在 S 中找出包含 T 所有字母的最小子串
* */
class Solution {
public:
string minWindow(string s, string t) {
string minString;
if ((s.empty() || t.empty()) || s.size() < t.size())
return minString;
map<char, int> mymap;
for (auto c : t)
mymap[c]++;
int l = 0;
int r = t.size() - 1;
int minLength = s.size() + 1;
while (l < s.size()) {
map<char, int> mymap2;
for (int i = l; i < r + 1; i++)
mymap2[s[i]]++;
if (mapequal(mymap, mymap2)) {
minLength = min(minLength,r-l+1);
minString = s.substr(l,minLength);
l++;
} else
r++;
}
return minString;
}
bool mapequal(map<char,int>& a,map<char,int>& b) {
if (a.size() != b.size()) return false;
map<char, int>::iterator iter;
for (iter = a.begin(); iter != a.end(); iter++) {
if (b.find(iter->first) != b.end() && b.count(iter->first) == iter->second)
return true;
return false;
}
}
};