1.数据再处理之去重 数据的再处理是一个很大的方面,但鉴于本人水平不高,只能班门弄斧的学到什么就去总结什么,今天姑且整理一下关于数据的去重; 为了更好地说明,这里先举一个例子: 问:输入十个整数,分别求出这十个整数 B 的结果,统计有几种不同的结果; 分析:十个数据是我们的输入数据,这十个数 分别B 又会得到十个数据 ,而本题显然还需要对这十个结果进行再处理,即去重;
(1)标记法去重 何为标记法?我们可以定义一个结构体,当然有时不去定义也没有关系,定义结构体是为了更好的说明:
struct Bdata{ int data; bool flag; Bdata(){ data=0; flag=false; } }d[10];这里我们定义了一个结构体,data成员是我们得到的数据,在本例中即 B 后的结果,flag是一个标记,在本例中 判断是否有数据相同的情况,我们可以在输入数据时就对 flag进行处理,最后判断flag的值去输出数据,即可以达到 去重的效果; 标记法不仅仅可以用于去重,这是一个很灵活的方法,比如我们可以定义一个 标记time,time的值随我们的每一次输入数据而增加值,那么我们就可以利用 标记time来倒序输出我们的数据; (2)桶原理去重 emmm,桶原理是我自己胡编的原理,最开始接触这种思想是从桶排序开始,桶排序的思想就是:假定我们有无数多个桶,而每一个数据就是一个小球,数据的大小就是小球的编号,当然我们也把所有的桶按0~??进行了编号,这个小球的编号是多少,就把它放进和它相同编号的桶里,最后的结果就是,有可能一部分桶里面没有一个小球,又有可能一部分桶里面有多个小球,我们只要从0开始依此遍历所有的桶,把有小球的桶的编号输出,如果有的桶有多个小球,就多次输出,这样就完成了排序的任务。至于桶,我们可以用一个大数组来实现;数组的值最开始设为0,桶里面每有一个小球,就让值加1,这样就可以控制对一个桶有多个小球的情况究竟要输出多少次的问题; 很显然,桶排序的思想是 用空间来换效率,这种方法会造成空间的大量浪费,但胜在人家效率高啊; 至于如何用桶排序的思想 去重,只要稍微想一想就不难想到:
int a[42];//因为 B,故存放结果的桶有42个就足够了 int data; for(int i=0;i<10;i++) { scanf("%d",&data); a[dataB]=1;//结果入桶 } for(int i=0;i<42;i++)//遍历所有桶 { if(a[i]){ count++; } }(3)STL去重 STL是C++里面很好用的一个工具,这里介绍两种利用STL的去重方法: #1.set去重 set意为集合,它是一个对内部数据自动进行排序和去重的容器; 只需要利用 insert()函数插入数据,以及size()函数获得容器内元素个数,就可以轻松解决本例的问题; 但注意一点,set内元素的访问只能通过迭代器来完成; #2.unique去重 unique是下的函数,关于unique的用法这里进行一个说明: unique的作用是“去除”容器或者数组中相邻元素的重复出现的元素,注意这里的去除不是真正意义的去除,它和erase不一样,它的去除只是把重复的元素全部放到了数组或者容器的最末尾,而它的返回值则是一个指向去重后的数据串的最后一个数据的下一个位置的迭代器; 另外,由于unique的"去除机制"是相邻两个元素进行两两比较,然后去除重复,故大部分情况下使用unique时都要先对原数据串进行排序;
2.求最大公约数与最小公倍数 (1)最大公约数 可以用 gcd()函数 实现,即gcd(int a,int b); gcd函数的原理是欧几里得算法,我们可以自己编写gcd函数,也可以调用C++里面自带的gcd内建函数; #1 直接调用gcd:
int a=8,b=6; cout<<__gcd(a,b);//输出2,注意gcd前面有两个_符号,不能省略;#2 编写gcd函数(利用递归实现)
int gcd(int a,int b){ if(b==0) return a;//5和0的最大公约数是5不是0 else return gcd(b,a%b); }//具体原理参考欧几里得算法(辗转相除法)(2)最小公倍数 可以用 lcm函数 实现 也可以在求出最大公约数的基础上,利用 最小公倍数=a*b/最大公约数实现
3.再认识一下 / 和 % 运算符 对于整型数据来讲,/ 运算符 可以让原数据 去掉最后一位, 而 % 运算符则可以 得到原数据的最后一位,结合利用 / 和 %,就可以做到在不知道 整型数据有多少位的情况下,分开得到它在每一级位权上的数; 这个知识点我在 数组与整型数据 的转换中已经学习过,但在今天的题目中仍不能想到并去灵活地运用,故再提一次加深印象;
4.位运算 本来是打算直接在这篇笔记里面总结的,但看了看发现位运算涉及的知识有点多,在这里总结篇幅不是太够,所以决定再写一篇笔记,专门记录位运算相关的知识,慢慢总结,慢慢消化;
5.再回顾 scanf 关于scanf一直有几个掌握的不是很牢固的地方,今天刷题之余抽空做了一个总结: (1) %f 与 %lf 在 printf里面,对于 float 型数据,用%f 或者 %lf 都没有任何问题,结果也不会受影响,但对于 double 型数据,用 %f 可能会导致结果错误; 在 scanf 里面,double 类型使用了%f格式,会导致输入值错误,float类型使用double类型不仅会导致输入错误,还可能引起程序崩溃; 所以最好 不管是 printf 还是 scanf ,都严格按照 数据类型 选择 格式符; (2)scanf 是有返回值的 , 读入数据成功 它会返回 读入成功的 数据个数,否则返回EOF; (3)scanf("%*d");
是scanf函数中的一个修饰符,它的作用是只向输入缓冲区中传入一个数据,而不把这个数据赋给任何变量; 另外,关于缓冲区的问题,我一直都有做一个详细的了解的打算,下次也做一个总结;6.scanf与缓冲区 什么是缓冲区?缓冲区就是缓存,也就是Cache ,属于内存空间的一部分,CPU访问缓存的速度要比访问内存的速度还要快;在计算机工作情况下,要查询或者访问一个数据时,CPU优先在缓存中查找,如果找不到再在内存中查找,因此为了使计算机的运行效率达到最大化,缓存中的数据要进行不断地更换,一般缓存中的数据都是最近一段时间内操作人员使用最多的数据;另外注意一点,缓存中的数据是内存中一小部分数据的复制,这一小部分数据通常就是访问最频繁的数据; 好了,扯得有点远了,再回到缓冲区上,缓冲区按使用方法不同又分为输入缓冲区与输出缓冲区,对C来说也就是stdin流与stdout流,这里提一点,对于从键盘中得到的数据,是存放在一个叫键盘缓冲区的地方,并不是输入缓冲区; 对于scanf这个函数,它是从输入缓冲区,即stdin流中读入数据的,当缓冲区中存在数据时直接读入,缓冲区为空时则等待用户输入; 在输入时,用户输入的数据先存放在键盘缓冲区中,只有按回车之后这些数据才会进入输入缓冲区中;
缓冲区可以分为三类:全缓冲、行缓冲和不带缓冲; (1) 全缓冲 在这种情况下,当填满标准I/O缓存后才进行实际I/O操作。全缓冲的典型代表是对磁盘文件的读写; (2) 行缓冲 在这种情况下,当在输入和输出中遇到换行符时,执行真正的I/O操作。这时,我们输入的字符先存放在缓冲区,等按下回车键换行时 才进行实际的I/O操作。典型代表是标准输入(stdin)和标准输出(stdout); (3) 不带缓冲 也就是不进行缓冲,标准出错情况stderr是典型代表,这使得出错信息可以直接尽快地显示出来;
什么情况会引起缓冲区的刷新: (1)缓冲区满时; (2)行缓冲区遇到回车时; (3)关闭文件; (4)使用特定函数刷新缓冲区; 这里的特定函数有很多,但鉴于对于初学者来说(比如我)平时用上的几率不大,故暂时不作整理,等到以后有机会会进行补充;
7.精确得到圆周率的方法 (…圆周率的那个pai怎么用电脑打出来…) (1)math库里面已经定义了 高精度的 M_PI 作为圆周率值; (2)调用math库,再利用 acos(-1.0) 即得到精确的圆周率值;
8.C++中的std 在C++中,std是一个类,作为名称空间,std:: 是 名称空间标识符;
以 举例: C++标准库非常庞大,为了避免程序员自己定义的成员或者变量与标准库中一些成员冲突,把标准库中的一切都放在名称空间std中。但这又会带来了一个新问题。无数原有的C++代码都依赖于使用了多年的伪标准库中的功能,他们都是在全局空间下的。 所以就有了和<iostream.h>等等这样的头文件,一个是为了兼容以前的C++代码,一个是为了支持新的标准; 在早期的C++中, 输入输出实际上用的是 <iostream.h>,是 .h 型的头文件,但是 C++ 为了和 C区分开,后来规定不再支持 .h 头文件;而 C 与 C++ 的一个区别之一就是 :使用 C文件里面的成员不需要预处理,而 使用 C++ 里面的 则需要预处理; using namespace std; 就是一条预处理语句,它声明在以后的程序中可以随便 调用 里面的成员;
于是在想要使用C++标准库成员时,有以下几种方法:
(1) using namespace std; //一条语句解决 所有 后顾之忧; (2) using std::cin; //一条语句解决 cin 的后顾之忧; using std::endl; //一条语句解决 endl 的后顾之忧; (3) std::cin<<a; //在主函数中使用,用一次使用一次;9.以后刷题一定要做的事 写完一道题后,应该大部分情况下我都是以最常规,最普通的思路做出来的,这种方法做出来的题,虽然绝大部分都能AC,但时间复杂度太高,这种程序其实是可有可无的,能解决问题,但是解决问题起来非常吃力,这种程序写出来有什么用! 以后做完一道题后,不能急于去写下一道题,而要在这道题上再停留一会儿,这个停留时间主要是用来思考有没有什么更优的写法,如果实在想不出来,再去看评论区里大牛的解析,思考别人的思路,总结别人是在那些方面比你强; 另外,关于更优的解法,这些天我刷了几十道题,发现大部分有更优解的,绝大部分都与数学知识相挂钩,这就给我以后的刷题提供了一个思路:把每一道题当成数学题再做一遍。
ps: 5.24 这几天的刷题遇到了好几个数学方面的问题,几乎每一道题都让我无从下手,也有的题目简直超出了我的理解能力范围,不过我看题解,有一道题好像还涉及到了 博弈论 类似的,虽然是入门,但看完题解 突然感觉 博弈论 好有意思啊,百度了下 发现 这应该也算是数学的一个分支,而且在计算机科学领域应用广泛,以后有空一定要研究研究,,,虽然也不知道我的智商能不能吃得下,,,不过先记下再说; 5.25 评论区这都是些什么人?!!!刷题刷到智商受到奇耻大辱!我严重怀疑 我上的小学怕是个假的,,,,,