力扣刷题2020/3/13

这篇博客总结了力扣(LeetCode)上的几道经典题目,包括使用哈希表解决两数之和的问题,避免重复的三数之和解法,利用unordered_map找到和为k的子数组,找出数组中前K个高频元素的策略,以及通过堆排序和随机+隔离方法解决TOPk问题,最后讨论了找出第三大数时需要注意INT_MIN特殊情况。
摘要由CSDN通过智能技术生成

一、两数之和
在这里插入图片描述

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。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值