diff --git a/codes/javascript/chapter_sorting/bucket_sort.js b/codes/javascript/chapter_sorting/bucket_sort.js new file mode 100644 index 0000000000..e2242df073 --- /dev/null +++ b/codes/javascript/chapter_sorting/bucket_sort.js @@ -0,0 +1,39 @@ +/** + * File: bucket_sort.js + * Created Time: 2023-04-08 + * Author: Justin (xiefahit@gmail.com) + */ + +/* 桶排序 */ +function bucketSort(nums) { + // 初始化 k = n/2 个桶,预期向每个桶分配 2 个元素 + const k = nums.length / 2; + const buckets = []; + for (let i = 0; i < k; i++) { + buckets.push([]); + } + // 1. 将数组元素分配到各个桶中 + for (const num of nums) { + // 输入数据范围 [0, 1),使用 num * k 映射到索引范围 [0, k-1] + const i = Math.floor(num * k); + // 将 num 添加进桶 i + buckets[i].push(num); + } + // 2. 对各个桶执行排序 + for (const bucket of buckets) { + // 使用内置排序函数,也可以替换成其它排序算法 + bucket.sort((a, b) => a - b); + } + // 3. 遍历桶合并结果 + let i = 0; + for (const bucket of buckets) { + for (const num of bucket) { + nums[i++] = num; + } + } +} + +/* Driver Code */ +const nums = [0.49, 0.96, 0.82, 0.09, 0.57, 0.43, 0.91, 0.75, 0.15, 0.37]; +bucketSort(nums); +console.log('桶排序完成后 nums =', nums); diff --git a/codes/javascript/chapter_sorting/counting_sort.js b/codes/javascript/chapter_sorting/counting_sort.js new file mode 100644 index 0000000000..53f2faf00d --- /dev/null +++ b/codes/javascript/chapter_sorting/counting_sort.js @@ -0,0 +1,71 @@ +/** + * File: coutning_sort.js + * Created Time: 2023-04-08 + * Author: Justin (xiefahit@gmail.com) + */ + +/* 计数排序 */ +// 简单实现,无法用于排序对象 +function countingSortNaive(nums) { + // 1. 统计数组最大元素 m + let m = 0; + for (const num of nums) { + m = Math.max(m, num); + } + // 2. 统计各数字的出现次数 + // counter[num] 代表 num 的出现次数 + const counter = new Array(m + 1).fill(0); + for (const num of nums) { + counter[num]++; + } + // 3. 遍历 counter ,将各元素填入原数组 nums + let i = 0; + for (let num = 0; num < m + 1; num++) { + for (let j = 0; j < counter[num]; j++, i++) { + nums[i] = num; + } + } +} + +/* 计数排序 */ +// 完整实现,可排序对象,并且是稳定排序 +function countingSort(nums) { + // 1. 统计数组最大元素 m + let m = 0; + for (const num of nums) { + m = Math.max(m, num); + } + // 2. 统计各数字的出现次数 + // counter[num] 代表 num 的出现次数 + const counter = new Array(m + 1).fill(0); + for (const num of nums) { + counter[num]++; + } + // 3. 求 counter 的前缀和,将“出现次数”转换为“尾索引” + // 即 counter[num]-1 是 num 在 res 中最后一次出现的索引 + for (let i = 0; i < m; i++) { + counter[i + 1] += counter[i]; + } + // 4. 倒序遍历 nums ,将各元素填入结果数组 res + // 初始化数组 res 用于记录结果 + const n = nums.length; + const res = new Array(n); + for (let i = n - 1; i >= 0; i--) { + const num = nums[i]; + res[counter[num] - 1] = num; // 将 num 放置到对应索引处 + counter[num]--; // 令前缀和自减 1 ,得到下次放置 num 的索引 + } + // 使用结果数组 res 覆盖原数组 nums + for (let i = 0; i < n; i++) { + nums[i] = res[i]; + } +} + +/* Driver Code */ +const nums = [1, 0, 1, 2, 0, 4, 0, 2, 2, 4]; +countingSortNaive(nums); +console.log('计数排序(无法排序对象)完成后 nums =', nums); + +const nums1 = [1, 0, 1, 2, 0, 4, 0, 2, 2, 4]; +countingSort(nums1); +console.log('计数排序完成后 nums1 =', nums1); diff --git a/codes/javascript/chapter_sorting/radix_sort.js b/codes/javascript/chapter_sorting/radix_sort.js new file mode 100644 index 0000000000..948a60b13a --- /dev/null +++ b/codes/javascript/chapter_sorting/radix_sort.js @@ -0,0 +1,64 @@ +/** + * File: radix_sort.js + * Created Time: 2023-04-08 + * Author: Justin (xiefahit@gmail.com) + */ + +/* 获取元素 num 的第 k 位,其中 exp = 10^(k-1) */ +function digit(num, exp) { + // 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算 + return Math.floor(num / exp) % 10; +} + +/* 计数排序(根据 nums 第 k 位排序) */ +function countingSortDigit(nums, exp) { + // 十进制的位范围为 0~9 ,因此需要长度为 10 的桶 + const counter = new Array(10).fill(0); + const n = nums.length; + // 统计 0~9 各数字的出现次数 + for (let i = 0; i < n; i++) { + const d = digit(nums[i], exp); // 获取 nums[i] 第 k 位,记为 d + counter[d]++; // 统计数字 d 的出现次数 + } + // 求前缀和,将“出现个数”转换为“数组索引” + for (let i = 1; i < 10; i++) { + counter[i] += counter[i - 1]; + } + // 倒序遍历,根据桶内统计结果,将各元素填入 res + const res = new Array(n).fill(0); + for (let i = n - 1; i >= 0; i--) { + const d = digit(nums[i], exp); + const j = counter[d] - 1; // 获取 d 在数组中的索引 j + res[j] = nums[i]; // 将当前元素填入索引 j + counter[d]--; // 将 d 的数量减 1 + } + // 使用结果覆盖原数组 nums + for (let i = 0; i < n; i++) { + nums[i] = res[i]; + } +} + +/* 基数排序 */ +function radixSort(nums) { + // 获取数组的最大元素,用于判断最大位数 + let m = Number.MIN_VALUE; + for (const num of nums) { + if (num > m) { + m = num; + } + } + // 按照从低位到高位的顺序遍历 + for (let exp = 1; exp <= m; exp *= 10) { + // 对数组元素的第 k 位执行计数排序 + // k = 1 -> exp = 1 + // k = 2 -> exp = 10 + // 即 exp = 10^(k-1) + countingSortDigit(nums, exp); + } +} + +/* Driver Code */ +const nums = [10546151, 35663510, 42865989, 34862445, 81883077, + 88906420, 72429244, 30524779, 82060337, 63832996]; +radixSort(nums); +console.log('基数排序完成后 nums =', nums); diff --git a/codes/typescript/chapter_sorting/bucket_sort.ts b/codes/typescript/chapter_sorting/bucket_sort.ts new file mode 100644 index 0000000000..3aeb4dc41d --- /dev/null +++ b/codes/typescript/chapter_sorting/bucket_sort.ts @@ -0,0 +1,41 @@ +/** + * File: bucket_sort.ts + * Created Time: 2023-04-08 + * Author: Justin (xiefahit@gmail.com) + */ + +/* 桶排序 */ +function bucketSort(nums: number[]): void { + // 初始化 k = n/2 个桶,预期向每个桶分配 2 个元素 + const k = nums.length / 2; + const buckets: number[][] = []; + for (let i = 0; i < k; i++) { + buckets.push([]); + } + // 1. 将数组元素分配到各个桶中 + for (const num of nums) { + // 输入数据范围 [0, 1),使用 num * k 映射到索引范围 [0, k-1] + const i = Math.floor(num * k); + // 将 num 添加进桶 i + buckets[i].push(num); + } + // 2. 对各个桶执行排序 + for (const bucket of buckets) { + // 使用内置排序函数,也可以替换成其它排序算法 + bucket.sort((a, b) => a - b); + } + // 3. 遍历桶合并结果 + let i = 0; + for (const bucket of buckets) { + for (const num of bucket) { + nums[i++] = num; + } + } +} + +/* Driver Code */ +const nums = [0.49, 0.96, 0.82, 0.09, 0.57, 0.43, 0.91, 0.75, 0.15, 0.37]; +bucketSort(nums); +console.log('桶排序完成后 nums =', nums); + +export {}; diff --git a/codes/typescript/chapter_sorting/counting_sort.ts b/codes/typescript/chapter_sorting/counting_sort.ts new file mode 100644 index 0000000000..99ac3297ad --- /dev/null +++ b/codes/typescript/chapter_sorting/counting_sort.ts @@ -0,0 +1,73 @@ +/** + * File: coutning_sort.ts + * Created Time: 2023-04-08 + * Author: Justin (xiefahit@gmail.com) + */ + +/* 计数排序 */ +// 简单实现,无法用于排序对象 +function countingSortNaive(nums: number[]): void { + // 1. 统计数组最大元素 m + let m = 0; + for (const num of nums) { + m = Math.max(m, num); + } + // 2. 统计各数字的出现次数 + // counter[num] 代表 num 的出现次数 + const counter: number[] = new Array(m + 1).fill(0); + for (const num of nums) { + counter[num]++; + } + // 3. 遍历 counter ,将各元素填入原数组 nums + let i = 0; + for (let num = 0; num < m + 1; num++) { + for (let j = 0; j < counter[num]; j++, i++) { + nums[i] = num; + } + } +} + +/* 计数排序 */ +// 完整实现,可排序对象,并且是稳定排序 +function countingSort(nums: number[]): void { + // 1. 统计数组最大元素 m + let m = 0; + for (const num of nums) { + m = Math.max(m, num); + } + // 2. 统计各数字的出现次数 + // counter[num] 代表 num 的出现次数 + const counter: number[] = new Array(m + 1).fill(0); + for (const num of nums) { + counter[num]++; + } + // 3. 求 counter 的前缀和,将“出现次数”转换为“尾索引” + // 即 counter[num]-1 是 num 在 res 中最后一次出现的索引 + for (let i = 0; i < m; i++) { + counter[i + 1] += counter[i]; + } + // 4. 倒序遍历 nums ,将各元素填入结果数组 res + // 初始化数组 res 用于记录结果 + const n = nums.length; + const res: number[] = new Array(n); + for (let i = n - 1; i >= 0; i--) { + const num = nums[i]; + res[counter[num] - 1] = num; // 将 num 放置到对应索引处 + counter[num]--; // 令前缀和自减 1 ,得到下次放置 num 的索引 + } + // 使用结果数组 res 覆盖原数组 nums + for (let i = 0; i < n; i++) { + nums[i] = res[i]; + } +} + +/* Driver Code */ +const nums = [1, 0, 1, 2, 0, 4, 0, 2, 2, 4]; +countingSortNaive(nums); +console.log('计数排序(无法排序对象)完成后 nums =', nums); + +const nums1 = [1, 0, 1, 2, 0, 4, 0, 2, 2, 4]; +countingSort(nums1); +console.log('计数排序完成后 nums1 =', nums1); + +export {}; diff --git a/codes/typescript/chapter_sorting/radix_sort.ts b/codes/typescript/chapter_sorting/radix_sort.ts new file mode 100644 index 0000000000..152f023861 --- /dev/null +++ b/codes/typescript/chapter_sorting/radix_sort.ts @@ -0,0 +1,66 @@ +/** + * File: radix_sort.ts + * Created Time: 2023-04-08 + * Author: Justin (xiefahit@gmail.com) + */ + +/* 获取元素 num 的第 k 位,其中 exp = 10^(k-1) */ +function digit(num: number, exp: number): number { + // 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算 + return Math.floor(num / exp) % 10; +} + +/* 计数排序(根据 nums 第 k 位排序) */ +function countingSortDigit(nums: number[], exp: number): void { + // 十进制的位范围为 0~9 ,因此需要长度为 10 的桶 + const counter = new Array(10).fill(0); + const n = nums.length; + // 统计 0~9 各数字的出现次数 + for (let i = 0; i < n; i++) { + const d = digit(nums[i], exp); // 获取 nums[i] 第 k 位,记为 d + counter[d]++; // 统计数字 d 的出现次数 + } + // 求前缀和,将“出现个数”转换为“数组索引” + for (let i = 1; i < 10; i++) { + counter[i] += counter[i - 1]; + } + // 倒序遍历,根据桶内统计结果,将各元素填入 res + const res = new Array(n).fill(0); + for (let i = n - 1; i >= 0; i--) { + const d = digit(nums[i], exp); + const j = counter[d] - 1; // 获取 d 在数组中的索引 j + res[j] = nums[i]; // 将当前元素填入索引 j + counter[d]--; // 将 d 的数量减 1 + } + // 使用结果覆盖原数组 nums + for (let i = 0; i < n; i++) { + nums[i] = res[i]; + } +} + +/* 基数排序 */ +function radixSort(nums: number[]): void { + // 获取数组的最大元素,用于判断最大位数 + let m = Number.MIN_VALUE; + for (const num of nums) { + if (num > m) { + m = num; + } + } + // 按照从低位到高位的顺序遍历 + for (let exp = 1; exp <= m; exp *= 10) { + // 对数组元素的第 k 位执行计数排序 + // k = 1 -> exp = 1 + // k = 2 -> exp = 10 + // 即 exp = 10^(k-1) + countingSortDigit(nums, exp); + } +} + +/* Driver Code */ +const nums = [10546151, 35663510, 42865989, 34862445, 81883077, + 88906420, 72429244, 30524779, 82060337, 63832996]; +radixSort(nums); +console.log('基数排序完成后 nums =', nums); + +export {};