素数查找 && 二分查找

素数查找 && 二分查找

背景

今天在刷洛谷的题的时候,遇到了 质数口袋 这道题,作为刷题人的第一直觉肯定就是去想对应的算法(狭义的算法),但是我并没有想到什么合适的算法,看了一下旁栏的算法标签,竟然空空如也。然后我就开始了正常解题(暴力枚举模拟

判断素数(一般思路)

废话不多说,先上代码 (注:本人不是很喜欢 C/C++ 里面的 Bool 类型)

int isprime(int n) {
	if (n <= 1) {					//对 1 和 2 这种情况进行单独判断
		return 0;
	} else if (n == 2) {
		return 1;
	} else {
		int i = 0;
		for (i = 2; i < n; ++i) {   
			if (n % i == 0) {
				break;
			}
		}
		if (i == n ) {             //若 i == n 则证明循环是正常结束的,而不是因为break跳出的
			return 1;
		} else {
			return 0;
		}
	}
}
  • 当然 for循环也可以这样写

    for(i=2; i<sqrt(n)+1; ++i)
    

    这样优化,可以大大减少循环的次数,但是需要一定的数学知识

判断素数(埃氏筛选法)

导入

  • 要得到自然数n以内的全部素数,必须把不大于 根号n 的所有素数的倍数剔除,剩下的就是素数。

  • 在对普通方法的 for 循环进行优化的时候,就已经提到了 根号n ,在上面的循环中,它的作用主要是为了减少循环的次数

    /*
    	解释一些为什么是 根号n (算是数学问题,可以不看,只记住结论)
    	---------------------------------------------------------------------------------------------
    	在这里就不去说公式的推导了(个人不喜欢推导的公式)
    	如果一个数可以看成两个数乘积(先不论是不是素数),那么这两个 **乘数** 肯定是一大一小
    	(极限情况是相等)
    	所以说,其中较小的一个乘数的取值范围为 (1 ~ sqrt(n))
    	所有我们需要排查, n 能否被其中较小的积整除 即可
    	---------------------------------------------------------------------------------------------
    	我感觉我已经用了非常通俗的语言来讲述这个问题了,希望对你能有所帮助
    */
    

    埃氏筛选法的代码实现

    int prime[0X7ffffff] = {0};
    
    //返回从 1 到 n 的所有的素数
    int *isprime(int n) {
    	int num = 0;                 //用来记录素数的个数
    	//flag[i] 代表的含义是:i 是否是素数
    	int *flag = (int *)malloc((n + 1) * sizeof(int));
    	for (int i = 0 ; i < n; ++i) {
    		flag[i] = 1;            //初始化所有数字为素数
    	}
    	for (int i = 2; i <= sqrt(n); ++i) {
    		if (flag[i] == 1) {
    			for (int j = i * i; j <= n; j += i) {
    				flag[j] = 0;    //将素数的倍数全部叉掉
    			}
    		}
    	}
    	for (int i = 2; i < n; ++i) {
    		if (flag[i] == 1) {
    			prime[num] = i;
    			num++;
    		}
    	}
    	return prime;
    }
    

    其实这两种解法的思想是不一样的,埃氏筛选法不存在判断,而是通过排除,而第一种则是一个数一个数去判断(无论是否优化)

    二分查找解素数

    话说,我是怎样想到用二分查找的呢?根据二分查找的特点

    • 二分查找适合用在对增删 要求不大的 有序表
    • 从 0 到 n 还不叫有序表吗(手动狗头)
    • 第一次直接模拟的时候 TML 了,也就是说时间太长了,而二分查找的时间复杂度是 logn, 接近 n

    所有就很顺其自然的想到了二分查找。

    一个二分查找的模板

    可能你还没有了解过 二分查找 ,所有我们先用一个经典的二分查找的模板来引入这个概念

    //如果目标元素存在,则返回其的下标,如果不存在返回-1;
    int search(int num[], int target) {
    	int left = 0;
    	int right = sizeof(num) / sizeof(num[0]) - 1; //left 和 rigiht 代表的是下标
    	while (left <= right) {      				//当情况允许存在时(或说合理时)
    		int min = left + (right - left) / 2;     //防止左右边界相加会直接爆掉
    		if (num[mid] == target) {
    			return mid;
    		} else if (num[mid] < target) {
    			left = mid + 1;
    		} else if (num[mid] > target) {
    			right = mid - 1;
    		}
    	}
    	return -1;
    }
    

    二分查找本身也并不难,我觉得通过上面这个小例子,就算是没有了解过二分查找的也应该对其有一个大概的了解了

    至于,有些地方我没加注释,你却又没有看明白的,建议多看几遍:(下面的话仅代表个人的观点,不喜勿喷)

    • 首先是可以锻炼你对知识的理解能力(或说你学习知识的能力)
    • 其实我认为,完成一件较难的事情比完成简单的事情更有成就感;
    • 再者,我如果每一行都加注释,该不明白的也不一定明白(毕竟每个人理解问题的角度不一样),还会破坏我代码整体的美观度
    • 最后,我这有一个听来的道理 “ 我认为,一个真正好的老师讲课时,不应该把所有的东西都讲完,要留三分让学生自己去悟,学生学的似懂非懂就会去自己思考,然后某一天,突然恍然大悟,原来是这么回事啊!如果一个老师能带动学生的思考,那么我觉得这个老师就算是一个好老师
    • 当然,我没有认为我是一个老师,但我是以 及其负责任的态度 来写这篇题解的。

    二分查找找素数的思路

    • 第一步,打表()
    • 第二步,调用二分查找,主函数一个循环进行查找目标数是否在表内。

    至于打表的代码我就不再赘述了,我其实不太喜欢打表,之所以讲这个方法,只是为了引出 二分查找


    一些数学味很大的代码

    辗转相除

    int gcd(int a, int b) {
    	int t;
    	while (b != 0) {
    		t = a % b;
    		a = b;
    		b = t;
    	}
    	return a;
    }
    

    证明过程太数学了,放在这篇文章里不太合适,如果你感兴趣的话,可以移步哔哩哔哩辗转相除的数学解释

    回文数判断模板

    • 你可能也想知到判断回文数的函数怎么写
    int ispalindrome(int num) {
    
        int temp=num,ans=0;
        while (temp!=0) {
            ans=ans*10+temp%10;
            temp/=10;
        }
        if (ans==num)
            return 1;
        else
            return 0;
    }
    

    至此,这篇题解就算是写完了。(完结撒花)

    • 因为是第一次写题解,我总共花费了 165 分钟(其实不止)
    • 浅浅谈一下自己对写题解的看法吧
      • 写题解是一件利人利己的好事,写题解不仅可以帮你自己深入理解掌握知识点,还可以帮助别人。感谢HAL_20大佬的带动作用
      • 写一篇题解很简单,但是写一篇好的题解并不容易,我昨天晚上已经规划了这篇文章的大致内容,但是仍然用了近三个小时的时间(也可能是我菜,或者表达能力不强)
      • 我认为并不是说只有难题适合写题解,只要你写的有营养就好。(不知道我写的怎么样,希望大家在下面给我评论指正一下啊,我必定虚心接受所有批评,并且逐步改正)
      • 以后没有什么事情的话打算三天或者两天一篇 高质量 (高质量并不是说题解的质量,而是我投入的程度)题解

    最后,你看我都这么努力了,你不打算点个赞吗!

    写题解的时间统计

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值