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

逗号运算符

表达式,表达式,…

左到右

--

17. free(): invalid next size (fast): 0x0000000000d00c40 ***

  • 要free的对象被损坏了,可能其他地方出现了越界写;
  • free了已经被释放的内存指针,或者释放了没有内存没有申请成功的指针
  • 指向free对象的指针不正确,像我的问题中一样,释放的指针不是malloc时返回的指针;

18. 在涉及到二叉树结构时,切记每次malloc为node分配空间之后,同时将左子树和右子树指向空,第897. Increasing Order Search Tree出错原因记录。

你可能感兴趣的:(LeetCode)