week38
371 - 两整数之和
题里说的是不用加法完成加法运算,这里考的是位运算的应用
用异或来算不带进位的和(异或:0⊕0=0,1⊕0=1,0⊕1=1,1⊕1=0)
用与 和 左移 来算进位
递归计算
举例来说,a=101, b=011
第一步: 异或,计算无需进位的部分,a^b=110,最右位的和2需要进位;
第二步: 与,a&b=001,结果表示的是需要进位的地方,将它左移一位得到010
第三步,递归调用,求进位前后的和,直到无需进位
class Solution {
public:
int getSum(int a, int b) {
if(!a) return b;
int sum = a ^ b, carry = (unsigned)(a & b) << 1;
return getSum(carry, sum);
}
};其中负数在计算机中存储是以补码的形式,左移有溢出可能,比如负数加负数(符号位都为1),用unsigned强转,在左移超过int位数之后自动丢弃,不会溢出
372 - 超级次方
提到次方就来狠狠回顾一下快速幂怎么写
这个题把快速幂和高精度结合在一起,对于本题而言, 把数字拆成个位和其他,递归求解
a ^ b3b2b1 % mod = (a ^ b3b2 % mod) ^ 10 * a ^ b1 % mod;
373 - 查找和最小的k组数字
考察的是多路归并
374 - 猜数字大小
靠一些二分模板。。。
376 - 摆动序列
本题是贪心算法,思路还挺难想的;
最优解就是在每个波动的波峰和波谷中选择顶点和底点,与开头结尾两个点组成的序列最长。
证明过程简要来说是反证法,如果存在非顶点或低点被选中的话,这个点的附近一定存在两个比他高or比他低的点,也就是新的局部极值点,与前面的假设矛盾(大概记一下就好了)
代码中的细节有两个,一个是相邻重复元素怎么去除;
nums.erase(unique(nums.begin(), nums.end()), nums.end())
原理是unique函数将重复元素交换到后面并返回迭代器,最后直接删除
另一个就是判断上升趋势与下降趋势是否与之前相同的逻辑怎么写 -> 异或运算,这里值得看一下
377 - 组合总和IV
回忆一下和组合问题三的区别,组合问题三中顺序不同的序列被视为相同的组合,对于这种有限制的组合问题可以用背包问题(这里是完全背包来解决);
而对于本题,顺序不同的序列被视为不同的组合,这里也是动态规划的思想,具体如下:
状态定义: f[i]表示总和为i的所有排列组合可能性数量
状态转移: 枚举每个元素x 在原有序列后面拼上新的元素x的话,保证和之前所有的序列不重复,所以是f[i] += f[i - x]
378 - 有序矩阵中的第K小元素
每行元素均按升序排序 -> 多路归并,复杂度n^2logn
本题有额外的条件,每列元素按升序排序,且复杂度优于n^2 -> nlogn + 第K小元素,可以尝试下二分;
首先判断是否满足二分的性质,对于某个元素t:
小于等于t的元素数 < k: 则证明第K小元素 一定存在于[t + 1, matrix[n-1][n-1])
小于等于t的元素数 >= k: 第K小元素 index的区间[matrix[0][0], t]
matrix[n-1][n-1]为最大值,matrix[0][0]为最小值
接下来就是给定一个元素,如何计算小于等于他的元素个数:
对于第一行,可以直接线性扫描得到小于等于他的元素个数; 而对于第二行,由于按列递增,小于等于他的元素区间一定小于上一行;
这一性质对于接下来的每一行都成立,区间是越来越小的,我们可以利用这一性质,扫描下一行的时候,直接以上一行的终点向前扫描,直到列扫描结束,或者该行所有元素都满足大于;
因此,找到小于等于他元素个数的时间复杂度是o(n)
380 - O(1)时间插入,删除和获取随机元素
实现一个数据结构, 可以完成:
O(1)插入 -> hash
O(1)删除 -> hash
O(1)随机返回一个值,保证等概率 -> vector
等概率返回可以通过数组实现,按照下标直接顺序返回就可以了,因此通过哈希+数组的双数据结构来实现,哈希表内存储元素->vector下标,问题是如何实现O(1)的删除;
对于vector pop_back()操作是o(1)的,那么对于想删除元素的话,只需要交换想删除元素 与最后一个就好;
注意下swap和rand()怎么用
Last updated