LeetCode笔记

本人比较菜,学了这么久的计算机,私以为自己基础知识还是有一些的。然而,一写代码就各种错误=_= 这篇文章是为了记录我在做LeetCode时遇到的各种错误以及原因,基本上都是很基础很基础的知识。

写代码注意事项:
  • 写明注释,以便以后查阅时能够立刻看懂;
  • 代码开始,先检查边界值。如果为边界值,直接返回相应结果;如过是指针则检查是否为NULL,如果是数字检查是否为0

1. mysql数据库相关
  • 数据交换 set sex = CHAR(ASCII('f') + ASCII('m') - ASCII(sex)) 或者 set sex = CHAR(ASCII('f') ^ ASCII('m') ^ ASCII(sex)) 这种方法适用于任何需要交换数据的场景 不仅是在数据库中
或者 update salary set sex = if(sex = 'm', 'f', 'm')
或者 update salary set sex = case sex when 'm' then 'f' else 'm' end
或者 update salary set sex = (case when sex = 'm' then 'f' when sex = 'f' then 'm' end)
可延伸为两个数不借助第三个数的情况下交换
a = a + b; b = a - b; a = a - b;
a = a ^ b; b = a ^ b; a = a ^ b;

2. Time Limit Exceeded错误
Run Code时出现这种类型的错误,一定是由于代码中循环部分出错。因为运行代码一般给出的是十分简单的例子,即使所用的算法时间复杂度再大,也不会出现超时问题,那么就一定是代码出错。
而代码中会出现超时问题的部分必然与循环相关,出现了死循环,此时应该仔细检查循环部分,是否因为某个变量的设置,导致循环无法结束。

3. load of null pointer of type 'const int'
在调用函数返回时,返回值如果是一个常量,则没问题。
返回值若为指针,则需注意会出现这个错误。如果返回的指针地址指向函数内的局部变量,在函数退出时,该变量的存储空间会被销毁,此时去访问该地址就会出现这个错误。
解决办法有以下三种:
  • 返回的指针使用malloc分配空间
  • 将该变量使用static修饰 static修饰的内部变量作用域不变 但是声明周期延长到程序结束 即该变量在函数退出后仍然存在
  • 使用全局变量
建议使用malloc分配空间返回

4. assignment to expression with array type
数组名不能被赋值,即数组名不能作为赋值表达式的左值,初始化时除外。
例如:
int *array[100];
array = (int *)malloc(sizeof(int)*100); 错误
再如:
char str1[];
char str2[] = "hello,world!"
str1 = str2; 错误
而同样的情况下,指针是可以这样赋值的
原因:数组声明后会在内存空间中开辟一段空间,而数组名指向那段内存空间的首地址,它相当于一个常量指针,所存放的地址不变,就像我们不能改变常量的值一样。因此数组名不能作为赋值表达式的左值。

5. warning: incompatible implicit declaration of built-in function ‘xyz’
在调用之前未声明被调用函数(比如未引入头文件),或者被调用函数的定义位置在调用之后,都有可能会出现这个警告。
原因:在上述情况下,编译器不知道被调用函数的返回值信息,C会使用默认声明,默认声明规定函数返回值为int类型。而GCC为一些标准函数内置了定义,如果默认声明和内置定义不符,则会出现这个警告。

6. 本地调试
leetcode中的代码结果不正确时 需要在本地添加main函数 将程序写完整 进行测试 但是我每次测试时都 会遇到segmentation fault的错误 而且这种错误常常伴有二维指针的参数 之前我都是在main函数中定义一个二维数组 调用时传递二维数组的名称 在被调用函数中以array[i][j]的形式来操作该数组 出错位置就在此 在mian函数中可以以这种方式操作数组 但是在被调用函数中 它没有二维数组的概念 只是指向一维数组的指针 若想在被调用函数中像操作二维数组那样操作该参数 应该在main函数中定义一个指针数组 使得指针数组的每一项指向二维数组中每个一维数组的开始地址 同时传递该指针数组给被调用函数 这样才能达到效果

7. 在本地测试时可以得到正确结果 单独执行leetcode上给的例子也可以通过 但提交时就是不通过 找了很久的原因 才发现是我使用了全局变量的原因 不太清楚leetcode测试时的逻辑 但我猜测他在测试每个例子时不会清空全局变量的值 导致后面的例子使用的全局变量不是初始值 当我把全局变量变为参数传递时 则顺利通过

8. overflow in implicit constant conversion
原因:这个错误就是:常量转换溢出。C语言中char, int, float, double,unsigned char, unsigned int 等数值有极限范围,当它们之间(隐式)转换时,可能因数值极限而超界溢出。有的编译器会报告这一类型的错误,并不是所有编译器都会报告

9. 指针数组初始化方法
指针数组中的每个元素都是一个指针,如下的array数组中的每个元素都是一个字符串指针,指向一个一维字符串数组。
char **array;
array = (char **)malloc(sizeof(char *) * 100) ; //array包含100个指针元素 为这100个指针变量分配空间
for(int i = 0; i < 100; i++)
array[i] = (char *)malloc(sizeof(char) * 50); // 为array中的每个指针变量进行初始化 上面的表达式只是为指针变量分配了空间 并没有为它们赋值 此语句为每个指针分配了长度为50个字符的空间 并将该空间的初始地址赋值给array中的指针

10. 当条件分支不止两个时,要考虑分支的执行顺序,否则,会出现错误,本来该执行第三个分支,结果在第一个分支处结束。举例如下:
if(i % 3 == 0)
array[i - 1] = "Fizz";
else if(i % 5 == 0)
array[i - 1] = "Buzz";
else if(i % 15 == 0)
array[i - 1] = "FizzBuzz";
你原本想让它在3的倍数处输出Fizz,在5的倍数处输出Buzz,在3和5共同的倍数处输出FizzBuzz,然而,这段代码永远不会执行到第三个分支。

11. 在被调用函数中使用返回二维数组时使用指针数组还是二维指针?
结果是使用二维指针
首先看一下指针数组和二维指针的区别:
指针数组
int *array[100]; //在定义时已经为该数组开辟了100个整型指针空间,只是它们都指向NULL
for(int i = 0; i < 100; i++)
array[i] = (int *)malloc(sizeof(int)); //为每个指针元素所指向的元素开辟空间,并将指针元素指向开辟的空间首地址
二维指针:
int **array; //定义了一个二维指针,并没有赋初值
array = (int **)malloc(sizeof(int *) * 100) //为100个整型指针元素开辟空间,并将该空间的开始地址赋值给array
for(int i = 0; i < 100; i++)
array[i] = (int *)malloc(sizeof(int)); //为整型指针元素所指向的元素开辟空间,同时将其地址赋值给指针元素
看起来指针数组和二维指针没有差别,都是首先为指针元素开辟空间,再为指针元素所指向的元素开辟空间并将其地址赋值给指针元素,即使得指针元素指向该空间地址。
但是作为函数的返回值来传递时,使用指针数组会出现load of null pointer错误,因为指针数组作为函数内的数组变量,在程序退出函数时,数组内的指针变量会被释放,而这些指针变量所指向的空间为malloc开辟,不会被释放,但是指针变量被释放。
反观二维指针,它的指针变量空间和指针变量所指向的空间都是由malloc开辟,所以在函数退出时,它的空间不会被释放,可以被调用函数使用。
因此,在函数需要返回二维数组时,需要使用二维指针来传递。

12. 当使用哈希同时散列空间过大时,可以使用char类型定义散列数组,节省空间和时间,如下例:
long int hash[200000] = {0};
可以写为:
char hash[200000] = {0};
能够达到同样的效果
char和int类型比较形式相同,都是if(hash[i] == 0) hash[i] = 1;

13. malloc和calloc区别
void *malloc(unsigned int size); 
void *calloc(size_t nmenb, size_t size); 
malloc和calloc都是在程序执行过程中为变量分配内存空间,他们的区别是malloc只是分配空间,而calloc不仅分配空间,还为所分配空间进行初始化,将nmenb个元素空间置为0.

14. 两数比较
整数:a > b
注意:操作符两边如果类型不同 会默认进行强制转换 如strlen(str) > b 由于strlen函数返回值为无符号类型 b为int 比较时会对b进行强制转换为无符号类型 此时如果b为负数 转换之后的变量b将会是一个很大的无符号整数 导致比较结果失效 这种情况可以设置b为正数或者对前者进行强制类型转换为int类型
字符串:strcmp(a,b) 等于零时相等,小于零时a串小于b,大于零时a串大于b串
浮点型 fabs(a - b) < 0.00001 两数之差的绝对值小于某个很小的数时 可判定两个数相等

15. double free or corruption (fasttop)
多次释放内存错误,例子如下:
hasCycle(struct ListNode *head)
struct ListNode *p = head;
p->next = p;
在函数hasCycle中对链表的指针进行更改,使得main函数中释放链表时出错。
在这个例子中,判断是否有循环存在,我的思路是每次遍历一个node时,将其next指针指向head或者指向它本身,以此作为标记,当此node再一次出现时,即为有循环存在。
然而,这样破坏了链表的连接性,那些被访问过的节点指向了head或者它本身,导致main函数中释放链表时出现多次释放内存的错误,而且,只有head可以被释放,剩下的node没有指针指向它们。

16. 运算符优先级
693  Binary Number with Alternating Bits
判断一个数的二进制形式是否每两个相邻的bit都不同,而二进制只有0和1。所以我的思路是如果为奇数,最后一位为1,那么前面肯定是0101010101才符合每两个数都不同,跟0x55555555按位异或为0则满足条件,故代码如下:
if(n % 2 == 1)
{
if(n ^ 0x55555555 == 0)
return true;
else
return false;
}
然而结果总不对,后来才知道是因为if(n ^ 0x55555555 == 0)实际执行时为if(n ^ (0x55555555 == 0)),背离了我的意图即if((n ^ 0x55555555) == 0),所以结果不对。
然而,这道题这样改了之后还是不对。因为按位异或要求两个操作数完全相同才能是0。每个无符号数都占32位,即使它的有效位只有三位,如数字5,其余的高位都为零,而按位操作这些高位也会参与,除非你知道这个数占多少位,那么选择相应0x55555555的多少位与之进行异或,所以这样异或之后肯定不会是零,如5^0x55555555=0x55555550
第二种思路:如果改为按位与,所有偶数与0x55555555进行按位与运算为零则满足条件,否则不满足条件,比如10为1010,1010^0101=0,10^0x55555555=0。但存在反例,如数字8,二进制表示为1000,8^0x55555555=0,而8并不满足条件。
第三种思路:将原数右移一位与它本身做按位异或操作,如果满足条件结果将是每个二进制位都为1。然而这种思路也需要考虑高位为零的情况,跟第一种思路类似,不通。
总之,想通过一次按位操作就得到判断似乎是不可达的,必须老老实实依次对该数的每一位与邻位进行比较=_=!
优先级
运算符
名称或含义
使用形式
结合方向
说明
1
[]
数组下标
数组名[常量表达式]
左到右
--
()
圆括号
(表达式)/函数名(形参表)
--
.
成员选择(对象)
对象.成员名
--
->
成员选择(指针)
对象指针->成员名
--

2
-
负号运算符
-表达式
右到左
单目运算符
~
按位取反运算符
~表达式
++
自增运算符
++变量名/变量名++
--
自减运算符
--变量名/变量名--
*
取值运算符
*指针变量
&
取地址运算符
&变量名
!
逻辑非运算符
!表达式
(类型)
强制类型转换
(数据类型)表达式
--
sizeof
长度运算符
sizeof(表达式)
--

3
/
表达式/表达式
左到右
双目运算符
*
表达式*表达式
%
余数(取模)
整型表达式%整型表达式
4
+
表达式+表达式
左到右
双目运算符
-
表达式-表达式
5
<< 
左移
变量<<表达式
左到右
双目运算符
>> 
右移
变量>>表达式

6
大于
表达式>表达式
左到右
双目运算符
>=
大于等于
表达式>=表达式
小于
表达式<表达式
<=
小于等于
表达式<=表达式
7
==
等于
表达式==表达式
左到右
双目运算符
!=
不等于
表达式!= 表达式

8
&
按位与
表达式&表达式
左到右
双目运算符
9
^
按位异或
表达式^表达式
左到右
双目运算符
10
|
按位或
表达式|表达式
左到右
双目运算符
11
&&
逻辑与
表达式&&表达式
左到右
双目运算符
12
||
逻辑或
表达式||表达式
左到右
双目运算符

13
?:
条件运算符
表达式1?表达式2: 表达式3
右到左
三目运算符

14
=
赋值运算符
变量=表达式
右到左
--
/=
除后赋值
变量/=表达式
--
*=
乘后赋值
变量*=表达式
--
%=
取模后赋值
变量%=表达式
--
+=
加后赋值
变量+=表达式
--
-=
减后赋值
变量-=表达式
--
<<=
左移后赋值
变量<<=表达式
--
>>=
右移后赋值
变量>>=表达式
--
&=
按位与后赋值
变量&=表达式
--
^=
按位异或后赋值
变量^=表达式
--
|=
按位或后赋值
变量|=表达式
--

15
逗号运算符
表达式,表达式,…
左到右
--

猜你喜欢

转载自blog.csdn.net/g1269420003/article/details/80451187