本篇文章是在做LeetCode 题目#524时总结的,主要弥补了二维数组、多个字符串的存储方法、qsort的使用三方面的知识。
#524. 通过删除字母匹配到字典里最长单词
题目类型
双指针
做题总结
1. 关于二维数组的用法
- 二维数组的概念:二维数组和一维数组差不多,只是其数组元素为一维数组,其实a[0]、a[1]...就是一维数组的首地址。要想理解好二维数组的概念,需要理解二维数组的内存模型,以
int a[4][3]
为例,[___|___|___][___|___|___][___|___|___][___|___|___] ^ ^ ^ ^ a a+1 a+2 a+3 int ** a[0] a[1] a[2] a[3] int * a[0][0] a[1][0] a[2][0] a[3][0] int 下标与指针的关系和一维数组相同: a[1] = *(a+1) a[2] = *(a+2) ... a[1][1] = *(a[1]+1) a[1][2] = *(a[1]+2) ...
- 注意,a[0]和数组元素a[0][0]的地址在数值上是相同的,但二者不是相同的地址(指针)类型
- 二维数组的数组名:在
int a[3][4]
中,数组名a是指向第一个一维数组的指针,(a+1)指向第二个...而a[0] = *(a+0) = *a为第一个一维数组的数组名,a[1] = *(a+1)为第二个一维数组的数组名... - 如何声明一个指向整形数组的指针?:
int (*p)[4]
,这里必须指明你要指向的数组的元素数量,这样p
在运算时才能够进行正确的偏移,如p++
会将地址增加4*sizeof(int)
。可以使用指向数组的指针来对二维数组进行操作,如
如果想要一个指针来逐个访问二维数组中的元素,那么可以这样声明:int a[3][4]; int (*p)[4] = a; p++; ...
int *p = &a[0][0]
或int *p = a[0]
- 以多维数组作为函数参数时,对应函数形参怎么声明?:作为函数参数的多维数组名的传递方式和一维数组相同——实际传递的是一个指向数组第一个元素的指针。首先看一维数组的数组名作为函数参数的情况:
这里func1的函数原型可以是以下2种中的任何一种:int a[10]; ... func1( a );
再看二维数组的情况:void func1( int *vec ); void func1( int vec[] );
这里func2的函数原型可做如下声明:int b[4][10] ... func2( b );
注意以下声明是错误的:void func2( int (*mat)[10] ); void func2( int mat[][10] );
void func2( int **mat );
。虽然二维数组名属于指向指针的指针,但其指向的是一维数组的指针(首地址),这里包含了一维数组的性质——元素个数,而int **mat;
指向的是整形变量的指针(地址)。如果把二维数组名b
传递给函数void func2( int **mat )
,则在进行mat的运算操作时会产生问题:mat++
只能将地址偏移sizeof(int)
,这相当于mat指向数组b[4][1],如果是void func2( int (*mat)[10] );
,则mat++
会将地址偏移10*sizeof(int)
。 - 如何存储一组字符串?:有2种方法,一种是用指针数组,声明及初始化如下:
或char *ps[] = { "just", "do", "it" };
使用这种方法,数组元素为各个字符串的指针,占用空间大小为char *ps[3] = { "just", "do", "it" };
3*sizeof(char *)
另一种方法是用二维数组,声明及初始化如下:
使用这种方法,字符串的内容全部存储在二维数组中,占用空间大小为char s[3][10] = { "just", "do", "it" };
3*10*sizeof(char)
,这种方法占用空间较多,尤其是字符串长短差异很大时,会有很多空间浪费产生。 - sizeof(数组名)的值是多少?:数组名虽然是一个指针,但sizeof(数组名)并不是一个指针所占用的字节,而是整个数组占用的字节数。例如:
i的值为100,而不是32(由具体的指针位数决定)char s[10][10]; int i = sizeof( s );
2. 关于qsort中cmp函数的定义
qsort用于对数组元素进行排序,函数原型为void qsort (void* base, size_t num, size_t size, int (*compar)(const void*,const void*));
其中compar为比较函数,qsort函数会把要比较的2个数组元素的指针传递给它,并根据其返回值进行排序。由于传递到compar函数的为元素指针,因此其内部实现一般有如下形式:
int compareMyType (const void * a, const void * b)
{
if ( *(MyType*)a < *(MyType*)b ) return -1;
if ( *(MyType*)a == *(MyType*)b ) return 0;
if ( *(MyType*)a > *(MyType*)b ) return 1;
}
即首先要把void *
类型的指针转换回数组元素的指针类型,然后再进行比较,如整形数组的排序:
int compar( const void *a, const void *b )
{
return ( *(int*)a - *(int*)b ); //从小到大排序
}
int a[4] = { 3, 1, 4, 5 };
qsort( a, 4, sizeof(int), compar );
又如字符串数组的比较:
int compar( const void *a, const void *b )
{
//注意,由于比较的是二维数组的元素,因此qsort传递给
//compar的是数组元素的指针,也就是各一维数组的指针,
//其类型和二维数组名相同,为指向字符数组的指针,其
//声明方式为:char (*p)[5],因此类型转换为(char (*)[5]),
//由于strcmp需要的是字符串的指针,因此需要解引用。
//强制转换后a的类型为指向字符数组的指针,因此解引用
//后类型为字符数组的数组名(首地址),也是字符串指针
//,于是就可以给strcmp使用了。
return strcmp( *(char (*)[5])a, *(char (*)[5])b );
}
char a[3][5] = { "go", "to", "do" };
qsort( a, 3, 5*sizeof(char), compar );
又如字符指针(字符串的字面值)数组的排序:
int compar( const void *a, const void *b )
{
//由于数组元素为字符指针类型,传递给
//compar的是字符指针的指针(地址),
//因此应先转换回类型char **
return strcmp( *(char **)a, *(char **)b );
}
char *a[3] = { "go", "to", "do" };
qsort( a, 3, sizeof(char *), compar );