From 50a726a1a6f2084042dd1846a00614f7e928d591 Mon Sep 17 00:00:00 2001 From: justin Date: Mon, 12 Dec 2022 22:59:51 +0800 Subject: [PATCH 1/2] Add the TypeScript code (Chapter of Sorting) --- .../typescript/chapter_sorting/bubble_sort.ts | 51 ++++++ .../chapter_sorting/insertion_sort.ts | 27 +++ .../typescript/chapter_sorting/merge_sort.ts | 53 ++++++ .../typescript/chapter_sorting/quick_sort.ts | 168 ++++++++++++++++++ 4 files changed, 299 insertions(+) create mode 100644 codes/typescript/chapter_sorting/bubble_sort.ts create mode 100644 codes/typescript/chapter_sorting/insertion_sort.ts create mode 100644 codes/typescript/chapter_sorting/merge_sort.ts create mode 100644 codes/typescript/chapter_sorting/quick_sort.ts diff --git a/codes/typescript/chapter_sorting/bubble_sort.ts b/codes/typescript/chapter_sorting/bubble_sort.ts new file mode 100644 index 0000000..74e5e6d --- /dev/null +++ b/codes/typescript/chapter_sorting/bubble_sort.ts @@ -0,0 +1,51 @@ +/** + * File: quick_sort.ts + * Created Time: 2022-12-12 + * Author: Justin (xiefahit@gmail.com) + */ + +/* 冒泡排序 */ +function bubbleSort(nums: number[]): void { + // 外循环:待排序元素数量为 n-1, n-2, ..., 1 + for (let i = nums.length - 1; i > 0; i--) { + // 内循环:冒泡操作 + for (let j = 0; j < i; j++) { + if (nums[j] > nums[j + 1]) { + // 交换 nums[j] 与 nums[j + 1] + let tmp = nums[j]; + nums[j] = nums[j + 1]; + nums[j + 1] = tmp; + } + } + } +} + +/* 冒泡排序(标志优化)*/ +function bubbleSortWithFlag(nums: number[]): void { + // 外循环:待排序元素数量为 n-1, n-2, ..., 1 + for (let i = nums.length - 1; i > 0; i--) { + let flag = false; // 初始化标志位 + // 内循环:冒泡操作 + for (let j = 0; j < i; j++) { + if (nums[j] > nums[j + 1]) { + // 交换 nums[j] 与 nums[j + 1] + let tmp = nums[j]; + nums[j] = nums[j + 1]; + nums[j + 1] = tmp; + flag = true; // 记录交换元素 + } + } + if (!flag) break; // 此轮冒泡未交换任何元素,直接跳出 + } +} + +/* Driver Code */ +const nums = [4, 1, 3, 1, 5, 2]; +bubbleSort(nums); +console.log('排序后数组 nums =', nums); + +const nums1 = [4, 1, 3, 1, 5, 2]; +bubbleSortWithFlag(nums1); +console.log('排序后数组 nums =', nums1); + +export {}; diff --git a/codes/typescript/chapter_sorting/insertion_sort.ts b/codes/typescript/chapter_sorting/insertion_sort.ts new file mode 100644 index 0000000..82d913b --- /dev/null +++ b/codes/typescript/chapter_sorting/insertion_sort.ts @@ -0,0 +1,27 @@ +/** + * File: quick_sort.ts + * Created Time: 2022-12-12 + * Author: Justin (xiefahit@gmail.com) + */ + +/* 插入排序 */ +function insertionSort(nums: number[]): void { + // 外循环:base = nums[1], nums[2], ..., nums[n-1] + for (let i = 1; i < nums.length; i++) { + const base = nums[i]; + let j = i - 1; + // 内循环:将 base 插入到左边的正确位置 + while (j >= 0 && nums[j] > base) { + nums[j + 1] = nums[j]; // 1. 将 nums[j] 向右移动一位 + j--; + } + nums[j + 1] = base; // 2. 将 base 赋值到正确位置 + } +} + +/* Driver Code */ +const nums = [4, 1, 3, 1, 5, 2]; +insertionSort(nums); +console.log('排序后数组 nums =', nums); + +export {}; diff --git a/codes/typescript/chapter_sorting/merge_sort.ts b/codes/typescript/chapter_sorting/merge_sort.ts new file mode 100644 index 0000000..b84c782 --- /dev/null +++ b/codes/typescript/chapter_sorting/merge_sort.ts @@ -0,0 +1,53 @@ +/** + * File: quick_sort.ts + * Created Time: 2022-12-12 + * Author: Justin (xiefahit@gmail.com) + */ + +/** + * 合并左子数组和右子数组 + * 左子数组区间 [left, mid] + * 右子数组区间 [mid + 1, right] + */ +function merge(nums: number[], left: number, mid: number, right: number): void { + // 初始化辅助数组 + let tmp = nums.slice(left, right + 1); + // 左子数组的起始索引和结束索引 + let leftStart = left - left, leftEnd = mid - left; + // 右子数组的起始索引和结束索引 + let rightStart = mid + 1 - left, rightEnd = right - left; + // i, j 分别指向左子数组、右子数组的首元素 + let i = leftStart, j = rightStart; + // 通过覆盖原数组 nums 来合并左子数组和右子数组 + for (let k = left; k <= right; k++) { + // 若 “左子数组已全部合并完”,则选取右子数组元素,并且 j++ + if (i > leftEnd) { + nums[k] = tmp[j++]; + // 否则,若 “右子数组已全部合并完” 或 “左子数组元素 < 右子数组元素”,则选取左子数组元素,并且 i++ + } else if (j > rightEnd || tmp[i] <= tmp[j]) { + nums[k] = tmp[i++]; + // 否则,若 “左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ + } else { + nums[k] = tmp[j++]; + } + } +} + +/* 归并排序 */ +function mergeSort(nums: number[], left: number, right: number): void { + // 终止条件 + if (left >= right) return; // 当子数组长度为 1 时终止递归 + // 划分阶段 + let mid = Math.floor((left + right) / 2); // 计算中点 + mergeSort(nums, left, mid); // 递归左子数组 + mergeSort(nums, mid + 1, right); // 递归右子数组 + // 合并阶段 + merge(nums, left, mid, right); +} + +/* Driver Code */ +const nums = [7, 3, 2, 6, 0, 1, 5, 4]; +mergeSort(nums, 0, nums.length - 1); +console.log('归并排序完成后 nums =', nums); + +export {}; diff --git a/codes/typescript/chapter_sorting/quick_sort.ts b/codes/typescript/chapter_sorting/quick_sort.ts new file mode 100644 index 0000000..6379e08 --- /dev/null +++ b/codes/typescript/chapter_sorting/quick_sort.ts @@ -0,0 +1,168 @@ +/** + * File: quick_sort.ts + * Created Time: 2022-12-12 + * Author: Justin (xiefahit@gmail.com) + */ + +/* 快速排序类 */ +class QuickSort { + /* 元素交换 */ + swap(nums: number[], i: number, j: number): void { + let tmp = nums[i]; + nums[i] = nums[j]; + nums[j] = tmp; + } + + /* 哨兵划分 */ + partition(nums: number[], left: number, right: number): number { + // 以 nums[left] 作为基准数 + let i = left, j = right; + while (i < j) { + while (i < j && nums[j] >= nums[left]) { + j -= 1; // 从右向左找首个小于基准数的元素 + } + while (i < j && nums[i] <= nums[left]) { + i += 1; // 从左向右找首个大于基准数的元素 + } + // 元素交换 + this.swap(nums, i, j); // 交换这两个元素 + } + this.swap(nums, i, left); // 将基准数交换至两子数组的分界线 + return i; // 返回基准数的索引 + } + + /* 快速排序 */ + quickSort(nums: number[], left: number, right: number): void { + // 子数组长度为 1 时终止递归 + if (left >= right) { + return; + } + // 哨兵划分 + const pivot = this.partition(nums, left, right); + // 递归左子数组、右子数组 + this.quickSort(nums, left, pivot - 1); + this.quickSort(nums, pivot + 1, right); + } +} + +/* 快速排序类(中位基准数优化) */ +class QuickSortMedian { + /* 元素交换 */ + swap(nums: number[], i: number, j: number): void { + let tmp = nums[i]; + nums[i] = nums[j]; + nums[j] = tmp; + } + + /* 选取三个元素的中位数 */ + medianThree(nums: number[], left: number, mid: number, right: number): number { + // 使用了异或操作来简化代码 + // 异或规则为 0 ^ 0 = 1 ^ 1 = 0, 0 ^ 1 = 1 ^ 0 = 1 + if (Number(nums[left] > nums[mid]) ^ Number(nums[left] > nums[right])) { + return left; + } else if (Number(nums[mid] < nums[left]) ^ Number(nums[mid] < nums[right])) { + return mid; + } else { + return right; + } + } + + /* 哨兵划分(三数取中值) */ + partition(nums: number[], left: number, right: number): number { + // 选取三个候选元素的中位数 + let med = this.medianThree(nums, left, Math.floor((left + right) / 2), right); + // 将中位数交换至数组最左端 + this.swap(nums, left, med); + // 以 nums[left] 作为基准数 + let i = left, j = right; + while (i < j) { + while (i < j && nums[j] >= nums[left]) { + j--; // 从右向左找首个小于基准数的元素 + } + while (i < j && nums[i] <= nums[left]) { + i++; // 从左向右找首个大于基准数的元素 + } + this.swap(nums, i, j); // 交换这两个元素 + } + this.swap(nums, i, left); // 将基准数交换至两子数组的分界线 + return i; // 返回基准数的索引 + } + + /* 快速排序 */ + quickSort(nums: number[], left: number, right: number): void { + // 子数组长度为 1 时终止递归 + if (left >= right) { + return; + } + // 哨兵划分 + const pivot = this.partition(nums, left, right); + // 递归左子数组、右子数组 + this.quickSort(nums, left, pivot - 1); + this.quickSort(nums, pivot + 1, right); + } +} + +/* 快速排序类(尾递归优化) */ +class QuickSortTailCall { + /* 元素交换 */ + swap(nums: number[], i: number, j: number): void { + let tmp = nums[i]; + nums[i] = nums[j]; + nums[j] = tmp; + } + + /* 哨兵划分 */ + partition(nums: number[], left: number, right: number): number { + // 以 nums[left] 作为基准数 + let i = left, j = right; + while (i < j) { + while (i < j && nums[j] >= nums[left]) { + j--; // 从右向左找首个小于基准数的元素 + } + while (i < j && nums[i] <= nums[left]) { + i++; // 从左向右找首个大于基准数的元素 + } + this.swap(nums, i, j); // 交换这两个元素 + } + this.swap(nums, i, left); // 将基准数交换至两子数组的分界线 + return i; // 返回基准数的索引 + } + + /* 快速排序(尾递归优化) */ + quickSort(nums: number[], left: number, right: number): void { + // 子数组长度为 1 时终止 + while (left < right) { + // 哨兵划分操作 + let pivot = this.partition(nums, left, right); + // 对两个子数组中较短的那个执行快排 + if (pivot - left < right - pivot) { + this.quickSort(nums, left, pivot - 1); // 递归排序左子数组 + left = pivot + 1; // 剩余待排序区间为 [pivot + 1, right] + } else { + this.quickSort(nums, pivot + 1, right); // 递归排序右子数组 + right = pivot - 1; // 剩余待排序区间为 [left, pivot - 1] + } + } + } +} + +/* Driver Code */ +/* 快速排序 */ +const nums = [4, 1, 3, 1, 5, 2]; +const quickSort = new QuickSort(); +quickSort.quickSort(nums, 0, nums.length - 1); +console.log('快速排序完成后 nums =', nums); + +/* 快速排序(中位基准数优化) */ +const nums1 = [4, 1, 3, 1, 5, 2]; +const quickSortMedian = new QuickSort(); +quickSortMedian.quickSort(nums1, 0, nums1.length - 1); +console.log('快速排序(中位基准数优化)完成后 nums =', nums1); + +/* 快速排序(尾递归优化) */ +const nums2 = [4, 1, 3, 1, 5, 2]; +const quickSortTailCall = new QuickSort(); +quickSortTailCall.quickSort(nums2, 0, nums2.length - 1); +console.log('快速排序(尾递归优化)完成后 nums =', nums2); + +export {}; From 14e14677cd575fb01a031fd2b12ce4690a73b095 Mon Sep 17 00:00:00 2001 From: justin Date: Mon, 12 Dec 2022 23:18:12 +0800 Subject: [PATCH 2/2] Add the TypeScript code to docs (Chapter of Sorting) --- docs/chapter_sorting/bubble_sort.md | 35 +++++++++++- docs/chapter_sorting/insertion_sort.md | 15 +++++- docs/chapter_sorting/merge_sort.md | 39 ++++++++++++++ docs/chapter_sorting/quick_sort.md | 75 ++++++++++++++++++++++++-- 4 files changed, 158 insertions(+), 6 deletions(-) diff --git a/docs/chapter_sorting/bubble_sort.md b/docs/chapter_sorting/bubble_sort.md index 488c575..2dddba6 100644 --- a/docs/chapter_sorting/bubble_sort.md +++ b/docs/chapter_sorting/bubble_sort.md @@ -150,7 +150,21 @@ comments: true === "TypeScript" ```typescript title="bubble_sort.ts" - + /* 冒泡排序 */ + function bubbleSort(nums: number[]): void { + // 外循环:待排序元素数量为 n-1, n-2, ..., 1 + for (let i = nums.length - 1; i > 0; i--) { + // 内循环:冒泡操作 + for (let j = 0; j < i; j++) { + if (nums[j] > nums[j + 1]) { + // 交换 nums[j] 与 nums[j + 1] + let tmp = nums[j]; + nums[j] = nums[j + 1]; + nums[j + 1] = tmp; + } + } + } + } ``` === "C" @@ -297,7 +311,24 @@ comments: true === "TypeScript" ```typescript title="bubble_sort.ts" - + /* 冒泡排序(标志优化)*/ + function bubbleSortWithFlag(nums: number[]): void { + // 外循环:待排序元素数量为 n-1, n-2, ..., 1 + for (let i = nums.length - 1; i > 0; i--) { + let flag = false; // 初始化标志位 + // 内循环:冒泡操作 + for (let j = 0; j < i; j++) { + if (nums[j] > nums[j + 1]) { + // 交换 nums[j] 与 nums[j + 1] + let tmp = nums[j]; + nums[j] = nums[j + 1]; + nums[j + 1] = tmp; + flag = true; // 记录交换元素 + } + } + if (!flag) break; // 此轮冒泡未交换任何元素,直接跳出 + } + } ``` === "C" diff --git a/docs/chapter_sorting/insertion_sort.md b/docs/chapter_sorting/insertion_sort.md index c9be01c..1ec47f8 100644 --- a/docs/chapter_sorting/insertion_sort.md +++ b/docs/chapter_sorting/insertion_sort.md @@ -116,7 +116,20 @@ comments: true === "TypeScript" ```typescript title="insertion_sort.ts" - + /* 插入排序 */ + function insertionSort(nums: number[]): void { + // 外循环:base = nums[1], nums[2], ..., nums[n-1] + for (let i = 1; i < nums.length; i++) { + const base = nums[i]; + let j = i - 1; + // 内循环:将 base 插入到左边的正确位置 + while (j >= 0 && nums[j] > base) { + nums[j + 1] = nums[j]; // 1. 将 nums[j] 向右移动一位 + j--; + } + nums[j + 1] = base; // 2. 将 base 赋值到正确位置 + } + } ``` === "C" diff --git a/docs/chapter_sorting/merge_sort.md b/docs/chapter_sorting/merge_sort.md index 64942f6..2d67dfd 100644 --- a/docs/chapter_sorting/merge_sort.md +++ b/docs/chapter_sorting/merge_sort.md @@ -246,7 +246,46 @@ comments: true === "TypeScript" ```typescript title="merge_sort.ts" + /** + * 合并左子数组和右子数组 + * 左子数组区间 [left, mid] + * 右子数组区间 [mid + 1, right] + */ + function merge(nums: number[], left: number, mid: number, right: number): void { + // 初始化辅助数组 + let tmp = nums.slice(left, right + 1); + // 左子数组的起始索引和结束索引 + let leftStart = left - left, leftEnd = mid - left; + // 右子数组的起始索引和结束索引 + let rightStart = mid + 1 - left, rightEnd = right - left; + // i, j 分别指向左子数组、右子数组的首元素 + let i = leftStart, j = rightStart; + // 通过覆盖原数组 nums 来合并左子数组和右子数组 + for (let k = left; k <= right; k++) { + // 若 “左子数组已全部合并完”,则选取右子数组元素,并且 j++ + if (i > leftEnd) { + nums[k] = tmp[j++]; + // 否则,若 “右子数组已全部合并完” 或 “左子数组元素 < 右子数组元素”,则选取左子数组元素,并且 i++ + } else if (j > rightEnd || tmp[i] <= tmp[j]) { + nums[k] = tmp[i++]; + // 否则,若 “左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ + } else { + nums[k] = tmp[j++]; + } + } + } + /* 归并排序 */ + function mergeSort(nums: number[], left: number, right: number): void { + // 终止条件 + if (left >= right) return; // 当子数组长度为 1 时终止递归 + // 划分阶段 + let mid = Math.floor((left + right) / 2); // 计算中点 + mergeSort(nums, left, mid); // 递归左子数组 + mergeSort(nums, mid + 1, right); // 递归右子数组 + // 合并阶段 + merge(nums, left, mid, right); + } ``` === "C" diff --git a/docs/chapter_sorting/quick_sort.md b/docs/chapter_sorting/quick_sort.md index 7934b59..7724a05 100644 --- a/docs/chapter_sorting/quick_sort.md +++ b/docs/chapter_sorting/quick_sort.md @@ -144,7 +144,30 @@ comments: true === "TypeScript" ```typescript title="quick_sort.ts" + /* 元素交换 */ + function swap(nums: number[], i: number, j: number): void { + let tmp = nums[i]; + nums[i] = nums[j]; + nums[j] = tmp; + } + /* 哨兵划分 */ + function partition(nums: number[], left: number, right: number): number { + // 以 nums[left] 作为基准数 + let i = left, j = right; + while (i < j) { + while (i < j && nums[j] >= nums[left]) { + j -= 1; // 从右向左找首个小于基准数的元素 + } + while (i < j && nums[i] <= nums[left]) { + i += 1; // 从左向右找首个大于基准数的元素 + } + // 元素交换 + swap(nums, i, j); // 交换这两个元素 + } + swap(nums, i, left); // 将基准数交换至两子数组的分界线 + return i; // 返回基准数的索引 + } ``` === "C" @@ -246,7 +269,18 @@ comments: true === "TypeScript" ```typescript title="quick_sort.ts" - + /* 快速排序 */ + function quickSort(nums: number[], left: number, right: number): void { + // 子数组长度为 1 时终止递归 + if (left >= right) { + return; + } + // 哨兵划分 + const pivot = partition(nums, left, right); + // 递归左子数组、右子数组 + quickSort(nums, left, pivot - 1); + quickSort(nums, pivot + 1, right); + } ``` === "C" @@ -401,7 +435,27 @@ comments: true === "TypeScript" ```typescript title="quick_sort.ts" + /* 选取三个元素的中位数 */ + function medianThree(nums: number[], left: number, mid: number, right: number): number { + // 使用了异或操作来简化代码 + // 异或规则为 0 ^ 0 = 1 ^ 1 = 0, 0 ^ 1 = 1 ^ 0 = 1 + if (Number(nums[left] > nums[mid]) ^ Number(nums[left] > nums[right])) { + return left; + } else if (Number(nums[mid] < nums[left]) ^ Number(nums[mid] < nums[right])) { + return mid; + } else { + return right; + } + } + /* 哨兵划分(三数取中值) */ + function partition(nums: number[], left: number, right: number): number { + // 选取三个候选元素的中位数 + let med = medianThree(nums, left, Math.floor((left + right) / 2), right); + // 将中位数交换至数组最左端 + swap(nums, left, med); + // 以 nums[left] 作为基准数 + // 下同省略... ``` === "C" @@ -492,7 +546,7 @@ comments: true ```js title="quick_sort.js" /* 快速排序(尾递归优化) */ - quickSort(nums, left, right) { + function quickSort(nums, left, right) { // 子数组长度为 1 时终止 while (left < right) { // 哨兵划分操作 @@ -512,7 +566,22 @@ comments: true === "TypeScript" ```typescript title="quick_sort.ts" - + /* 快速排序(尾递归优化) */ + function quickSort(nums: number[], left: number, right: number): void { + // 子数组长度为 1 时终止 + while (left < right) { + // 哨兵划分操作 + let pivot = partition(nums, left, right); + // 对两个子数组中较短的那个执行快排 + if (pivot - left < right - pivot) { + quickSort(nums, left, pivot - 1); // 递归排序左子数组 + left = pivot + 1; // 剩余待排序区间为 [pivot + 1, right] + } else { + quickSort(nums, pivot + 1, right); // 递归排序右子数组 + right = pivot - 1; // 剩余待排序区间为 [left, pivot - 1] + } + } + } ``` === "C"