目录
[查找最小 k 堆数字和](#查找最小 k 堆数字和)
[位 1 的个数](#位 1 的个数)
[x 的平方根](#x 的平方根)
数组中的第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];
}
};
以上便是全部内容,有问题欢迎在评论区指正,感谢观看!