今天在刷题时,题目:
输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。
遇到了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
#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
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]
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的用法与区别