int gcd(int a, int b){
return b == 0? a: gcd(b, a%b);
}
int lcm(int a, int b){
return a / gcd(a, b) * b;
}
进一步地,我们也可以通过扩展欧几里得算法(extended gcd)在求得 a 和 b 最大公因数的同时,也得到它们的系数 x 和 y,从而使 ax + by = gcd(a, b)。
int xGCD(int a,int b, int &x, int &y){
if(!b){
x = 1, y = 0;
return a;
}
int x1, y1,;
int gcd = xGCD(b, a%b, x1, y1);
x = y1, y = x1 - (a / b) * y1;
return gcd;
}
埃拉托斯特尼筛法(Sieve of Eratosthenes ,简称埃氏筛法)是非常常用的,判断一个整数是否是质数的方法。并且它可以在判断一个整数 n 时,同时判断所小于 n 的整数,因此非常适合这道题。其原理也十分易懂:从 1 到 n 遍历,假设当前遍历到 m ,则把所有小于 n 的、且是 m 的倍数的整数标为和数;遍历完成后,没有被标为和数的数字即为质数。
204. 计数质数
204. Count Primes
给定整数 n
,返回 所有小于非负整数 n
的质数的数量 。
class Solution {
public:
int countPrimes(int n) {
if(n <= 2) return 0;
vector prime(n, true);
int count = n - 2;
for(int i=2; i
利用质数的一些性质,我们可以进一步优化该算法。
class Solution {
public:
int countPrimes(int n) {
if(n <= 2) return 0;
vector prime(n, true);
int count = n / 2; // 所有的偶数一定不是质数
int i = 3, sqrtn = sqrt(n);
while(i <= sqrtn){ // 最小质因子一定小于等于开方数
for(int j=i*i; j
504. 七进制数
504. Base 7
给定一个整数 num
,将其转化为 7 进制,并以字符串形式输出。
进制转换类型的题,通常是利用除法和取模(mod )来进行计算,同时也要注意一些细节,如 负数和零。如果输出是数字类型而非字符串,则也需要考虑是否会超出整数上下界。
class Solution {
public:
string convertToBase7(int num) {
if(num == 0) return "0";
bool is_negative = num < 0;
if(is_negative) num = -num;
string ans;
while(num){
int a = num / 7, b = num % 7;
ans = to_string(b) + ans;
num = a;
}
return is_negative? "-" + ans: ans;
}
};
172. 阶乘后的零
172. Factorial Trailing Zeroes
给定一个整数 n
,返回 n!
结果中尾随零的数量。
提示 n! = n * (n - 1) * (n - 2) * ... * 3 * 2 * 1
每个尾部的 0 由 2 × 5 = 10 而来,因此我们可以把阶乘的每一个元素拆成质数相乘,统计有多少个 2 和 5 。明显的,质因子 2 的数量远多于质因子 5 的数量,因此我们可以只统计阶乘结果里有多少个质因子 5 。
class Solution {
public:
int trailingZeroes(int n) {
if(n == 0) return 0;
return n / 5 + trailingZeroes(n / 5);
}
};
415. 字符串相加
415. Add Strings
给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和并同样以字符串形式返回。
你不能使用任何內建的用于处理大整数的库(比如 BigInteger), 也不能直接将输入的字符串转换为整数形式。
class Solution {
public:
string addStrings(string num1, string num2) {
string output("");
reverse(num1.begin(), num1.end());
reverse(num2.begin(), num2.end());
int onelen = num1.length(), twolen = num2.length();
if(onelen <= twolen){
swap(num1, num2);
swap(onelen, twolen);
}
int addbit = 0;
for(int i=0; i
326. 3 的幂
326. Power of Three
给定一个整数,写一个函数来判断它是否是 3 的幂次方。如果是,返回 true ;否则,返回 false 。
整数 n 是 3 的幂次方需满足:存在整数 x 使得 n == 3^x
有两种方法,一种是利用对数。设 ,如果 n 是 3 的整数次方,那么 m 一定是整数。
class Solution {
public:
bool isPowerOfThree(int n) {
return fmod(log10(n) / log10(3), 1) == 0;
}
};
另一种方法是,因为在 int 范围内 3 的最大次方是 3^ 19 = 1162261467 ,如果 n 是 3 的整数次方,那么 1162261467 除以 n 的余数一定是零;反之亦然。
class Solution {
public:
bool isPowerOfThree(int n) {
return n > 0 && 1162261467 % n == 0;
}
};
384. 打乱数组
384. Shuffle an Array
给你一个整数数组 nums ,设计算法来打乱一个没有重复元素的数组。打乱后,数组的所有排列应该是 等可能 的。
实现 Solution class:
Solution(int[] nums) 使用整数数组 nums 初始化对象
int[] reset() 重设数组到它的初始状态并返回
int[] shuffle() 返回数组随机打乱后的结果
我们采用经典的 Fisher-Yates 洗牌算法,原理是通过随机交换位置来实现随机打乱,有正向和反向两种写法,且实现非常方便。注意这里“reset ”函数以及类的构造函数的实现细节。
class Solution {
public:
vector origin;
Solution(vector& nums): origin(std::move(nums)) {
}
vector reset() {
return origin;
}
vector shuffle() {
if(origin.empty()) return {};
vector shuffled(origin);
int n = origin.size();
//反向洗牌
for(int i=n-1; i>=0; --i){
swap(shuffled[i], shuffled[rand() % (i + 1)]);
}
//正向洗牌
// for(int i=0; i param_1 = obj->reset();
* vector param_2 = obj->shuffle();
*/
528. 按权重随机选择
528. Random Pick with Weight
给你一个 下标从 0 开始 的正整数数组 w ,其中 w[i] 代表第 i 个下标的权重。
请你实现一个函数 pickIndex ,它可以 随机地 从范围 [0, w.length - 1] 内(含 0 和 w.length - 1)选出并返回一个下标。选取下标 i 的 概率 为 w[i] / sum(w) 。
例如,对于 w = [1, 3],挑选下标 0 的概率为 1 / (1 + 3) = 0.25 (即,25%),而选取下标 1 的概率为 3 / (1 + 3) = 0.75(即,75%)。
我们可以先使用 partial_sum 求前缀和(即到每个位置为止之前所有数字的和),这个结果对于正整数数组是单调递增的。每当需要采样时,我们可以先随机产生一个数字,然后使用二分法查找其在前缀和中的位置,以模拟加权采样的过程。这里的二分法可以用 lower_bound 实现。以样例为例,权重数组[1,3] 的前缀和为 [1,4] 。如果我们随机生成的数字为 1 ,那么 lower_bound 返回的位置为 0 ;如果我们随机生成的数字是 2 、 3 、 4 ,那么 lower_bound 返回的位置为 1 。关于前缀和的更多技巧,我们将在接下来的章节中继续深入讲解。
class Solution {
public:
vector sums;
Solution(vector& w): sums(std::move(w)) {
partial_sum(sums.begin(), sums.end(), sums.begin());
}
int pickIndex() {
int pos = (rand() % sums.back()) + 1;
return lower_bound(sums.begin(), sums.end(), pos) - sums.begin();
}
};
/**
* Your Solution object will be instantiated and called as such:
* Solution* obj = new Solution(w);
* int param_1 = obj->pickIndex();
*/
382. 链表随机节点
382. Linked List Random Node
给你一个单链表,随机选择链表的一个节点,并返回相应的节点值。每个节点 被选中的概率一样 。
实现 Solution 类:
Solution(ListNode head) 使用整数数组初始化对象。
int getRandom() 从链表中随机选择一个节点并返回该节点的值。链表中所有节点被选中的概率相等。
不同于数组,在未遍历完链表前,我们无法知道链表的总长度。这里我们就可以使用水库采样:遍历一次链表,在遍历到第 m 个节点时,有 的概率选择这个节点覆盖掉之前的节点选择。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* head;
Solution(ListNode* n): head(n) {
}
int getRandom() {
int ans = head->val;
ListNode* node = head->next;
int i = 2;
while(node){
if((rand() % i) == 0){
ans = node->val;
}
++i;
node = node->next;
}
return ans;
}
};
/**
* Your Solution object will be instantiated and called as such:
* Solution* obj = new Solution(head);
* int param_1 = obj->getRandom();
*/
168. Excel表列名称
168. Excel Sheet Column Title
67. 二进制求和
67. Add Binary
238. 除自身以外数组的乘积
238. Product of Array Except Self
462. 最小操作次数使数组元素相等 II
462. Minimum Moves to Equal Array Elements II
169. 多数元素
169. Majority Element
470. 用 Rand7() 实现 Rand10()
470. Implement Rand10() Using Rand7()
202. 快乐数
202. Happy Number
欢迎大家共同学习和纠正指教