LeetCode做題筆記(1)——二維數組及qsort的compar函數寫法詳解


本篇文章是在做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]
  • 以多維數組作為函數參數時,對應函數形參怎么聲明?:作為函數參數的多維數組名的傳遞方式和一維數組相同——實際傳遞的是一個指向數組第一個元素的指針。首先看一維數組的數組名作為函數參數的情況:
    int a[10];
    ...
    func1( a );
    這里func1的函數原型可以是以下2種中的任何一種:
    void func1( int *vec );
    void func1( int vec[] );
    再看二維數組的情況:
    int b[4][10]
    ...
    func2( b );
    這里func2的函數原型可做如下聲明:
    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(數組名)並不是一個指針所占用的字節,而是整個數組占用的字節數。例如:
    char s[10][10];
    int i = sizeof( s );
    i的值為100,而不是32(由具體的指針位數決定)

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 );


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM