本篇文章是在做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 );