C++ 踩坑实例
Get to the key point firstly, the article comes from LawsonAbs!
0.常踩的坑
写算法题时,悲催不是你的wrong answer
,而是在你写完代码之后,你却发现你的思路存在纰漏。这里总结了一些我写题时的一些常犯错误,供大家编程时参考。
- 1.题意搞清楚了吗?
- 2.写
<
,>
号的时候,需要考虑会用到=
号吗?
上面这种问题,经常出现在二分法,排序等情况下。 - 3.写
if
条件的时候,要考虑后面的else
是什么样的情况?是二选一的吗?if
内的代码块有必要二选一执行吗? - 4.
if
中的条件是什么样的情况成立?&&
和||
符号正确吗? - 5.数组下标越界了吗?
如果出现Runtime Error
,通常就是出现了数组越界的情况,这时,就需要考虑是否在判断数组边界是否越界。 - 6.数组开的够大吗?是导致
Runtime Error
的原因吗? - 7.递归有明确的返回条件吗?递归哪里可能会出现死循环?
- 8.
while
,for
循环是导致死循环的原因吗? - 9.二分法时,如果涉及到比较精度的问题,可能会因为精度而导致死循环。
- 10.可能在有些变量没有初始化时,导致本地运行和评判机运行出现偏差。从而出现明明“对的程序”无法AC
- 11.二分法的边界一定要搞清楚!十分重要
- 12.一些常数的取法是否有问题?比如说
π
是取成3,还是3.14,还是3.1415926?如果题意没有说明,那么这些就是容易出现问题的地方。 - 13.贪心算法能证明出来是正确的吗?如果不能,请谨慎使用,否则在你不仅WA了答案,还浪费了时间。
给我这样的感受的题有很多,比如背包问题(让你求出平均价值最大的放法,如果是直接按照 价值/体积 形成的权重来从大到小放,是会出现问题的。);再比如络谷 P1514 引水入城 问题,这里是我的关于此题的题解,我最开始的想法是: dfs+贪心(失败)。直到WA之后才恍然大悟自己是多么幼稚!
------------------------智慧分割线-------------------------------
1.define
使用不当
#define N 100000000001
define 定义的值不能过大,否则会到值编译错误,停止工作。
#define
的用法不仅仅局限于对数据,同样对于字符串,以及其他的都是适用的,比如以下的程序。
#include <stdio.h>
#define P printf("I Love programming\n");
int main()
{
P;
return 0;
}
如果觉得define
不好使,也可以使用const来定义静态变量。
2. 未引用正确的方法库
报错:[Error] 'strcmp' was not declared in this scope
需要针对具体的报错信息添加头文件。比如在上面的这个报错信息中,就需要添加#include<cstring>
【c++】或者是#include<string.h>
【c】。
3.整型的范围【非常重要!】
算法题中经常会出现整数范围的考察,这是一个非常基础但重要的知识点,务必掌握。这里我讲解一些最常用的整型。
3.1 int
范围大小
int
声明的数据是带正负号的整数,一般情况其占内存大小是4B
,即32位,有一位是符号位,故最大正整数是2^31-1
,最大负数是2^31
【这与计算机的存储方式有关,因为是二进制补码存储】。在 #include<climits>
头文件中可以看到int的最大值和最小值。代码如下所示:
#include<iostream>
#include<climits>
using namespace std;
int main(){
cout << "INT_MAX="<< INT_MAX<<"\n";
cout << "INT_MIN="<< INT_MIN<<"\n";
}
3.2 long long
的范围大小
同样的道理,我们可以获取到long long
类型的最大、小值。
3.3 其它
这个库里 (应该) 还有很多其它好东西,值得研究。
4.位运算
位运算是非常重要的一类运算方法,下面我逐一讲解。
4.1 位移运算
位移分左移和右移。
- 左移一位就相当于
*2
- 右移一位相当于
/2
下面结合一个实例来看一下。
...
void test11(){
int a = 1<<1, b = 1<<2;
cout <<"a = "<< a <<"\n";
cout <<"b = "<< b<<"\n";
}
int main(){
test11();
}
但是需要注意,我们入如果是下面这么写,就会出现问题:
void test10(){
ll a ,b;
a = 1ll << 40;
b = 1 << 40;
cout <<"a = " <<a<<"\n";
cout <<"b = "<<b<<"\n";
}
int main(){
test10();
}
得到的执行结果如下:
需要注意的就是:cpp程序中的1并非是long long
,默认是什么类型我也不清楚(我猜测是int
型)。 所以在左移40位之后,直接得到了结果0,而非想要的正确答案。
4.1.2 关于cpp
中的一个整数是什么类型的讨论
使用如下代码测试:
int main(){
cout << sizeof(1)<<"\n";
cout << sizeof(1ll)<<"\n";
}
执行结果如下:
可以看到所占内存大小分别是4B
和8B
。
我就是因为这个问题没搞清楚,在写一道程序题时就出现了问题。本来可以用long long
存储的数,我自己硬是写了一个高精!
5.二叉树相关常见错误
5.1 对指针理解不够
如下代码是我在为每个节点设置树高。但是其中有一处很明显的错误。
void btHigh(node* root)
{
if (root == NULL){
root->height = 0;
return ;//一定不要少了这个边界条件
}
btHigh(root->lchild);
btHigh(root->rchild);
//二叉树的高度为左子树和右子树中高的那个高度加1
int ret1 = root->lchild->height;
int ret2 = root->rchild->height;
root->height = ret1 > ret2 ? ret1 + 1 : ret2 + 1;
}
里面存在的错误代码是:
if (root == NULL){
root->height = 0;
return ;//一定不要少了这个边界条件
}
root->height = 0
是典型的低级错误,怎么能在root=NULL
的条件下给root->height
赋值呢? 也就是说,if
中的判断是要有意义的,不是白判断的。