【无标题】

目录

数组中的第k个元素

IPO

[查找最小 k 堆数字和](#查找最小 k 堆数字和)

数据流的中位数

二进制求和

颠倒二进制位

[位 1 的个数](#位 1 的个数)

只出现一次的数字

只出现一次的数字二

数字范围按位与

回文数

加一

阶乘后的零

[x 的平方根](#x 的平方根)

Pow(x,n)

直线上最多的数

爬楼梯

打家劫舍

单词拆分


数组中的第k个元素

解法:优先级队列

cpp 复制代码
class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        priority_queue<int,vector<int>,greater<int>> q;
        for(auto& num: nums)
        {
            q.push(num);
            if(q.size() > k)
            {
                q.pop();
            }
        }
        return q.top();
    }
};

IPO

解法:优先级队列

思路(有问题):优先级队列按照比 w 小的,最大的 profit 排在栈顶,但这样实现出来只有第一次是对的,第二次的参照物就不是 w,而是 w+=profit

cpp 复制代码
    int sum = 0;
class Solution {
public:
    struct cmp
    {
        bool operator()(const pair<int,int>& p1, const pair<int,int>& p2)
        {
            if(p1.second <= sum && p2.second <= sum)
            {
                return p1.first - p1.second < p2.first - p2.second;
            }
            if(p1.second <= sum)
            {
                return false;
            }
            return true;
        }
    };
    int findMaximizedCapital(int k, int w, vector<int>& profits, vector<int>& capital) {
        sum = w;
        priority_queue<pair<int,int>,vector<pair<int,int>>,cmp> q;
        int n = profits.size();
        for(int i = 0; i < n; i++)
        {
            q.emplace(profits[i], capital[i]);
        }
        int ret = w;
        while(k--)
        {
            // 这样的话,第一次最优,其他就不一定了
            ret += q.top().first;
            q.pop();
        }
        return ret;
    }
};

正确思路:使用 vector 按升序排序 capital,每次那最大 profit 之前,把比 w 小的数放到优先级队列中,每次取最大后更新w = w + st.top()...直到取了 k 个后退出循环,返回 w

cpp 复制代码
class Solution {
public:
    int findMaximizedCapital(int k, int w, vector<int>& profits, vector<int>& capital) {
        vector<pair<int,int>> v;
        int n = profits.size();
        for(int i = 0; i < n; i++)
        {
            v.emplace_back(capital[i], profits[i]);
        }
        sort(v.begin(), v.end(), [](const pair<int,int>& p1, const pair<int,int>& p2) -> bool{
            return p1.first < p2.first;
        });
        priority_queue<int> st;
        int i = 0, sum = w;
        while(k--)
        {
            while(i < n && v[i].first <= w)
            {
                st.push(v[i++].second);
            }
            if(!st.empty())
            {
                w += st.top();
                st.pop();
            }
            else
            {
                break;
            }
        }
        return w;
    }
};

查找最小 k 堆数字和

解法:贪心 + 最小堆

假设 nums1 = [1, 7, 11], nums2 = [2, 4, 6]。我们可以构建一个和的矩阵:

最小的数字和 (i,j)在左上角,下一个最小的数字和在(i + 1,j)或者(i,j+1),所以:可以先把第一列按照 nums[i] + nums2[j],i,j 的格式放在最小堆中(按照和进行排优先级),每次拿完堆顶后,把(i,j + 1) 坐标的值入堆...

cpp 复制代码
class Solution {
public:
    struct cmp
    {
        bool operator()(const pair<int,pair<int,int>>& p1, const pair<int,pair<int,int>>& p2)
        {
            return p1.first > p2.first;
        }
    };
    vector<vector<int>> kSmallestPairs(vector<int>& nums1, vector<int>& nums2, int k) {
        priority_queue<pair<int,pair<int,int>>,vector<pair<int,pair<int,int>>>,cmp> st;
        int n = nums1.size(), m = nums2.size();
        for(int i = 0; i < n; i++)
        {
            st.emplace(nums1[i] + nums2[0], make_pair(i, 0));
        }
        vector<vector<int>> ret(k);
        for(int i = 0; i < k; i++)
        {
            auto tmp = st.top().second;
            st.pop();
            int x = tmp.first, y = tmp.second;
            ret[i].push_back(nums1[x]);
            ret[i].push_back(nums2[y]);
            // 越界就存了
            if(x >=n || (y + 1) >=m)
            {
                continue;
            }
            st.emplace(nums1[x] + nums2[y + 1],make_pair(x, y + 1));
        }
        return ret;
    }
};

思路 2:双指针 + 二分

使用二分找一段区间,使得该区间内数字组合之和 >= k,使用右区间模板来解决

使用双指针来统计:在某个区间中数字组合个数 <= mid

cpp 复制代码
class Solution {
public:
    int n,m;
    long long LessMid(int mid, vector<int>& nums1, vector<int>& nums2)
    {
        int pos1 = 0, pos2 = m - 1, sum = nums1[0] + nums2[m - 1];
        long long cnt = 0;
        while(pos1 < n && pos2 >= 0)
        {
            while(pos2 >= 0 && nums1[pos1] + nums2[pos2] > mid)
            {
                pos2--;
            }
            cnt += pos2 + 1;
            pos1++;
        }
        return cnt;
    }
    vector<vector<int>> kSmallestPairs(vector<int>& nums1, vector<int>& nums2, int k) {
        n = nums1.size();
        m = nums2.size();
        
        // 1. 二分查找第 k 小的和是多少
        int l = nums1[0] + nums2[0];
        int r = nums1[n - 1] + nums2[m - 1];
        
        while (l < r) {
            int mid = l + (r - l) / 2;
            if (LessMid(mid, nums1, nums2) > k) {
                r = mid;
            } else {
                l = mid + 1;
            }
        }

        // 2. 收集结果
        vector<vector<int>> ret;

        // 第一遍:把所有和严格小于 l 的对子找出来
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m && nums1[i] + nums2[j] < l; j++) {
                ret.push_back({nums1[i], nums2[j]});
                if (ret.size() == k) return ret; // 虽然概率低,但防万一
            }
        }

        // 第二遍:补充和等于 l 的对子,直到够 k 个
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m && nums1[i] + nums2[j] <= l; j++) {
                if (nums1[i] + nums2[j] == l) {
                    ret.push_back({nums1[i], nums2[j]});
                    if (ret.size() == k) return ret;
                }
            }
        }
        return ret;
    }
};

数据流的中位数

第七题讲解思路

cpp 复制代码
class MedianFinder {
public:
    priority_queue<int> st_left_Max;
    priority_queue<int,vector<int>,greater<int>> st_right_Min;
    int maxSum = 0;
    int minSum = 0;
    MedianFinder() {
        
    }
    
    void addNum(int num) {
        if(minSum == maxSum)
        {
            st_right_Min.push(num);
            st_left_Max.push(st_right_Min.top());
            st_right_Min.pop();
            maxSum++;
        }
        else
        {
            st_left_Max.push(num);
            st_right_Min.push(st_left_Max.top());
            st_left_Max.pop();
            minSum++;
        }
    }
    
    double findMedian() {
        if(minSum < maxSum)
        {
            return st_left_Max.top();
        }
        return (st_left_Max.top() + st_right_Min.top()) / 2.0;
    }
};

/**
 * Your MedianFinder object will be instantiated and called as such:
 * MedianFinder* obj = new MedianFinder();
 * obj->addNum(num);
 * double param_2 = obj->findMedian();
 */

二进制求和

解法:模拟

模拟加法求和进行解决

cpp 复制代码
class Solution {
public:
    string addBinary(string a, string b) {
        int n = a.size(), m = b.size();
        reverse(a.begin(), a.end());
        reverse(b.begin(), b.end());
        int tmp = 0, posA = 0, posB = 0;
        string ret;
        while(posA < n || posB < m)
        {
            if(posA < n)
            {
                tmp += a[posA++] - '0';
            }
            if(posB < m)
            {
                tmp += b[posB++] - '0';
            }
            ret += (tmp % 2) + '0';
            tmp /= 2;
        }
        if(tmp)
        {
            ret += tmp + '0';
        }
        reverse(ret.begin(), ret.end());
        return ret;
    }
};

颠倒二进制位

解法;模拟

按照题目要求,对值和二进制进行相互转换

cpp 复制代码
class Solution {
public:
    int reverseBits(int n) {
        // 值 -> 二进制
        string tmp;
        while(n)
        {
            tmp += (n % 2) + '0';
            n /= 2;
        }
        // 得到的 tmp 刚好是颠倒的二进制
        // 补0变完整的2进制
        for(int i = tmp.size(); i < 32;i++)
        {
            tmp += '0';
        }
        // 二进制 -> 值
        int ret = 0, m = tmp.size(), cnt = 0;
        for(int i = m - 1; i >= 0; i--)
        {
            ret += pow(2, cnt++) * (tmp[i] - '0');
        }
        return ret;
    }
};

位 1 的个数

解法:模拟

n &= (n-1) 会把比特位的低 1 位给消除

cpp 复制代码
class Solution {
public:
    int hammingWeight(int n) {
        int cnt = 0;
        while(n)
        {
            cnt += 1;
            n &= (n - 1);
        }
        return cnt;
    }
};

只出现一次的数字

解法:模拟

异或会把相同数字进行消除

cpp 复制代码
class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int ret = 0;
        for(auto& num: nums)
        {
            ret ^= num;
        }
        return ret;
    }
};

只出现一次的数字二

解法:模拟

进行比特位级别的相加,再对各个比特位%3(消除出现三次的数字),最后把比特位还原为值就是答案

cpp 复制代码
class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int bit[32] = {0};
        for(auto& num: nums)
        {
            int pos = 0;
            for(int i = 0; i < 32;i++)
            {
                bit[pos] += (num >> i) & 1;
                bit[pos++] %= 3;
            }
        }
        long long ret = 0, pos = 0;
        for(int i = 0; i < 32; i++)
        {
            ret += pow(2, pos++) * bit[i];
        }
        return ret;
    }
};

数字范围按位与

解法:模拟

比如 100 和 111 :结果是 100:也就是要把后两比特位置 0

如何得到 left 和 right 要置 0 的长度?

left ^ right 的比特位个数

如果将 left 把 left ^ right 长度置 0?

右移后左移

cpp 复制代码
class Solution {
public:
    int rangeBitwiseAnd(int left, int right) {
        int len = bit_width((uint32_t)left ^ right);
        // return left & ~((long long)pow(2,len) - 1);
        return left >> len << len;
    }
};

回文数

解法:数学

循环获取x最后一位进行组合,比如:1212 -> 2 < 121 -> 2 * 10 + 1 = 12 == 12

cpp 复制代码
class Solution {
public:
    bool isPalindrome(int x) {
        if(x < 0 || (x % 10 == 0 && x != 0))
        {
            return false;
        }
        int sum = 0;
        while(sum < x)
        {
            int last = x % 10;
            sum = sum * 10 + last;
            x /= 10;
        }
        //   8888  和   88888       
        if(x == sum || x == sum / 10)
        {
            return true;
        }
        return false;
    }
};

加一

解法:模拟

从右往前遍历,如果加一后变成 10,往左进行进位...直到最高位,如果最高位为 10:先尾插 0,再把最高位变为 1

cpp 复制代码
class Solution {
public:
    vector<int> plusOne(vector<int>& digits) {
        for(int i = digits.size() - 1; i >=0; i--)
        {
            if(digits[i] < 9)
            {
                digits[i]++;
                return digits;
            }
            // 进位
            digits[i] = 0;
        }
        // 999 -> 1000
        digits.push_back(0);
        digits[0] = 1;
        return digits;
        // int tmp = 1;
        // for(int i = digits.size() - 1; i >= 0; i--)
        // {
        //     if(!tmp)
        //     {
        //         break;
        //     }
        //     digits[i] += tmp;
        //     tmp = digits[i] / 10;
        //     digits[i] %= 10;
        //     if(tmp && i == 0)
        //     {
        //         digits.insert(digits.begin(), tmp);
        //     }
        // }
        // return digits;
    }
};

阶乘后的零

解法:数学

求阶乘后的零,不就是求 n ! 的过程有哪些情况的值相乘得到 10 ?

那么 10 怎么来? 通过 10 = 2 * 5 得到,所以现在问题转为求有多少个 2,多少个 5 相乘得到 10,怎么办?

因数为 2 的值多,还是因数为 5 的值多?当然是 5,因为每 2 个数就有一个数是 2 的因数,所以问题再次转化为求 5 的个数?当然不是,5 * 5 = 25,此时 25 后面也有 5?所以问题应该是求 5 的因数

cpp 复制代码
class Solution {
public:
    int trailingZeroes(int n) {
        // 10 = 2 * 5 -> 5 出现的个数少
        int tmp = 5, ret = 0;
        while(tmp <= n)
        {
            ret += n / tmp;
            tmp *= 5;
        }
        return ret;
    }
};

x 的平方根

解法:二分

使用二分的右端点进行解答

python 复制代码
class Solution {
public:
    int mySqrt(int x) {
        int l = 0, r = x;
        while(l < r)
        {
            long long mid = l + ((long long)r - l + 1) / 2;
            if(mid * mid <= x)
            {
                l = mid;
            }
            else
            {
                r = mid - 1;
            }
        }
        return l;
        // if(x == 0)
        // {
        //     return 0;
        // }
        // long long ret = 1;
        // while(true)
        // {
        //     if(ret * ret == x)
        //     {
        //         return ret;
        //     }
        //     ret++;
        //     if(ret * ret > x)
        //     {
        //         return ret - 1;
        //     }
        // }
        // return -1;
    }
};

Pow(x,n)

解法:快速幂

要解决这个问题,必须使用快速幂从而缩短遍历。其核心思想是二分法:

  • 如果 n 是偶数,x^n = (x^2)^{n/2}

  • 如果 n 是奇数,x^n = x \cdot (x^2)^{(n-1)/2}

  • 如果 n 为 1,返回 x^n = x

cpp 复制代码
class Solution {
public:
    double dfs(double x, long long n)
    {
        if(n == 1)
        {
            return x;
        }
        else if(n % 2 == 0)
        {
            return dfs(x * x, n / 2);
        }
        return dfs(x * x, (n - 1) / 2) * x;
    }
    double myPow(double x, int n) {
        if(n > 0)
        {
            return dfs(x, n);
        }
        else if(n < 0)
        {
            return dfs(1/x, -(long long)n);
        }
        return 1;
    }
};

直线上最多的数

解法:暴力

本质:任取一个坐标为基准点,与剩下坐标组合计算斜率,斜率相同的说明再同一条直线上,所以直线上最多的数就是看看有多少斜率相同的最多有多少个

注意:使用hash 统计时使用 double 保存斜率,斜率不一定 / 之后是整数

cpp 复制代码
class Solution {
public:
    int maxPoints(vector<vector<int>>& points) {
        int n = points.size(),ret = 0;
        for(int i = 0; i < n; i++)
        {
            auto t1 = points[i];
            // 使用 hash 统计出现斜率相同的最多个数
            unordered_map<double,int> hash;
            for(int j = 0; j < n; j++)
            {
                if(i == j) continue;
                auto t2= points[j];

                int dy = t2[1] - t1[1];
                int dx = t2[0] - t1[0];
                double k = dx == 0 ? DBL_MAX : dy * 1.0 / dx;
                ret = max(ret,++hash[k]);
            }
        }
        return ret + 1;
    }
};

爬楼梯

解法:动态规划

斐波那契数列变形

cpp 复制代码
class Solution {
public:
    int climbStairs(int n) {
        if(n <= 3)
        {
            return n;
        }
        long long a = 2, b = 3, c = 0;
        for(int i = 3; i < n; i++)
        {
            c = a + b;
            a = b;
            b = c;
        }
        return c;
    }
};

打家劫舍

解法:动态规划

多状态类型题

cpp 复制代码
class Solution {
public:
    int rob(vector<int>& nums) {
        int n = nums.size();
        vector<int> steal(n),unSteal(n);
        steal[0] = nums[0];
        for(int i = 1; i < n; i++)
        {
            steal[i] = max(unSteal[i - 1] + nums[i], steal[i - 1]);
            unSteal[i] = max(steal[i - 1], unSteal[i - 1]);
        }
        return max(steal[n - 1], unSteal[n - 1]);
    }
};

单词拆分

解法:动态规划

dp[i]:表示取 s 【0,i】的子串在不在单词列表,在为true,不在为false

设 j(0 <= j <= i)来辅助构建方程

dp[i] = dp[i - 1] && s.substr(j+1, i - (j+1) + 1) 的子串在不在单词列表中

s 空出一个空格好些循环

cpp 复制代码
class Solution {
public:
    bool wordBreak(string s, vector<string>& wordDict) {
        unordered_map<string,bool> hash;
        int n = s.size();
        for(auto& word: wordDict)
        {
            hash[word] = true;
        }
        s = " " + s;
        vector<bool> dp(n + 1, false);
        dp[0] = true;
        for(int i = 1; i <= n; i++)
        {
            // for(int j = 0; j < i; j++)
            for(int j = i; j >=0; j--)
            {
                dp[i] = dp[j] && hash[s.substr(j+1,i-(j+1)+1)];
                if(dp[i] == true)
                {
                    cout<<i<<' ';
                    break;
                }
            } 
        }
        return dp[n];
    }
};

以上便是全部内容,有问题欢迎在评论区指正,感谢观看!

相关推荐
re林檎1 小时前
算法札记——5.15
算法
鱼子星_1 小时前
【数据结构与算法】OJ题目详解(一)-单链表:从易到难的面试OJ题目
c语言·数据结构·算法·链表·面试·职场和发展
人道领域1 小时前
【LeetCode刷题日记】递归与回溯实战 257.二叉树的所有路径——一篇文章彻底搞懂回溯
开发语言·python·算法·leetcode
ulias2121 小时前
leetcode热题 - 7
数据结构·算法·leetcode
吃好睡好便好1 小时前
在Matlab中用sphere( )函数绘制球面图
开发语言·前端·javascript·学习·算法·matlab·信息可视化
图码1 小时前
矩阵中的“对角线强迫症”:如何优雅地判断Toeplitz矩阵?
数据结构·c++·线性代数·算法·青少年编程·矩阵
lynnlovemin1 小时前
二分查找与二分答案算法详解(基于C++实现)
c语言·开发语言·算法·二分查找·二分答案
瑞行AI1 小时前
一套数据格式框架搞定大模型微调和对齐训练
算法·语言模型
玛卡巴卡ldf1 小时前
【LeetCode 手撕算法】(动态规划)爬楼梯、杨辉三角、打家劫舍、完全平方数、零钱兑换、单词拆分、最长递增子序列、乘积最大子数组、分割等和子集
java·数据结构·算法·leetcode·动态规划·力扣