2021-09-10

算法分析

第一次

程序设计一般步骤
1 确定数据结构
2 确定算法
3 编写程序
4 调试程序
5 编写文档资料

上述5个步骤中最重要的第2步 确定算法
算法:所谓算法是指某个问题或者某个功能的求解方法。

实际中某个问题或者某个功能的求解方法不止一个。

考虑选择哪种算法作为我该问题或者该功能的最终解决方案呢?

1 算法正确

2 时间复杂度和空间复杂度

例1 输入一个正整数给x,判断其是否是素数?

分析 所谓素数就是质数:也就是只能被1和其本身(x)整除的数
其实任何额一个正整数都能被1和其本身整除,而这点其实可以不证明
重点去证明:不能被2到x-1范围内的所有的数整除。
举例:
x=7 2 3 4 5 6
2到6中每个数值都与7之间是不能整除的关系,所以7才是素数

x=8 2 3 4 5 6 7
8对2取余数就是0,整除关系,结果可以直接下结论8不是3到7就没必要再进行判断了,循环可以提前结束。

整个问题中涉及2个问题,是素数1,不是素数0
代码1
#include"stdio.h"
#include"windows.h"
int main()
{
int x,i,flag;//flag=1 是素数 flag=0 表示不是素数

printf(“请你任意输入一个正整数:”);
scanf("%d",&x);

flag=1;//相当于一开始假设x是素数
for(i=2;i<=x-1;i++)
{
if(x%i==0)
{
flag=0;
break;
}
}

if(flag==1)
printf(“yes”);
else
printf(“no”);
system(“pause”);
}

上述程序的时间复杂度是多少? o(n)


时间复杂度的表示形式是 o(?)

1 白话解释:看程序运行所耗费的时间

2 专业理解:形式o(?)

?体现出来的就是一个问题求解过程中操作的执行次数。
执行次数要把问题放大到n个数值的角度来看待。

特别注意: 专业理解时间复杂度的问题,把一个问题求解方法的过程,放大到n个数值类似的角度来看待问题。
举例说明:s=1+2+3+4+…+100

方法1 用等差数列求和公式来表示
s=(1+100)*100/2;
时间复杂度 o(1)
注意这里1 表示操作的执行次数

方法2 用循环
s=0;
for(i=1;i<=100;i++)
s=s+i;
时间复杂度 o(n)
注意这里n 表示操作的执行次数


如果在之前所学过的输出所有三位数值中的水仙花数。

for(a=1;a<=9;a++)
for(b=0;c<=9;b++)
for(c=0;c<=9;c++)
{
t=a100+b10+c;
if(t==aaa+bbb+ccc)
printf("%d\n",t);
}

白话解释 91010

时间复杂度 o(nnn) 等价 o(n^3)


改进素数程序

举例

x=7 2 3 4 5 6 2到6中每个数值都与7之间是不能整除的关系,所以7才是素数

x=8 2 3 4 5 6 7 8对2取余数就是0,整除关系,结果可以直接下结论8不是
3到7就没必要再进行判断了。
循环可以提前结束。

注意 上述方法中
其实对于7而言 4 5 6没必要判断,因为它们都超过了7的一半,所以不可能是整除关系
其实对于8而言 5 6 7没必要判断,因为它们都超过了8的一半,所以不可能是整除关系

最后修改的是循环 for(i=2;i<=x/2;i++)

代码2
#include"stdio.h"
#include"windows.h"
int main()
{
int x,i,flag;//flag=1 是素数 flag=0 表示不是素数

printf(“请你任意输入一个正整数:”);
scanf("%d",&x);

flag=1;//相当于一开始假设x是素数
for(i=2;i<=x/2;i++)
{
if(x%i==0)
{
flag=0;
break;
}
}

if(flag==1)
printf(“yes”);
else
printf(“no”);
system(“pause”);
}
白话理解:运算次数少了
专业理解:时间复杂度是多少呢? o(n)


继续修改

4不是素数 因为2
6不是素数 因为2
8不是素数 因为2
9不是素数 因为3
10不是素数 因为2
12不是素数 因为2
14不是素数 因为2
15不是素数 因为3
16不是素数 因为2
。。。。。。。。。。。
25不是素数 因为5
26不是素数 因为2

所以每个数值x是否是素数,判断范围还可以缩小,2到x的平方根 sqrt(x)

代码2
#include"stdio.h"
#include"windows.h"
#include"math.h"
int main()
{
int x,i,flag;//flag=1 是素数 flag=0 表示不是素数

printf(“请你任意输入一个正整数:”);
scanf("%d",&x);

flag=1;//相当于一开始假设x是素数
for(i=2;i<=sqrt(x);i++)
{
if(x%i==0)
{
flag=0;
break;
}
}

if(flag==1)
printf(“yes”);
else
printf(“no”);
system(“pause”);
return 0;
}

白话理解:运算次数少了
专业理解:时间复杂度是多少呢? o(logn)


最后提醒一下

一般我们强调某个方法的时间复杂度,应该是包含该问题求解方法的所有步骤所耗费的时间

实际处理的是一个问题求解方法中,所有步骤中取耗费时间(次数)最多的作为最后的时间复杂度的描述

一般情况下,时间复杂度的表示形式如下常见的5种

1 o(1)
2 o(n)
3 o(logn)
4 o(n*logn)
5 o(n^3) 或者 o(n^?)


例2 1-1000这1000个数放在含有1001个元素的数组中,只有唯一的一个元素值重复,
其他均只出现一次。设计一个算法,将它找出来,你能否设计一个算法实现?

最后要找出哪个数是重复的?如果可以的话再找出具体是哪2个位置的数?

方法1 简单粗暴,我想用枚举法,暴力破解法。好处,理解简单啊

a[1]     a[2]     a[3]     a[4]     a[5]     a[6]     a[7]     a[8]     a[9]     a[10]     a[11]
2         9         8         6         5         4         1         3         7         10         8
最终考虑到是任意输入的情况

(1)将a[1]与 a[2]到a[11]依次比较,如果相等则输出a[1],1,另外一个相同值的位置
for(i=2;i<=11;i++)
{
if(a[1]==a[i])
printf("%d %d %d\n",a[1],1,i);
}
(2)将a[2]与 a[3]到a[11]依次比较,如果相等则输出a[2],2,另外一个相同值的位置
for(i=3;i<=11;i++)
{
if(a[2]==a[i])
printf("%d %d %d\n",a[2],2,i);
}

(10)将a[10]与 a[11]到a[11]依次比较,如果相等则输出a[10],10,另外一个相同值的位置
for(i=11;i<=11;i++)
{
if(a[10]==a[i])
printf("%d %d %d\n",a[10],10,i);
}

上述过程再凑循环
for(j=1;j<=10;j++)
{
for(i=j+1;i<=11;i++)
{
if(a[j]==a[i])
printf("%d %d %d\n",a[j],j,i);
}
}

最后代码

#include"stdio.h"
#include"windows.h"
int main()
{
int a[12],i,j;

printf(“请你输入11个数值,且在1-10之间,且只能有一个数值重复\n”);
for(i=1;i<=11;i++)
scanf("%d",&a[i]);

for(j=1;j<=10;j++)
{
for(i=j+1;i<=11;i++)
{
if(a[j]==a[i])
printf("%d %d %d\n",a[j],j,i);
}
}

system(“pause”);
}

这里是算次数
10+9+…+1
n-1+n-2+…+1
(n-1+1)(n-1)/2
n
(n-1)/2

n^2-n/2

标准 o(1+n+(n^2-n)/2)

这里1 是定义代码的
这里n 是输入代码的
这里(n^2-n)/2 是求解代码的

最后取最大值 o(n^2)


最后的修改思路
a[1]     a[2]     a[3]     a[4]     a[5]     a[6]     a[7]     a[8]     a[9]     a[10]     a[11]
2         9         8         6         5         4         1         3         7         10         8
准备2个数组,一个数组长度11,另外一个数组长度10
数组a的每个数组元素的值,作为数组b数组元素的标号
数组b中所有数组元素初始值为0
b[1]     b[2]      b[3]      b[4]     b[5]      b[6]      b[7]      b[8]      b[9]     b[10]
1         1          1           1         1         1          1          2         1          1
          说明2出现了1次                                                          说明8出现了2次

#include"stdio.h"
#include"windows.h"
int main()
{
int a[12],b[12]={0},i,j,t;

printf(“请你输入11个数值,且在1-10之间,且只能有一个数值重复\n”);
for(i=1;i<=11;i++)
scanf("%d",&a[i]);

for(i=1;i<=11;i++)
{
b[a[i]]++;
if(b[a[i]]==2)
{
t=a[i];
break;
}
}

//如果输出既要位置也要输出的值,我得再来 一轮循环
for(i=1;i<=11;i++)
{
if(t==a[i])
printf(“相同值%d 相同位置%d\n”,t,i);
}
system(“pause”);
}

其实这个方法是用来 空间换时间。

最后问题该方法的时间复杂度是多少呢? o(n)

过程
1 定义部分 1
2 输入部分 n
3 求解部分 n
4 输出部分 n
最后取最大值 n


提出方法3 思路 请思考用异或(位运算)

异或 一样为0,不一样为1

如果是9

9^0 结果是 9

9^9 结果是 0

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
使用python中的pymsql完成如下:表结构与数据创建 1. 建立 `users` 表和 `orders` 表。 `users` 表有用户ID、用户名、年龄字段,(id,name,age) `orders` 表有订单ID、订单日期、订单金额,用户id字段。(id,order_date,amount,user_id) 2 两表的id作为主键,`orders` 表用户id为users的外键 3 插入数据 `users` (1, '张三', 18), (2, '李四', 20), (3, '王五', 22), (4, '赵六', 25), (5, '钱七', 28); `orders` (1, '2021-09-01', 500, 1), (2, '2021-09-02', 1000, 2), (3, '2021-09-03', 600, 3), (4, '2021-09-04', 800, 4), (5, '2021-09-05', 1500, 5), (6, '2021-09-06', 1200, 3), (7, '2021-09-07', 2000, 1), (8, '2021-09-08', 300, 2), (9, '2021-09-09', 700, 5), (10, '2021-09-10', 900, 4); 查询语句 1. 查询订单总金额 2. 查询所有用户的平均年龄,并将结果四舍五入保留两位小数。 3. 查询订单总数最多的用户的姓名和订单总数。 4. 查询所有不重复的年龄。 5. 查询订单日期在2021年9月1日至9月4日之间的订单总金额。 6. 查询年龄不大于25岁的用户的订单数量,并按照降序排序。 7. 查询订单总金额排名前3的用户的姓名和订单总金额。 8. 查询订单总金额最大的用户的姓名和订单总金额。 9. 查询订单总金额最小的用户的姓名和订单总金额。 10. 查询所有名字中含有“李”的用户,按照名字升序排序。 11. 查询所有年龄大于20岁的用户,按照年龄降序排序,并只显示前5条记录。 12. 查询每个用户的订单数量和订单总金额,并按照总金额降序排序。
最新发布
06-03
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值