// 元素交换 void _swap(List<int> nums, int i, int j) { int tmp = nums[i]; nums[i] = nums[j]; nums[j] = tmp; }
// 哨兵划分 int _partition(List<int> nums, int left, int right) { // 以 nums[left] 为基数 int i = left, j = right; while (i < j) { // 从右向左找首个小于基准数的元素 while (i < j && nums[j] >= nums[left]) j--; // 从左向右找首个大于基准数的元素 while (i < j && nums[i] <= nums[left]) i++; // 交换这两个元素 _swap(nums, i, j); } // 将基准数交换至两个子数组的分界线 _swap(nums, i, left); // 返回基准数的索引 return i; }
// 快速排序 void quickSort(List<int> nums, int left, int right) { // 子数组长度为 1 时终止递归 if (left >= right) return; // 哨兵划分 int pivot = _partition(nums, left, right); // 递归左子数组,右子数组 quickSort(nums, left, pivot - 1); quickSort(nums, pivot + 1, right); }
// 堆排序 void headSort(List<int> nums) { // 建堆操作:堆化除页节点以外的其他所有节点 for (var i = nums.length ~/ 2 - 1; i >= 0; i--) { shiftDown(nums, nums.length, i); }
// 从堆中提取最大元素,循环 n-1 轮 for (var i = nums.length - 1; i > 0; i--) { // 交换根节点与最右节点(交换首元素和尾元素) int tmp = nums[0]; nums[0] = nums[i]; nums[i] = tmp; // 以根节点为起点,从顶至低进行堆化 shiftDown(nums, i, 0); } }
// 堆的长度为 n,从节点 i 开始,从顶至低堆化 void shiftDown(List<int> nums, int n, int i) { while (true) { // 判断节点 i,l,r 中值最大的节点,记为 ma int l = 2 * i + 1; int r = 2 * i + 2; int ma = i; if (l < n && nums[l] > nums[ma]) ma = l; if (r < n && nums[r] > nums[ma]) ma = r; // 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出 if (ma == i) break; // 交换两节点 int temp = nums[i]; nums[i] = nums[ma]; nums[ma] = temp; // 循环向下堆化 i = ma; } }
// 获取元素 _num 的第 k 位,其中 exp = 10^(k-1) int digit(int _num, int exp) { // 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算 return (_num ~/ exp) % 10; }
// 计数排序(根据 nums 第 k 位排序) void countingSortDigit(List<int> nums, int exp) { // 十进制的范围位 0~9, 因此需要长度为 10 的桶数组 List<int> counter = List<int>.filled(10, 0); int n = nums.length; // 统计 0~9 各数字的出现次数 for (var i = 0; i < n; i++) { int d = digit(nums[i], exp); counter[d]++; } // 求前缀和,将“出现个数”转换为“数组索引” for (var i = 1; i < 10; i++) { counter[i] += counter[i - 1]; } // 倒序遍历,根据桶内统计结果,将各元素填入 res List<int> res = List<int>.filled(n, 0); for (var i = n - 1; i >= 0; i--) { int d = digit(nums[i], exp); int j = counter[d] - 1; // 获取 d 在数组中的索引 j res[j] = nums[i]; // 将当前元素填入索引 j counter[d]--; // 将 d 的数量减 1 }
// 使用结果覆盖原数组 nums for (var i = 0; i < n; i++) nums[i] = res[i]; }
// 基数排序 void radixSort(List<int> nums) { // 获取数组的最大元素,用于判断最大位数 // dart 中 int 的长度是 64 位的 int m = -1 << 63; for (var _numin nums) { if (_num > m) m = _num; }
// 按照地位到高位的顺序遍历 for (var exp = 1; exp <= m; exp *= 10) { // 对数组元素的第 k 位执行计数排序 // k = 1 -> exp = 1 // k = 2 -> exp = 10 // 即 exp = 10^(k-1) countingSortDigit(nums, exp); } }