chapter_sorting/radix_sort/ #442
Replies: 19 comments 20 replies
-
个数转索引 巧妙的方法啊 |
Beta Was this translation helpful? Give feedback.
-
c++语言 |
Beta Was this translation helpful? Give feedback.
-
//计数排序正序尝试
void countingSortDigit(vector<int> &nums, int exp) {
// 十进制的位范围为 0~9 ,因此需要长度为 10 的桶
vector<int> counter(10, 0);
int n = nums.size();
// 统计 0~9 各数字的出现次数
for (int i = 0; i < n; i++) {
int d = digit(nums[i], exp); // 获取 nums[i] 第 k 位,记为 d
counter[d]++; // 统计数字 d 的出现次数
}
// 求前缀和,将“出现个数“转换为第一次出现的位置
vector<int>profile(10,0);
for (int i = 1; i < 10; i++) {
profile[i] += counter[i-1]+profile[i-1];
}
// 正序遍历,根据桶内统计结果,将各元素填入 res
vector<int> res(n, 0);
for (int i = 0; i < n; i++) {
int d = digit(nums[i], exp);
int j = profile[d]; // 获取 d 在数组中的索引 j
res[j] = nums[i]; // 将当前元素填入索引 j
profile[d]++; // 将 d 的数量减 1
}
// 使用结果覆盖原数组 nums
for (int i = 0; i < n; i++)
nums[i] = res[i];
} 我这样正序排列好像也可以,有点不明白为什么不能用正序遍历 |
Beta Was this translation helpful? Give feedback.
-
该书的作者,你们好! |
Beta Was this translation helpful? Give feedback.
-
// 统计 0~9 各数字的出现次数
for (int i = 0; i < n; i++) {
int d = digit(nums[i], exp); // 获取 nums[i] 第 k 位,记为 d
counter[d]++; // 统计数字 d 的出现次数
}
// 求前缀和,将“出现个数”转换为“数组索引”
for (int i = 1; i < 10; i++) {
counter[i] += counter[i - 1];
} 核心在【个数】转【数组索引】这样理解可能更好: |
Beta Was this translation helpful? Give feedback.
-
def radix_sort(nums):
n=len(str(max(nums)))
for i in range(n):
bucket_list=[[] for _ in range(10)]
for j in nums:
idx=j//pow(10,i)%10
bucket_list[idx].append(j)
nums=[j for i in bucket_list for j in i]
return nums 可以这样来实现 更舒服一点 |
Beta Was this translation helpful? Give feedback.
-
汇总一一下下 # 借助桶排序
def radix_sort(nums):
# 计算数字的最大位数
n = len(str(max(nums)))
# 对每一位进行排序
for i in range(n):
# 创建桶列表
bucket_list = [[] for _ in range(10)]
# 将数字放入对应的桶中
for j in nums:
idx = j // pow(10, i) % 10
bucket_list[idx].append(j)
# 合并桶中的数字
nums = [j for bucket in bucket_list for j in bucket]
return nums
# 借用计数排序即可
def radix_sort1(nums):
# 计算数字的最大位数
n = len(str(max(nums)))
# 对每一位进行计数排序
for i in range(n):
count_sort(nums, i)
return nums
# 计数排序
def count_sort(nums, exp):
cnt = [0] * 10
# 统计数字出现的次数
for num in nums:
idx = num // pow(10, exp) % 10
cnt[idx] += 1
# 计算前缀和
for i in range(1, 10):
cnt[i] += cnt[i - 1]
n = len(nums)
ans = [0] * n
# 逆序遍历排序数组,保证稳定性
for i in range(n - 1, -1, -1):
idx = nums[i] // pow(10, exp) % 10
ans[cnt[idx] - 1] = nums[i]
cnt[idx] -= 1
# 将排序后的数组复制回原始数组
for i in range(n):
nums[i] = ans[i] |
Beta Was this translation helpful? Give feedback.
-
”假设我们需要对 n=10^6个学号进行排序,而学号是一个 8位数字”,对这么多的学号进行排序,博主没考虑到“res = [0] * n”这行代码的数组res会变得异常的大么? |
Beta Was this translation helpful? Give feedback.
-
不懂就问,C代码中/* 基数排序 */里的第一个for循环为啥要使用size_t类型捏?它和Int型有啥区别吗? |
Beta Was this translation helpful? Give feedback.
-
"相较于计数排序,基数排序适用于数值范围较大的情况,但前提是数据必须可以表示为固定位数的格式,且位数不能过大。" 似乎非固定位数不影响基数排序的正确性啊,比如数组 {546151, 35663510, 42865989, 34862445, 81883077, 88906420, 72429244, 30524779, 82060337, 2996} 位数不相同,但是依然可以正确排序。 |
Beta Was this translation helpful? Give feedback.
-
我的理解:基数排序事实上就是计数排序实际应用的一种情况,将多位数作为一个对象,从低位往高遍历的数字依次作为排序值,牵动多位数(对象)进行排序 |
Beta Was this translation helpful? Give feedback.
-
void Radixsorting(std::vector<int> &v, int K)
{
// 十进制,最大元素为9
int m = 9;
// temp1创建并建立数量映射
std::vector<int> temp1(m + 1, 0);
for (const auto &it : v)
{
// 按要排的第几位映射为索引
temp1[(it / K) % 10]++;
}
// 变成前缀和
for (int i = 0; i <= m; ++i)
{
temp1[i + 1] += temp1[i];
}
int l = v.size();
std::vector<int> temp2(l);
for (int i = l - 1; i >= 0; i--) // 由于前缀和为保证稳定排序需倒序填充
{
int n = (v[i] / K) % 10; // 只看要排那一位
temp2[temp1[n] - 1] = v[i]; // 根据前缀和索引到对应位置
--temp1[n]; // 前缀和更新为向前一个位置的索引
}
// temp2覆盖v
v = temp2;
} 请问一下,这段哪有问题啊? |
Beta Was this translation helpful? Give feedback.
-
NOTE:1.对数位做倒序排序保证算法有效性;2.构造 |
Beta Was this translation helpful? Give feedback.
-
/* 计数排序(根据 nums 第 k 位排序) */ 这步不需要free(res)吗 记得malloc的不用都要free的吧 |
Beta Was this translation helpful? Give feedback.
-
可不可以优先排序高位,然后将高位一样的元素分到同一个桶里继续排序里面的低位,这样的话在某个高位排序完成且不相等的情况下那个桶就完成排序了,而不需要继续排序低位了。从低位排序则必须要排完所有位才能保证结果。 |
Beta Was this translation helpful? Give feedback.
-
完整的计数排序C代码 void get_kth_digit(int x, int k, int *digit){ *digit = (x / (int)pow(10, k)) % 10; } // 获取数字x的第k位数字
void counting_sort_digit(int *arr, int size, int k){
int *counter = (int *) calloc(10, sizeof(int)), *res = (int *)malloc(sizeof(int) * size); // 计数数组(最终存储前缀和);结果数组
for (int i = 0; i < size; ++i) { int digit; get_kth_digit(arr[i], k, &digit); counter[digit]++; } // 累计计数
for (int i = 1; i < 10; ++i) { counter[i] += counter[i - 1]; } // 计算前缀和
for (int i = size - 1; i >= 0 ; --i) { int digit; get_kth_digit(arr[i], k, &digit); res[counter[digit] - 1] = arr[i]; counter[digit]--; } // 从后往前遍历,根据前缀和确定元素在结果数组中的位置
memcpy(arr, res, sizeof(int) * size); free(counter); free(res);
}
void radix_sort(int *arr, int size){
if (size <= 0) return; int max_val = arr[0], max_digit = 0;
for (int i = 1; i < size; i++) { if (arr[i] > max_val) max_val = arr[i]; } // 找到最大值,用于确定计数数组大小[0, max_val]
while (max_val > 0) { max_val /= 10; max_digit++; } // 计算最大值的位数
for (int i = 0; i < max_digit; i++) { counting_sort_digit(arr, size, i); } // 从低位到高位,依次对每一位进行计数排序
} |
Beta Was this translation helpful? Give feedback.
-
import random
def sort(nums):
max_bit = max(nums)
base = 1
while max_bit:
buckets = [[] for _ in range(10)]
new_nums = []
for num in nums:
buckets[num // base % 10].append(num)
for bucket in buckets:
new_nums.extend(bucket)
nums = new_nums
base *= 10
max_bit -= 1
return nums
nums = [random.randrange(0, 10000) for _ in range(10)]
print(nums)
print(sort(nums))
|
Beta Was this translation helpful? Give feedback.
-
K神,基数排序的核心思想是从低位对高位每一个位进行排序,从而得到一个有序的数列,我想知道如果每一位排序的算法不用计数排序,而用其他非稳定的排序方法,这样基数排序还有效吗? |
Beta Was this translation helpful? Give feedback.
-
C版本的countingSortDigit第一句应该用calloc |
Beta Was this translation helpful? Give feedback.
-
chapter_sorting/radix_sort/
一本动画图解、能运行、可提问的数据结构与算法入门书
https://www.hello-algo.com/chapter_sorting/radix_sort/
Beta Was this translation helpful? Give feedback.
All reactions