chapter_sorting/counting_sort/ #428
Replies: 26 comments 38 replies
-
在完整实现中,可排序对象,但代码里却传入的是 []int,一开始理解没转过来。 |
Beta Was this translation helpful? Give feedback.
-
在C语言的完整实现中进行第一次分配的代码是 |
Beta Was this translation helpful? Give feedback.
-
这里的完整实现我没怎么明白具体 “好” 在哪,感觉就是最后可以保持同一个成员属性值的多个对象的相对位置不会发生变化(即原来它们的前后位置是怎样的,排序后它们的顺序依然是那样),其他就没看出什么 “优势” 了,简单实现也是能对对象排序的吧?只是无法对同一属性值的对象做区分? |
Beta Was this translation helpful? Give feedback.
-
为什么简单计数不稳定呢,获得了价格排序再和商品对照一下不就可以获得对应商品排序了,也不会改变相对位置,是我理解错了吗
|
Beta Was this translation helpful? Give feedback.
-
这里的稳定基数排序只展示了思想,像更清楚的体会稳定性看基数排序里面的使用 |
Beta Was this translation helpful? Give feedback.
-
细心的同学可能发现,如果输入数据是对象,上述步骤 3. 就失效了。假设输入数据是商品对象,我们想要按照商品价格(类的成员变量)对商品进行排序,而上述算法只能给出价格的排序结果. |
Beta Was this translation helpful? Give feedback.
-
还是不太能理解“实际上,正序遍历 nums 也可以得到正确的排序结果,但结果是非稳定的。” |
Beta Was this translation helpful? Give feedback.
-
(java)完整实现的这一步是为了计算相同值的下一次放置的索引吗:counter[num]--; // 令前缀和自减 1 ,得到下次放置 num 的索引 |
Beta Was this translation helpful? Give feedback.
-
完整实现和简单实现的区别是什么?有人概括一下吗? |
Beta Was this translation helpful? Give feedback.
-
res[counter[num] - 1] = num; // 将 num 放置到对应索引处
counter[num]--; // 令前缀和自减 1 ,得到下次放置 num 的索引 这里是不是可以改成 |
Beta Was this translation helpful? Give feedback.
-
话说C语言那个counter数组 calloc(m, sizeof(int)) 是不是越界了 之后不是要更新counter[nums[i]] (包含m) 的值吗 这个是越界吗 |
Beta Was this translation helpful? Give feedback.
-
计数排序有个关于“计数器数组长度”的问题,比如对100万个199~999的数字进行排序,计数器数组counter是一个长度为999的数组,前面198个长度并没有使用,是浪费掉的,因此为了节省内存,计数器数组应该改成counter=max(nums)-min(nums)+1,这样更节省内存 |
Beta Was this translation helpful? Give feedback.
-
哈希函数跟计数排序有关联吗 |
Beta Was this translation helpful? Give feedback.
-
public static void countingSortPro(int[] nums){
//假定输入一个商品数组 ItemArray
//由此得到其成员变量价格 的数组 PriceArray
//对PriceArray中的元素进行计数排序得到prefix-->元素插入位置
//再遍历ItemArray得到 满足价格排序的 商品数组 且是稳定排序
int max=nums[0];
for(int i=1;i< nums.length;i++){
max=nums[i]>max?nums[i] : max;
}
int[] counter = new int[max + 1];
//得到counter数组
for(int i=0;i< nums.length;i++){
counter[nums[i]]++;
}
//得到prefix数组
for(int i=0;i< counter.length;i++){
counter[i+1]+=counter[i];
}
//创建存储结果的数组res
//Item[] res;
int[] res = new int[nums.length];
//关键步骤
for(int i= nums.length-1;i>=0;i--){
int num=nums[i];
//res[counter[num]-1]=ItemArray[i]
res[counter[num]-1]=nums[i];//counter[num]得到该数据对应的prefix
counter[num]-=1;
}
//使用结果数组res覆盖原数组nums
for(int i=0;i< nums.length;i++){
nums[i]=res[i];
}
} 希望帮助大家辅助理解一下完整实现的意义。priceArray中的价格元素和itemArray中的商品对象相对应,后面记录结果的时候直接从itemArray中提取元素就可以了。 |
Beta Was this translation helpful? Give feedback.
-
public class User{
int cost;
int age;
}
void countingSortNaive(User[] nums) {
int m = 0;
for(User user : nums){
if(m>user.age) m = user.ages;
}
int[]ans = new int[m+1];
for(User user:nums){
int num = user.age;
ans[num]++;//ans[3] = 4,表示年龄为3的对象有4个
}
//计算前缀合
int[]pre = new int[m+1]
for(int i=0;i<m;i++){
pre[i+1] += pre[i]; //pre[3] = 10,表示年龄为3的最后一个对象的索引为10-1=9
}
User[]res = new User[n];
for(i=nums.length-1;i>=0;i--){//从数组最后一位开始遍历可以保证元素的稳定性,即前后位置不变
//如果从i=0开始的话,相同元素的相互位置可能会改变
User user = nums[i];
index = user.age;//这个对象的年龄就是这个对象在ans数组的索引,也是在pre数组的索引
res[pre[index]-1] = user;
pre[index]--;
}
//将res拷贝回去nums;
for(int i=0;i<nums.length;i++){
nums[i] = res[i];
}
}
//之前发的版本User[]res = new User[m+1];不对,应该大小为n的对象数组 |
Beta Was this translation helpful? Give feedback.
-
感谢 K 神,当初看《算法 4th》计数排序虽然只占了很小几页,但一上来就是完整实现,反复看了很多遍都没能理解。。。现在看这篇弄清了,真可谓666也(原作者水平高内容立点也高,虽然也写得很清晰明了但看起来还是比这有难度多了) |
Beta Was this translation helpful? Give feedback.
-
算法特性中,为什么没有自适应性了呢? |
Beta Was this translation helpful? Give feedback.
-
看这部教程有一种当初看完交大郑益慧老师《模拟电子技术》课程的感觉,真的惊叹作者水平之高,通篇竟无一句废话 |
Beta Was this translation helpful? Give feedback.
-
我看大家提出疑问最多的是:为何简单实现不能排序对象? 简单实现用索引映射了nums数组的值,数组索引只能是int类型,所以不能对对象进行排序,硬排序的结果只能是针对对象中的int类型的成员变量。而且是不稳定的。不稳定体现在原nums如果是 完整实现采用了前缀和数组记录每一个元素应该在的位置,倒序遍历nums之后元素会严格按照相对放置填入,保证了稳定性,也兼容了对象的排序,更具有普适性 |
Beta Was this translation helpful? Give feedback.
-
创建一个新的数组,新的数组的值即为当前索引在原数组中出现的次数,提取出新数组中的非零元素的索引序列,即为原数组的排序结果。 |
Beta Was this translation helpful? Give feedback.
-
计数排序的时候最好把最小元素也先确定,这样只需要创建一个容量为 maxmimuu - minimum + 1 的计数数组 counter,counter[num - minimum] 表示元素 num 在待排序数组中出现的次数,这样既可以减小空间复杂度也可以直接推广到存在负整数的数组 |
Beta Was this translation helpful? Give feedback.
-
C语言实现计数排序完整版 void counting_sort(int *arr, int size){
if (size <= 0) return; int max_val = arr[0]; for (int i = 1; i < size; i++) { if (arr[i] > max_val) max_val = arr[i]; } // 找到最大值,用于确定计数数组大小[0, max_val]
int *counter = (int *) calloc(max_val + 1, sizeof(int)), *res = (int *)malloc(sizeof(int) * size); // 计数数组(最终存储前缀和);结果数组
for (int i = 0; i < size; i++) { counter[arr[i]]++; } for (int i = 1; i <= max_val; ++i) { counter[i] += counter[i - 1]; } // 计数和计算前缀和
for (int i = size - 1; i >= 0; i--) { int cur_val = arr[i]; res[counter[cur_val] - 1] = cur_val; counter[cur_val]--; } // 从后往前遍历原数组,通过前缀和找到元素在结果数组中的位置,存入后,前缀和计数减1
memcpy(arr, res, sizeof(int) * size); free(counter); free(res);
} 有个小问题,为什么作者给出的完整代码(第二部分)中counter数组的大小只需要m而不是m+1了呢? |
Beta Was this translation helpful? Give feedback.
-
def sort(nums):
cnt_array = [0] * (max(nums) + 1)
new_nums = []
for num in nums:
cnt_array[num] += 1
for idx, num in enumerate(cnt_array):
new_nums.extend([idx] * num)
return new_nums
nums = [1,3,32,4,1,23,17,23,6,2,9]
print(sort(nums)) |
Beta Was this translation helpful? Give feedback.
-
最好能补充一个用完整实现排序对象数组的例子 |
Beta Was this translation helpful? Give feedback.
-
AI 给出的前缀和式的计数排序,并通过商品数量给商品名称排序的示例:
一句话总结: |
Beta Was this translation helpful? Give feedback.
-
C 也可利用前缀运算符简化利用前缀和计算出索引并回填出的代码: for (i = n - 1; i >= 0; i--) {
val = arr[i];
ret[--count[val]] = val;
} |
Beta Was this translation helpful? Give feedback.
-
chapter_sorting/counting_sort/
一本动画图解、能运行、可提问的数据结构与算法入门书
https://www.hello-algo.com/chapter_sorting/counting_sort/
Beta Was this translation helpful? Give feedback.
All reactions