文章目录
上课部分内容
分析1:
如果假设判断的数是x
所谓素数就是质数:只能被1和其本身整除的数
其实任何一个正整数都能被1和其本身整除(1和x没必要判断)
只需要去判断除开1和x以外的其他数(也就是2到x-1范围中的数)与x本身之间的整除关系
举例
x=7 判断范围是 2 3 4 5 6 2到6与7都不能被整除,所以7是素数
x=8 判断范围是 2 3 4 5 6 7 2与8之间是整除关系,下结论8不是素数
并且3到8是没必要再进行整除判断
循环提前结束了
区别是素数和不是素数?
如果是素数,循环一定全部做完。
如果不是素数,循环一定提前结束。
颜老板版本
#include"stdio.h"
#include"windows.h"
int main()
{
int x,i;
printf("请你任意输入一个正整数:");
scanf("%d",&x);
for(i=2;i<=x-1;i++)
{
if(x%i==0)
break;
}
if(i==x-1+1)
printf("是素数");
else
printf("不是素数");
system("pause");
}
更新版
#include<iostream>
using namespace std;
bool isPrime(int n){for(int i=2;i<n;i++)if(n%i==0)return false;return true;}
int main()
{
int n;
cin>>n;
if(isPrime(n))cout<<1;else cout<<-1;
return 0;
}
改进素数的判断方法呢? 能否降低时间复杂度呢?
分析2
举例
x=7 判断范围是 2 3 4 5 6 2到6与7都不能被整除,所以7是素数
x=8 判断范围是 2 3 4 5 6 7 2与8之间是整除关系,下结论8不是素数
并且3到8是没必要再进行整除判断
循环提前结束了
注意以7为例,其实4 5 6没必要与7判断是否是素数。因为它们三个的大小已经超过了7的一半。
注意以8为例,其实5 6 7没必要与8判断是否是素数。因为它们三个的大小已经超过了8的一半。
其实最后建议修改判断范围是 2到x/2
颜老板版本
#include"stdio.h"
#include"windows.h"
int main()
{
int x,i;
printf("请你任意输入一个正整数:");
scanf("%d",&x);
for(i=2;i<=x/2;i++)
{
if(x%i==0)
break;
}
if(i==x/2+1)
printf("是素数");
else
printf("不是素数");
system("pause");
}
更新版
#include<iostream>
using namespace std;
bool isPrime(int n){for(int i=2;i<=n/2;i++)if(n%i==0)return false;return true;}
int main()
{
int n;
cin>>n;
if(isPrime(n))cout<<1;else cout<<-1;
return 0;
}
分析3
4 不是素数 因为2
6 不是素数 因为2
8 不是素数 因为2
9 不是素数 因为3
10 不是素数 因为2
12 不是素数 因为2
14 不是素数 因为2
15 不是素数 因为3
16 不是素数 因为2
。。。。。。。。。。。。。。。。。。。。。
25 不是素数 因为5
最后修改的范围是 2到x的平方根
颜老板版本
#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;//此刻x不是素数
break;
}
}
if(flag==1)
printf("是素数");
else
printf("不是素数");
system("pause");
}
更新版
#include<iostream>
#include<cmath>
using namespace std;
bool isPrime(int n){for(int i=2;i<=sqrt(n);i++)if(n%i==0)return false;return true;}
int main()
{
int n;
cin>>n;
if(isPrime(n))cout<<1;else cout<<-1;
return 0;
}
分析这个问题的时间复杂度的表示
如果是判断的是x,判断范围2到sqrt(x)
sqrt是平方根
对于任意个数值的角度考虑的话,对它开平方根后,运算次数呢?
以9为例,判断范围是2到3,
如何用9求得3呢?
3^2=9
前提是有9还有2,如何求得3呢
3=log9
最后站在n个数值的角度考虑
时间复杂度就是:o(logn)
例2
1-1000这1000个数放在含有1001个元素的数组中,只有唯一的一个元素值重复,其他均只出现一次。设计一个算法,将它找出来,你能否设计一个算法实现?
提醒:找到并输出重复值的位置
方法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
将a[1]与a[2]到a[11]之间的每个数值进行比较,如果相同,则输出1和相同值的标号
for(i=2;i<=11;i++)
{
if(a[1]==a[i])
printf("%d %d\n",1,i);
}
将a[2]与a[3]到a[11]之间的每个数值进行比较,如果相同,则输出1和相同值的标号
for(i=3;i<=11;i++)
{
if(a[2]==a[i])
printf("%d %d\n",2,i);
}
................................
将a[10]与a[11]到a[11]之间的每个数值进行比较,如果相同,则输出1和相同值的标号
for(i=11;i<=11;i++)
{
if(a[10]==a[i])
printf("%d %d\n",10,i);
}
上述操作可以在凑循环嘛
for(j=1;j<=10;j++)
{
for(i=j+1;i<=11;i++)
{
if(a[j]==a[i])
printf("%d %d\n",j,i);
}
}
颜老板版本
#include"stdio.h"
#include"windows.h"
#include"math.h"
int main()
{
int a[12],i,j;
printf("请你输入11个数,只能有2个不同:\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\n",j,i);
}
}
system("pause");
}
更新版
#include<iostream>
#in
clude<cstring>
using namespace std;
int main()
{
int n;
cin>>n;
const int tmpLen = n;
int a[tmpLen+10];
memset(a,0,sizeof a);
for(int i=0;i<n;i++)cin>>a[i];
for(int i=0;i<n;i++)
for(int j=i+1;j<n;j++)
if(a[i]==a[j])cout<<a[i];
return 0;
}
方法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个数组,其中第1个数组用来存放输入的所有数值
第2个数组的长度比第1个数少1个
第2个数组的作用是:将第1数组每个元素的值作为第2个数组的编号使用
第2个数组中所有的数组元素的初始值为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次
还要判断数组中8的位置
o(n)
颜老板版本
#include"stdio.h"
#include"windows.h"
#include"math.h"
int main()
{
int a[12],b[11]={0},i,j,t;
printf("请你输入11个数,只能有2个不同:\n");
for(i=1;i<=11;i++)
scanf("%d",&a[i]);
for(i=1;i<=11;i++)//表示数组a中11个数值的遍历
{
b[a[i]]++;
if(b[a[i]]==2)
{
t=a[i];
break;
}
}
//最后得把t和数组a中a[i]所有数值比较
for(i=1;i<=11;i++)
{
if(t==a[i])
printf("%d\n",i);
}
system("pause");
}
更新版
#include<iostream>
#include<cstring>
using namespace std;
int main()
{
int n;
cin>>n;
const int tmpLen = n;
int a[tmpLen+10];
memset(a,0,sizeof a);
for(int i=0;i<n;i++)
{
int tmpN;
cin>>tmpN;
a[tmpN]++;
}
for(int i=0;i<tmpLen+10;i++)if(a[i]>1)cout<<i;
return 0;
}
最后提出该程序的第3种方法:我只讲思路,不讲代码,而代码自己写!!!
思考题:方法3:
思路:使用C语言中的 异或 ^
异或 一样为0,不一样为1
如果假设x=9
9^0 结果是 9
9^9 结果是 0
#include<iostream>
#include<cstring>
using namespace std;
int main()
{
int n;
cin>>n;
const int tmpLen = n;
int a[tmpLen+10];
memset(a,0,sizeof a);
for(int i=0;i<n;i++)cin>>a[i];
for(int i=0;i<n;i++)
for(int j=i+1;j<n;j++)
if(int(a[i]^a[j])==NULL)cout<<a[i]<<" ";
return 0;
}
课后习题
题目来源:[LeetCode]11. 盛最多水的容器
难度标签:中等
算法标签:双指针,DP,枚举
题目描述:
给你 n 个非负整数 a1,a2,…,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
说明:你不能倾斜容器,且 n 的值至少为 2。
示例:
输入:[1,8,6,2,5,4,8,3,7]
输出:49
题目思路:
有题可知我们的目标是明确能盛如多少水量,此时问题转化为水量在整个可能存在的等式当中的关系。
实际读题之后我们发现,水量受到宽度
,高度
两个要素影响,且不是单纯递增关系,因此排出本来设想的贪心。
1.我们用最直接得想法可以瞄准暴力,列举所有可能存在再判断.
此时我们将目光瞄准为求最大水量的话,可以得到等式max_area=max(max_area,min(height[l],height[r])*(r-l))
最大水量等于所有情况中(现存宽度乘两边高度中相对较低的一边)的最大值
。
2.有了这个表达式之后剩下得任务则转换为降低降低频次。
依据题意两边的概念我们很容易想到双指针移动的方式,且因为等式高度关系,每次移动保持寻找更高值
即可,找到尽可能最大的二位面积即可。
题目代码:
解法一:双指针,动态规划
时间复杂度:o(n)
状态 :通过
class Solution {
public:
int maxArea(vector<int>& height) {
int max_area=0,l=0,r=height.size()-1;//max_area假设面积,l,左端点,r右端点
while(l<r)
{
max_area=max(max_area,min(height[l],height[r])*(r-l));//面积等式
if(height[l]<height[r])l++;//双指针移动,尽可能保持最大面积
else r--;
}
return max_area;
}
};
解法二:蛮力算法
时间复杂度:o(n^2)
状态 :超时,48 / 50 个通过测试用例,显然能过掉大部分数据,在oi赛制还是能拿分嘛
class Solution {
public:
int maxArea(vector<int>& height) {
int max_area=0;
for(int i=0;i<=height.size()-1;i++)
for(int j=i;j<=height.size()-1;j++)
max_area=max(max_area,(j-i)*min(height[i],height[j]));//暴力列举所有情况判断
return max_area;
}
};