qsort函数的使用

今天在刷题时,题目:

输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。

遇到了qsort函数的使用,本来感觉挺简单的,但是,让我一思考二考虑,给想的有点儿复杂,但是,又没弄懂到底是怎么回事,暂时记录一下正确的做法,以后能正确的使用就行了。

1、qsort函数的使用

void qsort(void*base,size_t num,size_t width,int(__cdecl*compare)(const void*,const void*));

各参数:1 待排序数组首地址 2 数组中待排序元素数量 3 各元素的占用空间大小 4 指向函数的指针

有下面的程序:

#include 
#include 
#include 
#include 

int compare(const void *, const void *);
using namespace std;

int main(){
    const char *a[10] = {"enter", "number", "size", "begin", "of", "cat", "case","program","certain","a"};
    cout << "该字符数组中有;" << endl;
    for (int m=0; m<10; m++) {
        cout << a[m]<< " ";
    }
    cout << endl;

    qsort(a, 10, sizeof(a[0]), compare); //!! 第三个参数也可以sizeof(char *)或者直接写4

    cout << endl;
    cout << "排序后的字符数组为:" << endl;
    for (int m=0; m<10; m++) {
        cout << a[m]<< " ";
    }
    cout << endl;
    return 0;
}

int compare(const void *c, const void *d){
    int m,n,r;
    char **a, **b; //!!
    a = (char **)c;
    b = (char **)d;
    m = strlen(*a);
    n = strlen(*b);
    r = m - n;
    if (r != 0)
        return r; //排序规则:当长度不同时,按照长度大小排序,长的(r为正值)在前,短的(r为负值)在后,
    return
        memcmp(*a,*b,m); //当长度相同时,按照字符串的比较规则排序;
}

这儿需要注意的就是注释加!!的位置
1、qsort的第一个参数是a,即一个指针数组;
2、compare传入的应该是元素类型的指针。对于本例,传递给compare函数的参数类型和a相同,即一个指针数组的数组名,也即指针的指针,所以在compare函数中,所以需要先将形参从void* 转化为 char *,此时 形参(即* c 、 * d)就代表了指针数组中的每一个元素,而这每一个元素都是指针,指向字符串常量,所以strlen ( * c)就是求a这个指针数组中每个指针元素所指向的字符串的长度。
3、由于a是一个指针数组,那么a中的每个元素都是指针。qsort的第三个参数表示a中元素的大小, a中的元素都是指针,很明显,就是4字节。
输出为:

该字符数组中有;
enter number size begin of cat case program certain a

排序后的字符数组为:
a of cat case size begin enter number certain program
1’ 假如数组a我们不使用指针数组,而使用 二维数组的话,程序是这样的:
#include 
#include 
#include 

int compare(const void *, const void *);

using namespace std;

int main()
{
    const char *t[10] = {"enter", "number", "size", "begin", "of", "cat", "case","program","certain","a"};
    char a[10][10] ;
    cout << "该字符数组中有" << endl;
    for (int m=0; m<10; m++) {
        strcpy(a[m], t[m]);
        cout << a[m]<< " ";
    }
    cout << endl;

    qsort(a, 10, sizeof(a[0]), compare); // !! sizeof(a[0]) = 10

    cout << endl;
    cout << "排序后的字符数组为" << endl;
    for (int m=0; m<10; m++) {
        cout << a[m]<< " ";
    }
    cout << endl;

    return 0;
}

int compare(const void *c, const void *d)
{
    int m,n,r;

    char *a, *b; //!!
    a = (char *)c;
    b = (char *)d;

    m = strlen(a); //!!
    n = strlen(b);
    r = m - n;

    if (r != 0)
        return r;
    return
        memcmp(a,b,m);
}

需要注意的还是注释加了!!的位置,
1、此时a是一个二维数组,a[0]可以认为是这个二维数组的第一行的首地址(可以想象a[0]是a[0][0]、a[0][1]、a[0][2]这个一维数组的名字),所以qsort的第三个参数sizeof(a[0]) = 10,即可以这么认为:a数组中各元素占用的空间是 10 字节;
2、再来考虑compare的参数,在将a传递到qsort函数之后,qsort函数将一个和a同类型的指针传递到compare函数,a是一个char * [10] 类型的指针 (即一个指向a [10] (即a[10][10]去掉第一维) 类型的指针) ,通过将a强制转化为char * 之后,只是指针指向的数据类型的大小变小了(数据类型从char a [10] 变为 char),但是由于compare函数中都是将a指向的数据当作字符串来操作的(memcmp函数的参数,以及strlen都是操作指向字符串的指针),而字符串结束的标志是’\0’,所以即使我们将char * [10]类型的指针转化为char *,在进行字符串操作的时候丝毫不受影响。
3、反过来,假如说compare函数中,我们将void * 强制转化为char **,那可就不对了,a是一个char * [10]类型的指针,换个角度考虑,这尼玛不就是一个一维指针嘛,只不过指针指向的数据(char [10])比较大而已,那如果我们把a强转化为char **,程序的第40行就应该写为strlen(*a)(即compare函数改成和程序1一样),那这就是把a指向的数据当作地址,去计算以a指向的数据为首地址的字符串的大小,这个,立刻就段错误了,因为以a指向的数据为地址的那段内存单元不是程序分配的,也不是我们开辟的,如果我们去操作它,那就是段错误了!
4、程序输出为:

该字符数组中有
enter number size begin of cat case program certain a

排序后的字符数组为
a of cat case size begin enter number certain program

2、 qsort总结

1、如果传递的是二维数据名字,则在compare函数中需要将void*强制转化为char *
2、如果传递的是二维指针,则在compare函数中直接将void*转化为 char **即可;
例:

int compare(const void*elem1,const void *elem2){
    return(strcmp((char*)elem1,(char*)elem2));
}
//str为char类型二维数组如str[1000][9], 那么,size是字符串的数目(1000), len是字符串的最长长度(9)
qsort(str,size,len,compare);

通过场面的讨论得知,通常操作字符数组时用qsort比较多,
而操作string数组时用sort比较多。[3]

3、把数组排成最小的数参考程序

class Solution {
public:
    string PrintMinNumber(vector<int> numbers) {
        string ret;
        if(numbers.empty())
            return ret;

        vector<string> p;
        for(int i = 0; i < numbers.size(); i++){ //将vector转化为vector
            p.push_back(to_string(numbers[i]));
        }

        sort(p.begin(), p.end(), cmp); //将p排序,排序的规则有cmp函数定义;

        for(int i = 0; i < numbers.size(); i++){
            ret += p[i];
        }  
        return ret;

    }

    static bool cmp(string a, string b){ //sort函数的第三个参数,即排序规则的定义;
        string aa = a + b;
        string bb = b + a;
        return aa < bb;
    }
};

参考
[1] 深入理解C/C++数组和指针
[2] 关于二级指针与二维数组之间的赋值?
[3] STL排序函数sort和qsort的用法与区别

你可能感兴趣的:(C/C++编程语言)