leetcode精选合集(更新中)
仅做收集重要理论和思维
- 1.位运算
- 1.1863.找出所有子集异或总和再求和
- 2.快速幂
- 1.1922.统计好数字的数目
1.位运算
1.1863.找出所有子集异或总和再求和
1863.找出所有子集异或总和再求和
一个个集合硬算当然是可以,但是这样时间复杂度无疑很高。所以先归纳好数学规律再动笔。数学规律如下:
原著@leetcode灵茶山艾府
方法1:
在有至少一个 1 的情况下,nums 的所有子集的异或和的总和为2n-1。(假设集合只有0和1)
对于异或运算,每个比特位是互相独立的,我们可以先思考只有一个比特位的情况,也就是 nums 中只有 0 和 1 的情况。(从特殊到一般)
在这种情况下,如果子集中有偶数个1,那么异或和为0;如果子集中有奇数个1,那么异或和为1。所以关键是求出异或和为 1 的子集个数。设 nums 的长度为 n,且包含 1。我们可以先把其中一个 1 拿出来,剩下 n−1 个数随便选或不选,有 2n−1种选法。
如果这 n−1 个数中选了偶数个 1,那么放入我们拿出来的 1(选这个 1),得到奇数个 1,异或和为 1。
如果这 n−1 个数中选了奇数个 1,那么不放入我们拿出来的 1(不选这个 1),得到奇数个 1,异或和为 1。
所以,恰好有 2n−1个子集的异或和为 1。
注意!!
这个结论与 nums 中有多少个 1 是无关的,只要有 1,异或和为 1 的子集个数就是 2n−1 。如果 nums 中没有 1,那么有 0 个子集的异或和为 1。所以,在有至少一个 1 的情况下,nums 的所有子集的异或和的总和为2n-1。
因此对于一个确定位来说,只要有1个数的这个位上有1,那么集合中所有子集的这个位的异或和总和是 2n-1。因为0是偶数,1是奇数,那么这个规律是否可以用到一般规律上?
方法1的一般性数理(二项式证明):
大小为n 的集合中,有 2n−1个大小为奇数的子集。其中 n 是正整数。
从组合数来说明,在n个数里面大小为k的子集个数是:
C
(
n
,
k
)
=
n
!
k
!
(
n
−
k
)
!
C(n, k) = \frac{n!}{k!(n-k)!}
C(n,k)=k!(n−k)!n!
因此,大小为奇数的子集个数是:
∑ k = 1 n 或 n − 1 C ( n , k ) = n ! k ! ( n − k ) ! ( k 是奇数) \sum_{k= 1}^{n或n-1}C(n, k) = \frac{n!}{k!(n-k)!}(k是奇数) k=1∑n或n−1C(n,k)=k!(n−k)!n!(k是奇数)
由二项式定理有:
(
a
+
b
)
n
=
∑
k
=
0
n
C
n
k
a
n
−
k
b
k
(a + b)^n=\sum_{k = 0}^{n}C_{n}^{k}a^{n - k}b^{k}
(a+b)n=k=0∑nCnkan−kbk
当 a = 1,b = 1时:
(
1
+
1
)
n
=
∑
k
=
0
n
C
n
k
×
1
n
−
k
×
1
k
(1 + 1)^n=\sum_{k = 0}^{n}C_{n}^{k}\times1^{n - k}\times1^{k}
(1+1)n=k=0∑nCnk×1n−k×1k
∑
k
=
0
n
C
n
k
×
1
n
−
k
×
1
k
=
C
n
0
+
C
n
1
+
C
n
2
+
⋯
+
C
n
n
\sum_{k = 0}^{n}C_{n}^{k}\times1^{n - k}\times1^{k}=C_{n}^{0}+C_{n}^{1}+C_{n}^{2}+\cdots + C_{n}^{n}
k=0∑nCnk×1n−k×1k=Cn0+Cn1+Cn2+⋯+Cnn
C
n
0
+
C
n
1
+
C
n
2
+
⋯
+
C
n
n
=
2
n
C_{n}^{0}+C_{n}^{1}+C_{n}^{2}+\cdots + C_{n}^{n}=2^n
Cn0+Cn1+Cn2+⋯+Cnn=2n
当 a = 1,b = -1 时:
(
1
−
1
)
n
=
∑
k
=
0
n
C
n
k
×
1
n
−
k
×
(
−
1
)
k
(1 - 1)^n=\sum_{k = 0}^{n}C_{n}^{k}\times1^{n - k}\times(-1)^{k}
(1−1)n=k=0∑nCnk×1n−k×(−1)k
∑
k
=
0
n
C
n
k
×
1
n
−
k
×
(
−
1
)
k
=
C
n
0
−
C
n
1
+
C
n
2
−
C
n
3
+
⋯
+
(
−
1
)
n
C
n
n
\sum_{k = 0}^{n}C_{n}^{k}\times1^{n - k}\times(-1)^{k}=C_{n}^{0}-C_{n}^{1}+C_{n}^{2}-C_{n}^{3}+\cdots+(-1)^{n}C_{n}^{n}
k=0∑nCnk×1n−k×(−1)k=Cn0−Cn1+Cn2−Cn3+⋯+(−1)nCnn
C
n
0
−
C
n
1
+
C
n
2
−
C
n
3
+
⋯
+
(
−
1
)
n
C
n
n
=
0
C_{n}^{0}-C_{n}^{1}+C_{n}^{2}-C_{n}^{3}+\cdots+(-1)^{n}C_{n}^{n}=0
Cn0−Cn1+Cn2−Cn3+⋯+(−1)nCnn=0
将两式相减:
(
C
n
0
+
C
n
1
+
C
n
2
+
⋯
+
C
n
n
)
−
(
C
n
0
−
C
n
1
+
C
n
2
−
C
n
3
+
⋯
+
(
−
1
)
n
C
n
n
)
=
2
n
−
0
(C_{n}^{0}+C_{n}^{1}+C_{n}^{2}+\cdots + C_{n}^{n})-(C_{n}^{0}-C_{n}^{1}+C_{n}^{2}-C_{n}^{3}+\cdots+(-1)^{n}C_{n}^{n})=2^n - 0
(Cn0+Cn1+Cn2+⋯+Cnn)−(Cn0−Cn1+Cn2−Cn3+⋯+(−1)nCnn)=2n−0
2
(
C
n
1
+
C
n
3
+
C
n
5
+
⋯
)
=
2
n
2(C_{n}^{1}+C_{n}^{3}+C_{n}^{5}+\cdots)=2^n
2(Cn1+Cn3+Cn5+⋯)=2n
(
C
n
1
+
C
n
3
+
C
n
5
+
⋯
)
=
2
n
−
1
(C_{n}^{1}+C_{n}^{3}+C_{n}^{5}+\cdots)=2^{n-1}
(Cn1+Cn3+Cn5+⋯)=2n−1
两边同时除以 2,可得:
(
C
n
1
+
C
n
3
+
C
n
5
+
⋯
=
2
n
−
1
)
(C_{n}^{1}+C_{n}^{3}+C_{n}^{5}+\cdots = 2^{n - 1})
(Cn1+Cn3+Cn5+⋯=2n−1)即大小为 n 的集合中,大小为奇数的子集个数是2n-1。
方法1的一般性数理(数学归纳法):
基于数学归纳法证明大小为 n 的集合中大小为奇数的子集个数 为2n-1。
- 基础步骤:当 n = 1时,集合{a}有子集 ∅ \emptyset ∅ 和{a},奇数大小子集{a}个数为1 = 21-1,成立
- 假设步骤:设大小为n的集合中奇数大小子集个数为2n-1。
- 归纳步骤:当集合大小为n+1,先把第n+1个元素拿出来,由假设步骤知道,剩下大小为n的集合中奇数大小子集个数为2n-1个。
大小为n的子集中奇数个数是2n-1个,那么大小是偶数的个数为2n-2n-1=2n-1个。将大小是偶数的子集再加上第n+1个元素,那么每个子集大小都是奇数,正好也是2n-1个。两个2n-1就是2n个 - 综上,大小为 n 的集合中大小为奇数的子集个数为2n-1。
抛砖引玉,写是永远写不完的,参与一下意思意思即可,现在要去专心做这一类的题了,查阅<<哆啦洋语录>>第1条: 写题要:专注完成,归纳总结,然后举一反三 写题要:专注完成,归纳总结,然后举一反三 写题要:专注完成,归纳总结,然后举一反三
——————哆啦洋 \quad \quad \quad \quad \quad \quad \quad \quad \quad \quad \quad \quad \quad \quad \quad \quad ——————哆啦洋 ——————哆啦洋
传送门:灵茶8题
2.快速幂
1.1922.统计好数字的数目
基础知识点,但是个人think有一个地方是易混淆的地方,理解这一难点,至少简单的快速幂都没有什么阻碍了。抛砖引玉,先看这题1922.统计好数字的数目
先理解一下什么是快速幂:
要计算
2
10
的值是多少,别和我说是
2
10
要计算2^{10}的值是多少,别和我说是 2^{10}
要计算210的值是多少,别和我说是210
傻瓜做法
=
2
∗
2
∗
2
∗
2
∗
2
∗
.
.
.
.
.
傻瓜做法=2*2*2*2*2*.....
傻瓜做法=2∗2∗2∗2∗2∗.....
正常人做法
=
2
10
=
2
1010
2
进制
=
(
2
8
∗
1
)
∗
(
2
4
∗
0
)
∗
(
2
2
∗
1
)
∗
(
2
1
∗
0
)
正常人做法=2^{10}=2^{{1010}_{2进制} }=(2^8*1)*(2^4*0)*(2^2*1)*(2^1*0)
正常人做法=210=210102进制=(28∗1)∗(24∗0)∗(22∗1)∗(21∗0)
仔细对照等式两边,如果还没看到一点玄机的右上角点击’X’离开。
显然,本来是10次for循环的计算优化成了4次for循环。代码如下:
int power_2(int n) //计算2的n次幂
{
int ans=1;
int base=2;
while(n)
{
if(n&1) //当前位是1
ans=ans*base;
n>>=1;
base*=base; //易混淆点
}
cout<<ans;
}
易混淆点在于: base*=base ,而不是base*=2。为什么是这样?
比如21010(二进制)=21000*20010,其中20010到20100是这样来的,20100=20010*20010,再21000=20100*20100.
因此这里要注意。
从特殊到一般,如果底数不是2,将base改成需要的底数即可
模板:
int power_x(int n,int x) //计算x的n次幂
{
int ans=1;
int base=x;
while(n)
{
if(n&1) //当前位是1
ans=ans*base;
n>>=1;
base*=base; //易混淆点
}
cout<<ans;
}
早就说了,写博客是永远写不完的,别往下翻了,快速幂到这里就没了,入个门就差不多了,我也要睡觉了,参阅<<哆啦洋语录>>第2条:
当天的任务完不成,那么改天再完成
当天的任务完不成,那么改天再完成
当天的任务完不成,那么改天再完成
——————哆啦洋
\quad \quad \quad \quad \quad \quad \quad \quad \quad \quad \quad \quad \quad \quad \quad \quad ——————哆啦洋
——————哆啦洋