题目描述:
有 n 位用户参加活动,他们的 ID 从 0 到 n - 1,每位用户都 恰好 属于某一用户组。给你一个长度为 n 的数组 groupSizes,其中包含每位用户所处的用户组的大小,请你返回用户分组情况(存在的用户组以及每个组中用户的 ID)。
你可以任何顺序返回解决方案,ID 的顺序也不受限制。此外,题目给出的数据保证至少存在一种解决方案。
示例 1:
输入:groupSizes = [3,3,3,3,3,1,3]
输出:[[5],[0,1,2],[3,4,6]]
解释:
其他可能的解决方案有 [[2,1,6],[5],[0,4,3]] 和 [[5],[0,6,2],[4,3,1]]。
示例 2:
输入:groupSizes = [2,1,3,3,3,2]
输出:[[1],[0,5],[2,3,4]]
提示:
groupSizes.length == n
1 <= n <= 500
1 <= groupSizes[i] <= n
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/group-the-people-given-the-group-size-they-belong-to
解题思路:
- 一开始是冲着贪心的标签做这题的,不知道是我太菜了还是怎么回事,觉得这题和贪心似乎关系不大?
- 数据数量有限,就直接做分类了,对所有的“所处用户组数量大小相同”的数据,记录其下标,并且分别记录每个用户组数量下的数据总数(有点绕口,比如说某几个数据,其所处用户组大小为3,记录这几个数据的总数),之后就对做好分类的数据做处理就可以了;
- 把已经分好类的数据以此填充到返回数组中即可。
个人疑虑:
- 一开始没有用题目给出的最大范围来开辟数组空间,想节省内存。在数据分类、将分类完的数据填充到返回数组时,使用了大量的realloc。使用原因:如果直接用最大范围开辟数组空间,会有一部分的空间没有被使用到,造成空间浪费,就想着确定要用了,然后再去开辟,结果时间和空间耗用都不尽如人意。
下面的第一段代码空间占用大概在11.7M,时间从40~50ms都有,第二段代码的代码空间占用达到了24.5M,时间达到了64ms,具体原因和realloc的机制应该有关系,解决之后再来填补。
代码一,时间、内存都还算可以的代码
int** groupThePeople(int* groupSizes, int groupSizesSize, int* returnSize, int** returnColumnSizes)
{
int group_array[500][500] = { 0 }; //第一维:用户组长度
//第二维:
int *group_length = (int*)calloc(groupSizesSize, sizeof(int));
short i;
short k = 0;
for (i = 0; i < groupSizesSize; i++)
{
k = groupSizes[i] - 1; //
if (k < 0 || k >= 500)
continue;
group_array[k][group_length[k]] = i; //将当前下标数据存到相应数组
group_length[k]++; //当前长度的用户组数量+1
}
int** ret_res = (int*)malloc(sizeof(int*)*groupSizesSize); //暂时所有长度,包含无用长度
(*returnColumnSizes) = (int*)malloc(sizeof(int)*groupSizesSize);
*returnSize = 0; //清零
int n;
for (i = 0; i < groupSizesSize; i++) //所有用户组长度进行遍历
{
n = 0; //
while (group_length[i] > 0) //当前数组长度仍然大于0,即没有全部分类完成
{
(*returnColumnSizes)[*returnSize] = i + 1; //指示当前一维数组的长度
ret_res[*returnSize] = (int*)malloc(sizeof(int)*(i + 1)); //当前数组有i+1个int数据
for (k = 0; k <= i; k++) //i 是用户组数-1 所以要加上等号
{
ret_res[*returnSize][k] = group_array[i][n];
n++;
group_length[i]--;
}
*returnSize += 1;
}
}
return ret_res;
}
第二段代码,时间、内存占用都很高的代码
int** groupThePeople(int* groupSizes, int groupSizesSize, int* returnSize, int** returnColumnSizes)
{
int group_array[500][500] = { 0 }; //第一维:用户组长度
//第二维:
int *group_length = (int*)calloc(groupSizesSize, sizeof(int));
short i;
short k = 0;
for (i = 0; i < groupSizesSize; i++)
{
k = groupSizes[i] - 1; //
if (k < 0 || k >= 500)
continue;
group_array[k][group_length[k]] = i; //将当前下标数据存到相应数组
group_length[k]++; //当前长度的用户组数量+1
}
int** ret_res = NULL;
(*returnColumnSizes) = (int*)malloc(sizeof(int)*groupSizesSize);
*returnSize = 0; //清零
int n;
for (i = 0; i < groupSizesSize; i++) //所有用户组长度进行遍历
{
n = 0; //
while (group_length[i] > 0) //当前数组长度仍然大于0,即没有全部分类完成
{
//(*returnColumnSizes) = (int*)realloc(*returnColumnSizes, sizeof(int)*(*returnSize + 1));
(*returnColumnSizes)[*returnSize] = i + 1; //指示当前一维数组的长度
//需要的时候再把返回数组延长
ret_res = (int*)realloc(ret_res, sizeof(int*)*(*returnSize + 1));
ret_res[*returnSize] = (int*)malloc(sizeof(int)*(i + 1)); //当前数组有i+1个int数据
for (k = 0; k <= i; k++) //i 是用户组数-1 所以要加上等号
{
ret_res[*returnSize][k] = group_array[i][n];
n++;
group_length[i]--;
}
*returnSize += 1;
}
}
return ret_res;
}
以下属于菜鸟的知识点总结,是知识漏洞,之前没有细看过。
- 之前没有在实际项目中使用二级指针做形参过,这个函数中形参int** returnColumnSizes,一开始以为是传了一个二维数组进去,我在VS中直接int** return_column_sizes,然后调用函数的时候报错。
后来才搞明白其实就是传了个一维数组进去,只不过传的不是一维数组的起始地址,而是传了一个指向这个地址的指针。
举个例子,定义了一个数组 int array[100],如果直接使用 array,array的值就是array[100]这个数组的起始地址;&array,就是array的地址,这个地址中存的数据是array[100]这个数组的起始地址。
函数中,int** returnColumnSizes 这个形参的位置,就应该放 &array,那么函数中 *returnColumnSizes = array,两者都是数组的起始地址,(*returnColumnSizes)[10] = array[10],两者都是这个数组的第十个元素(此处=符号仅代表等价)。