[算法课]全面翻新计划!第一周全解

上课部分内容

分析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;
    }
};



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Freeman Z

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值