[算法课]全面翻新计划!第一周全解 原创
文章目录
上课部分内容
分析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;
    }
};
 
                    
                
 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号