一、两数之和
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
//由于不能重复利用,所以容易想到hash
vector<int>res;
if(nums.size() <= 0) return res;
unordered_map<int,int>hashMap;
for(int i = 0; i < nums.size(); i++)
{
//在map中查找x+y=target
if(hashMap.find(target - nums[i]) != hashMap.end())
{
res.push_back(hashMap[target-nums[i]]);
res.push_back(i);
}
hashMap[nums[i]] = i;
}
return res;
}
};
总结:因为要找到两数之和等于target。且不能重复,我们可以使用hash表来一次hash实现。将元素的值作为键值,下标作为实值,每次比较是否有两数之和等于target。有就返回。
二、三数之和
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
//借鉴了一下大神的思路,首先我们如果使用暴力解法那么时间复杂度就是O(N^3)
//我们可以借鉴一下两数之和的思路可以把题目化成a+b= -c,并且要排除重复的子数组答案
vector<vector<int>> res;
if(nums.size() <= 2) return res;
int len = nums.size();
//首先我们需要对数组进行排序然后使用首尾指针来进行。
sort(nums.begin(), nums.end());
for(int i = 0; i < len - 2; i++)
{
//以nums[i]作为参考。双指针来作为a、b
//这个地方要去掉重复的结果,i = 0时不需要判断
if(i > 0 && nums[i] != nums[i - 1]|| i == 0)
{
int beg = i + 1;
int end = len - 1;
while(beg < end)
{
if(nums[beg] + nums[end] == -nums[i])
{
//在此处还要进行排除重复,且要排除i与beg相等情况
if(nums[beg] != nums[beg - 1] || beg == i + 1)
{
res.push_back({nums[i], nums[beg], nums[end]});
}
beg++;
end--;
}
else if(nums[beg] + nums[end] > -nums[i])
{
end--;
}
else
{
beg++;
}
}
}
}
return res;
}
};
总结:本题在我一次笔试题中出现过,那个时候的想法很简单。直接排序暴力解法
所以没过。今天再次遇到了此题,便重新理了一下(借鉴别人~~)思路。
之前写的都没有进行排除重复元素处理,在这里我们需要两次排除重复元素。
1.进入的时候,我们要判断以及排好序的同时,数组不能有重复的数字进行首位指针判断
2.找到符合条件时。我们应该加两次判断进行排除重复元素。
三、两数之和为k的子数组
class Solution {
public:
int subarraySum(vector<int>& nums, int k) {
//需要注意一个元素也是可以。
if(nums.size() <= 0) return 0;
int len = nums.size();
int sum = 0;
int res = 0;
//方法1 暴力解发复杂度为O(N^2);
/* for(int i = 0; i < len; i++)
{
for(int j = i; j < len; j++)
{
sum += nums[j];
if(sum == k)
{
res++;
}
}
sum = 0;
}*/
//方法2 使用map
//如果累计总和,在索引 i 和 j 处相差 k,即 sum[i] - sum[j] = ksum[i]−sum[j]=k,则位于索引 i 和 j 之间的元素之和是 k。
//用hash存储所以可能累计总和出现的个数,sum-k出现的次数即为所求元素。
unordered_map<int, int>map;
map[0] = 1;
for(int i = 0; i < len; i++)
{
sum += nums[i];
if(map.count(sum - k))
res += map[sum - k];
map[sum]++;
}
return res;
}
};
总结:使用unordered_map来对数组中每个可能的sum进行存储,为(sum, time)及和以及出现的次数。每次比较sum-k,若存在,则表示符合条件。
四、返回数组中前K个高频元素
class Solution {
public:
vector<int> topKFrequent(vector<int>& nums, int k) {
//出现频率,我们很容易想到hash表存储每个元素出现的次数
vector<int>res;
if(nums.size() <= 0 || k < 0) return res;
unordered_map<int, int>map;
int len = nums.size();
for(int i = 0; i <len; i++)
{
map[nums[i]]++;
}
//使用优先队列,由于priority_queue默认是大顶堆。我们需要修改一下
//如果不想修改将插入的元素变号即可
priority_queue<pair<int, int>, vector<pair<int, int> >, greater<pair<int, int>> > q;
for(unordered_map<int, int>::iterator it = map.begin(); it != map.end(); it++)
{
//当队列满了情况
if(k == q.size())
{
if(q.top().first < (*it).second)
{
q.pop();
q.push(make_pair((*it).second, (*it).first));
}
}
else
{
q.push(make_pair((*it).second, (*it).first));
}
/* q.push(make_pair((*it).second, (*it).first));
if(q.size() > k) q.pop();*/
}
//以及排序号的队列
while(!q.empty())
{
res.push_back(q.top().second);
q.pop();
}
return res;
}
};
总结:本题思路应该很清晰。
五、TOPk
class Solution {
public:
void swap(int &a, int &b)
{
if(a == b) return;
a ^= b;
b ^= a;
a ^= b;
}
//分割/划分,小的在前面,大的在后面
int CutVector(vector<int>& nums, int left, int right)
{
srand((int)time(0));
int rIndex = rand() % (right - left + 1) + left;
int cut = nums[rIndex];
//将它移动到尾部
swap(nums[right], nums[rIndex]);
int index = left;
for(int i = left; i < right; i++)
{
if(cut >= nums[i])
{
swap(nums[i], nums[index]);
index++;
}
}
swap(nums[right], nums[index]);
return index;
}
//选择
int selectSort(vector<int>&nums, int left, int right, int target)
{
if(right == left) return nums[left];
int cutIndex = CutVector(nums, left, right);
if(cutIndex == target) return nums[cutIndex];
else if(cutIndex > target) return selectSort(nums, left, cutIndex - 1, target);
else return selectSort(nums, cutIndex + 1, right, target);
}
int findKthLargest(vector<int>& nums, int k) {
if(nums.size() <= 0) return -1;
//方法1 使用堆排序时间复杂度为 nlogk
/* priority_queue<int, vector<int>, greater<int> > q;
for(int i = 0; i < nums.size(); i++)
{
q.push(nums[i]);
if(q.size() > k)
q.pop();
}
return q.top();*/
//方法2 选择快排(随机+隔离)
//随机选择,划分,比较那边,进行排序,转换为第size-k小的数
return selectSort(nums, 0, nums.size() - 1, nums.size() - k );
}
};
总结:可以使用1.堆排序,这样排序只需要k次,时间复杂度为N*logK。
2.可以使用随机+隔离的方法。让求top k问题转换为求top_ksmall的问题,
每次选择排序去分治,找到分支点,再进行比较。
六、第三大的数
class Solution {
public:
int thirdMax(vector<int>& nums) {
//找到第三大的数字,来回替换去寻找
if(nums.size() <= 0) return -1;
if(nums.size() == 1) return nums[0];
if(nums.size() == 2) return nums[0] > nums[1] ?nums[0] : nums[1];
int max = INT_MIN;
int second = INT_MIN;
int min = INT_MIN;
bool flg = false;
for(int i = 0; i < nums.size(); i++)
{
if(nums[i] == INT_MIN) flg = true;
if(nums[i] > max)
{
min = second;
second = max;
max = nums[i];
}
else if(nums[i] > second && max > nums[i])
{
min = second;
second = nums[i];
}
else if(second > nums[i]&& nums[i] > min)
{
min = nums[i];
}
}
if(min == INT_MIN)
{
if(second > INT_MIN && flg) return min;
return max;
}
return min;
}
};
总结这里要小心有元素等于INT_MIN。