本文已参与「新人创作礼」活动,一起开启掘金创作之路。
今日题解
丑数Ⅲ
思路:二分搜索加集合求交并补。小于给定mid值的丑数个数是可以用公式计算出来的:n == mid/a + mid/b + mid/c - mid/ab - mid/bc - mid/ac + mid/abc 明明大致思路是一样的,但是第一种方法怎么都出不来结果,卡在[7 7 7 7]
class Solution {
public:
long long big(long long a, long long b) {
return a/gcd(a, b)*b;
}
int nthUglyNumber(int n, int a, int b, int c) {
long long ab = big(a, b);
long long bc = big(b, c);
long long ac = big(a, c);
long long abc = big(ac, b);
long long left = 1, right = LONG_LONG_MAX;
while(left < right) {//左闭右开区间
long long mid = left + (right-left)/2;
if(mid/a + mid/b + mid/c - mid/ab - mid/bc - mid/ac + mid/abc == n) {
return int(max(mid/a*a, max(mid/b*b, mid/c*c)));
}
if(mid/a + mid/b + mid/c - mid/ab - mid/bc - mid/ac - mid/abc < n) {
left = mid + 1;
}else right = mid;
}
return 0;
}
};
最后忍无可忍的我只能找一个新方法 运用c++17的新特性,求最小公倍数,用lcm内置函数就行,但公式还是那个公式,只是变了个形式而已
class Solution {
public:
int nthUglyNumber(int n, int a, int b, int c) {
long lcm_ab = lcm<long>(a, b);//求最小公倍数
long lcm_ac = lcm<long>(a, c);
long lcm_bc = lcm<long>(b, c);
long lcm_abc = lcm<long>(lcm_ab, c);
int left = min({a, b, c}), right = min(static_cast<long>(left) * n, 2000000000l);
while (left <= right) {//左闭右闭区间
int mid = left + (right - left) / 2;
if (mid / a + mid / b + mid / c - mid / lcm_ab -
mid / lcm_ac - mid / lcm_bc + mid / lcm_abc < n) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return left;
}
};
播放列表的数量
思路:(动态规划)都在代码的注释里了,当然也可以去看上一期的动态规划讲解,比这个要清楚些
class Solution {
public:
typedef long long LL;
int numMusicPlaylists(int n, int goal, int k) {
LL m = pow(10, 9) + 7;
LL base = 1;
for(LL i=1; i<= n; i++) {
base = base*i%m;
}
//题目意思是说两首相同歌之间必须至少间隔K首歌
//dp[i][0]表示重复次数
//dp[0][j]表示k个间距
//k=n-1个间距的下标为0,也就是间距从最大值开始
vector<vector<LL>> dp(goal-n+1, vector<LL> (n-k, 1));
for(LL i=1; i<dp.size(); i++) {
for(LL j=1; j<dp[0].size(); j++) {
dp[i][j] = (dp[i][j-1] + (j+1)*dp[i-1][j]%m)%m;
}
}
LL res = dp[goal-n][n-k-1]*base%m;
return res;
}
};
统计点对的数目
思路:emmm,这一题直到晚上十点也没整出来,还是超时,卡在20000,实在搞不出来了,万能的C友啊,请教教我吧。。==下面的代码是超时的==
class Solution {
public:
struct BIT{
static const int maxn = 1e5+10;
int zu[maxn], n;
void init(int _n)
{
memset(zu, 0, sizeof(zu));
n = _n;
}
int lowbit(int x) { return x & -x; }
int sum(int x)
{
int ret = 0;
while(x)
{
ret += zu[x];
x -= lowbit(x);
}
return ret;
}
void add(int x, int val)
{
while(x <= n)
{
zu[x] += val;
x += lowbit(x);
}
}
}bit;
vector<int> countPairs(int n, vector<vector<int>>& edges, vector<int>& queries) {
vector<int>cnts(n+1, 0);
vector<vector<int>>common(n+1, vector<int>(n+1, 0));
for(auto edge : edges)
{
cnts[edge[0]]++;
cnts[edge[1]]++;
common[edge[0]][edge[1]]++;
common[edge[1]][edge[0]]++;
}
int edge_num = edges.size();
bit.init(edge_num);
unordered_map<int, int>mp;
for(int i = 1;i <= n;i++)
for(int j = i+1;j <= n;j++)
{
int tmp = cnts[i] + cnts[j] - common[i][j];
if(tmp) mp[tmp]++;
}
for(auto it = mp.begin();it != mp.end();it++) // 不会超过边数
if(it->second > 0) bit.add(it->first, it->second);
vector<int>res;
int all = bit.sum(edge_num);
for(int num : queries)
res.push_back(all - bit.sum(num));
return res;
}
};
二叉树的坡度今日打卡题
思路:深度优先搜索遍历+递归
class Solution {
int res = 0;
private:
int travel(TreeNode* root) {
//返回root包含当前节点的所有子节点的值
//同时计算出左右节点的绝对值
if(root == nullptr) {
return 0;
}else {
int left = travel(root->left);//左子树之和
int right = travel(root->right);//右子树之和
res += abs(left-right);//累加每个节点的坡度
return root->val + left + right;
}
}
public:
int findTilt(TreeNode* root) {
travel(root);
return res;
}
};
今天干脆就把丑数给大家讲个透彻
丑数
思路:判断一个数是不是丑数,先把有关五的全部去除,再把有关三的全部去除,最后去除所有有关二的,最后剩下的数若为1,证明它能整除2、3、5这三个丑数,也就是说他自己是丑数
class Solution {
public:
bool isUgly(int n) {
if(n <= 0) return false;
while(n%5 == 0) {
n /= 5;
}
while(n%3 == 0) {
n /= 3;
}
while(n%2 == 0) {
n /= 2;
}
if(n == 1) {
return true;
}else return false;
}
};
丑数Ⅱ
思路:三指针法,所以先设置一个dp数组表示当前已经找出并排列好的丑数数组 再3个指针:num2, num3, num5 表示 将当前已经排好的数组元素 *2,*3,*5 所得的数组 这里面最关键的在于dp[i] = min(min(num2, num3), num5); 这样可以让丑数按照大小顺序来排列,也不会产生重复的丑数
class Solution {
public:
int nthUglyNumber(int n) {
vector<int> dp(n+1);
dp[0] = 1;
int p2 = 0, p3 = 0, p5 = 0;
for(int i=1; i<n; i++) {
int num2 = dp[p2]*2, num3 = dp[p3]*3, num5 = dp[p5]*5;
dp[i] = min(min(num2, num3), num5);
if(dp[i] == num2) {
p2++;
}
if(dp[i] == num3) {
p3++;
}
if(dp[i] == num5) {
p5++;
}
}
return dp[n-1];
}
};
最长公共前缀
思路:先取第一个字符串当作他们的公共前缀,再找出它和第二个字符串的公共前缀,依此类推
class Solution {
public:
string longestCommonPrefix(vector<string>& strs) {
if(strs.size() == 0) return "";
int size = strs.size();
string res = strs[0];
int cnt = 1;
while(cnt < size) {
while(strs[cnt].find(res) != 0) {
res = res.substr(0, res.size()-1);
}
cnt++;
}
return res;
}
};
最接近的三数之和
思路:先用sort函数排好序,再在内部使用双指针 给大家一个小建议,能用 ++i 就不要用 i++ ,用 ++i 效率要高些
class Solution {
public:
int threeSumClosest(vector<int>& nums, int target) {
sort(nums.begin(), nums.end());//排序
int left = 0, right = 0;
int closetNum = nums[0] + nums[1] + nums[2];
for(int i=0; i<nums.size()-2; ++i) {
//因为i是内层循环的开始,为了保证最少有三个数,否则会出界
int left = i+1, right = nums.size()-1;//内部使用双指针
while(left < right) {
int threeSum = nums[left] + nums[right] + nums[i];
if(abs(threeSum-target) < abs(closetNum-target)) {
closetNum = threeSum;
}
if(threeSum > target) {
--right;
}else if(threeSum < target) {
++left;
}else return target;
}
}
return closetNum;
}
};
除自身以外数组的乘积
思路:当前乘积==当前数左边的乘积*当前数右边的乘积 方法一:两次遍历
class Solution {
public:
vector<int> productExceptSelf(vector<int>& nums) {
int size = nums.size();
vector<int> res(size, 1);
int cnt = 1;
for(int i=0; i<size; ++i) {
res[i] *= cnt;
cnt *= nums[i];
}
cnt = 1;
for(int i=size-1; i>=0; --i) {
res[i] *= cnt;
cnt *= nums[i];
}
return res;
}
};
优化:一次遍历
class Solution {
public:
vector<int> productExceptSelf(vector<int>& nums) {
int size = nums.size();
vector<int> res(size, 1);
int left = 1, right = 1;
for(int i=0; i<size; ++i) {
res[i] *= left;
left *= nums[i];
res[size-i-1] *= right;
right *= nums[size-i-1];
}
return res;
}
};
螺旋矩阵
思路:主要是确定四条边的位置,然后 从左到右,从上到下,从右到左,从下到上不断遍历,并将所有的元素值都加入到res里
class Solution {
public:
vector<int> spiralOrder(vector<vector<int>>& matrix) {
if(matrix.empty() || matrix[0].empty()) return {};
vector<int> res;
int m = matrix.size(), n = matrix[0].size();
//确定上下左右四条边的位置
int up = 0, down = m-1, left = 0, right = n-1;
while(1) {
for(int i=left; i<=right; i++) {//左到右
res.push_back(matrix[up][i]);
}
if(++up > down) break;
for(int i=up; i<=down; i++) {//上到下
res.push_back(matrix[i][right]);
}
if(--right < left) break;
for(int i=right; i>=left; i--) {//右到左
res.push_back(matrix[down][i]);
}
if(--down < up) break;
for(int i=down; i>=up; i--) {//下到上
res.push_back(matrix[i][left]);
}
if(++left > right) break;
}
return res;
}
};
螺旋矩阵Ⅱ
思路:和上一题思路是差不多的,稍微改部分代码就能实现
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
vector<vector<int>> res(n, vector<int>(n));
//确定上下左右四条边的位置
int up = 0, down = n-1, left = 0, right = n-1, cnt = 1;
while(1) {
for(int i=left; i<=right; i++) {//左到右
res[up][i] = cnt++;
}
if(++up > down) break;
for(int i=up; i<=down; i++) {//上到下
res[i][right] = cnt++;
}
if(--right < left) break;
for(int i=right; i>=left; i--) {//右到左
res[down][i] = cnt++;
}
if(--down < up) break;
for(int i=down; i>=up; i--) {//下到上
res[i][left] = cnt++;
}
if(++left > right) break;
}
return res;
}
};