1.selectmaxmin
下面展示一些 内联代码片
。
#include<stdio.h>
void selectmaxmin(int A[], int n, int *max, int *min) {
*max = A[0];
*min = A[0];
for(int i = 1; i < n; i++) {
if(A[i] > *max) *max = A[i];
if(A[i] < *min) *min = A[i];
}
}
int main() {
int n;
int i;
scanf("%d",&n);
int a[n];
int max, min;
for(i = 0; i < n; i++) {
scanf("%d",&a[i]);
}
selectmaxmin(a, n, &max, &min);
printf("max:%d\nmin:%d\n", max, min);
return 0;
}
思考:第一次函数参数设计为int,int传入int但是查询资料只是改变了副本,实现这类参数选择地址
要点:2(n-1)的平均时间复杂性
2.判断key是否在数组R中
下面展示一些 内联代码片
。
#include<stdio.h>
void find(int R[],int n,int key,int*i){
*i=1;
while(*i<=n){
if(R[*i]==key)return;
*i=*i+1;
}
}
int main(){
int n;
int i=0,j;
scanf("%d",&n);
int R[n+1];
for(j=1;j<=n;j++){
scanf("%d",&R[j]);
}
find(R,n,3,&i);
printf("%d",i);
}
key等于R中第i个元素的概率为q/n,比较i次
计算它的平均时间复杂度:
E
(
n
)
=
q
(
n
+
1
)
/
2
+
(
1
−
q
)
n
其中
q
是
k
e
y
在
R
中概率
E(n)=q(n+1)/2+(1-q)n其中q是key在R中概率
E(n)=q(n+1)/2+(1−q)n其中q是key在R中概率
3.adl语言格式特点
链接: ppt上算法用adl语言描述
在实现过程中我想到了上面的方法来解决输入参数和输出参数的问题;
就是传地址,事实证明很实用;
链接: 别人也是这样处理的
4.binaryselect
下面展示一些 内联代码片
。
#include<stdio.h>
void binaryselect(int A[],int i,int j,int*max,int*min){//其中max,min是输出参数
//BS1递归出口
if(i==j){
*max=A[i];
*min=A[i];
return;
}
//有两个元素
if(i==j-1){
if(A[i]<A[j]){
*max=A[j];
*min=A[i];
}
else{
*max=A[i];
*min=A[j];
}
return;
}
//BS2取中值
int mid=(i+j)/2;
//BS3递归调用
int gmax, gmin;
int hmax, hmin;
binaryselect(A, i, mid, &gmax, &gmin);
binaryselect(A, mid+1, j, &hmax, &hmin);
//BS4[合并]
if(gmax>hmax)*max=gmax;
else *max=hmax;
if(gmin<hmin)*min=gmin;
else *min=hmin;
}
int main(){
int i,j;
scanf("%d%d",&i,&j);
int R[j-i+2];
int k=i;
int max,min;
for(k=i;k<=j-i+1;k++){
scanf("%d",&R[k]);
}
binaryselect(R,i,j,&max,&min);a^x$ (指数)
log a b \log_a^b logab:$\l
printf("max:%d\nmin:%d\n", max, min);
}
这段代码实现了使用分治法寻找数组A中的最大值和最小值。
函数binaryselect的基本思路是将数组划分为两部分,分别递归求解左半部分和右半部分的最大值和最小值,然后将结果合并。具体实现如下:
如果数组A中只有一个元素,则这个元素既是最大值又是最小值,直接返回。
如果数组A中有两个元素,则比较它们的大小,较大的为最大值,较小的为最小值,直接返回。
如果数组A中有三个及以上的元素,则将数组分为左半部分和右半部分,分别递归调用binaryselect求解左半部分和右半部分的最大值和最小值。
将左半部分和右半部分的最大值和最小值合并为整个数组的最大值和最小值。具体实现为比较左半部分和右半部分的最大值,将较大的值作为整个数组的最大值;比较左半部分和右半部分的最小值,将较小的值作为整个数组的最小值。
在main函数中,首先输入数组A的左右下标i和j,然后输入数组A的元素,调用函数binaryselect求解最大值和最小值,最后输出结果。
需要注意的是,在输入数组A的元素时,要将元素依次存放到数组对应的位置中,因此输入循环的计数器应从i到j,共输入j-i+1个元素
思考:在这里
int gmax, gmin;
int hmax, hmin;
binaryselect(A, i, mid, &gmax, &gmin);
binaryselect(A, mid+1, j, &hmax, &hmin);
我给出的是指针,但是经过思考又犯了和之前一样的错误传了形参。
要点: 3/2n-2的时间复杂性
5.两个找数组最大最小值的算法比较
就时间而言binnaryselect算法优于算法selectmaxmin3/2n-2和2n-2
然而binnaryselect需要额外的辅助空间栈
6.算法的时间复杂度表示
大O是我们在分析算法复杂度时最常用的一种表示法。
f(x) = O(g(x)) 表示的含义是f(x)以g(x)为上界
当函数的大小只有上界,没有明确下界的时候,则可以使用大O表示法,该渐进描述符一般用于描述算法的 最坏复杂度。
我们在分析各种排序算法时,一般都使用大O来表现算法的性能。
链接: 算法时间复杂度分析——大O、大Ω、大θ、小o,小ω
大O标记对数底可以忽略->O(logn)
对数常数次幂可以忽略->O(logn)
常数项可以忽略
低次项可以忽略
高中记住的忘记推导过程的:
链接: 如何求解1²+2²+3²+……+n²?
链接: 公式编辑参考
因此:
1
3
+
2
3
+
3
3
+
.
.
.
+
n
3
=
O
(
n
4
)
1^{3}+2^{3}+3^{3}+...+n^{3}=O(n^{4})
13+23+33+...+n3=O(n4)
进一步
∑
k
=
1
n
k
d
约等于
∫
0
n
x
d
d
x
=
O
(
n
d
+
1
)
\displaystyle\sum_{k=1}^{n} k^d约等于\int_0^nx^d{\rm d}x=O(n^{d+1})
k=1∑nkd约等于∫0nxddx=O(nd+1)
调和级数
1
+
1
2
+
1
3
+
1
4
+
⋯
+
1
n
≤
1
+
∫
1
n
1
x
d
x
=
O
(
l
o
g
n
)
1+\frac{1}{2}+\frac{1}{3}+\frac{1}{4}+\dots+\frac{1}{n}\leq1+\int_1^n\frac{1}{x}{\rm d}x=O(logn)
1+21+31+41+⋯+n1≤1+∫1nx1dx=O(logn)
7该章节ppt习题
count=0;
for(i=0;i<n;i++)
for(j=1;j<n;j++)
count++;
O ( n 2 ) O(n^2) O(n2)
count=0;
for(i=0;i<n;i++)
for(j=1;j<9999;j++)
count++;
O ( n ) O(n) O(n)
x=0;
while(n>=(x+1)*(x+1))
x=x+1;
O ( l o g n ) O(logn) O(logn)
count=0;
for(i=1;i<=n;i++)
for(j=1;j<=i;j++)
count++;
O ( n 2 ) O(n^2) O(n2)
count=0;
for(i=1;i<n;i*=2)
count++;
O ( l o g n ) O(logn) O(logn)
count=0;
for(i=n;i>=1;i/=2)
count++;
O ( l o g n ) O(logn) O(logn)
下面是难的
下面展示一些 内联代码片
。
n>=3
for(i=0;i<n-1;i++)
for(j=1;j<n;j=j+n/3)
for(k=1;k<n;k=k*2)
x=x*5;
O ( n l o g n ) O(nlogn) O(nlogn)
count=0;
for(i=1;i<=n;i++)
for(j=0;j<n;j+=i)
count++;
思考: n/1+n/2+n/3+…n/n= n(调和级数)
为
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)
下面展示一些 内联代码片
。
int func(int n){
int i=0;sum=0;
while(sum<n)
sum+=++i;
return i;
}
思考经过几次循环sum>=n
第i次sum=1+2+3+…+i=(1+i)*i/2,而i和轮数对应所以是
O
(
n
)
O(\sqrt n)
O(n)
下面展示一些 内联代码片
。
int f(int a[],int n,int K){
int i,mid,result=1,s=0,e=n-1;
while(s<e){
for(i=s;i<=e;i++)
result=result*a[n];
mid=(s+e)/2;
if(K<a[mid])e=mid-1;
else if(K>a[mid])s=mid+1;
else break;
}
return result;
}
n
=
2
k
n=2^k
n=2k
T
(
n
)
=
n
+
n
2
+
n
4
+
n
8
+
⋯
+
1
T(n)=n+\frac{n}{2}+\frac{n}{4}+\frac{n}{8}+\dots+1
T(n)=n+2n+4n+8n+⋯+1
=
n
(
2
−
1
2
k
)
=
2
n
−
1
=
O
(
n
)
=n(2-\frac{1}{2^k})=2n-1=O(n)
=n(2−2k1)=2n−1=O(n)
8.空间复杂度的分析方法
链接: 空间复杂度分类的四类
int f(int a[],int n){
int i,result=1;
for(i=0;i<n;i++)
result=result*a[i];
return result;
}
int f(int a[],int n){
int i;
int*temp=new int[n];
for(i=0;i<n;i++)
temp[i]=a[n-i-1];
for(i=0;i<n;i++)
a[i]=temp[i];
}
区别:第一个存储空间和n无关,第二个动态生成;
第一个
O
(
1
)
O(1)
O(1),第二个
O
(
n
)
O(n)
O(n)
9.均摊时间复杂度
每个操作的平均代价
//均摊二进制加计数器
int addone(int A[],int n){
int i=0;
while(i<n&&A[i]==1){
A[i]=0;
i=i+1;
}
if(i<n)A[i]=1;
}
A[i]每
2
i
2^i
2i次调用算法翻转一次,n次调用翻转
n
2
i
\frac{n}{2^i}
2in
∑
i
=
0
n
−
1
n
2
i
<
n
∑
i
=
0
n
−
1
1
2
i
=
2
n
(
1
−
1
2
n
)
=
O
(
n
)
\displaystyle\sum_{i=0}^{n-1}\frac{n}{2^i}<n\displaystyle\sum_{i=0}^{n-1}\frac{1}{2^i}=2n(1-\frac{1}{2^n})=O(n)
i=0∑n−12in<ni=0∑n−12i1=2n(1−2n1)=O(n)
时间复杂度为
O
(
n
)
O(n)
O(n)
均摊时间复杂度为
O
(
1
)
O(1)
O(1)
输入情况 | 考察对象 | 计算方法 | 例子 | |
---|---|---|---|---|
平均时间复杂度 | 假设输入发生的各种概率分布 | 单个操作 | 对各种输入情况的执行时间加权平均 | find() |
均摊时间复杂度 | 不对输入作假设 | 连续的操作序列 | 操作序列的执行时间取平均值 | addone到n |