diff --git a/.eslintrc.json b/.eslintrc.json index f5e2cfe..6c87571 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -3,11 +3,25 @@ "browser": true, "es2021": true }, - "extends": "airbnb-base", + "extends": ["airbnb-base"], + "plugins": ["prettier"], "parserOptions": { "ecmaVersion": "latest", "sourceType": "module" }, "rules": { + "no-plusplus": "off", // 允许i++,i-- + "no-use-before-define": "off", // 无需在调用前面定义,之后定义也可以 + "no-console": "off", + "import/extensions": [ + "error", + "ignorePackages", // 忽略 node_modules 内的包 + { + "mjs": "always", // 不允许省略 .mjs 后缀 + "js": "always", // 不允许省略 .js 后缀 + "ts": "always" // 不允许省略 .ts 后缀 + } + ], + "no-param-reassign": "off" } } diff --git a/package-lock.json b/package-lock.json index b3cd119..b02ba1d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,9 @@ "devDependencies": { "eslint": "^8.57.0", "eslint-config-airbnb-base": "^15.0.0", - "eslint-plugin-import": "^2.29.1" + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-import": "^2.29.1", + "eslint-plugin-prettier": "^5.1.3" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -147,6 +149,18 @@ "node": ">= 8" } }, + "node_modules/@pkgr/core": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", + "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", @@ -753,6 +767,18 @@ "eslint-plugin-import": "^2.25.2" } }, + "node_modules/eslint-config-prettier": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, "node_modules/eslint-import-resolver-node": { "version": "0.3.9", "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", @@ -851,6 +877,36 @@ "node": ">=0.10.0" } }, + "node_modules/eslint-plugin-prettier": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.3.tgz", + "integrity": "sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.8.6" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": "*", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, "node_modules/eslint-scope": { "version": "7.2.2", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", @@ -944,6 +1000,12 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -1899,6 +1961,34 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", + "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", + "dev": true, + "peer": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -2237,6 +2327,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/synckit": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.8.tgz", + "integrity": "sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==", + "dev": true, + "dependencies": { + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -2255,6 +2361,12 @@ "strip-bom": "^3.0.0" } }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", diff --git a/package.json b/package.json index ba83431..c7ed402 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,10 @@ "version": "1.0.0", "description": "一个记录个人算法练习的仓库", "main": "index.js", - "scripts": {}, + "scripts": { + "eslint": "eslint **/*js **/*.mjs", + "fix": "eslint **/*js **/*.mjs --fix" + }, "repository": { "type": "git", "url": "https://gitea-icoding.ddnsto.com/yigencong/algorithm.git" @@ -19,6 +22,8 @@ "devDependencies": { "eslint": "^8.57.0", "eslint-config-airbnb-base": "^15.0.0", - "eslint-plugin-import": "^2.29.1" + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-import": "^2.29.1", + "eslint-plugin-prettier": "^5.1.3" } } diff --git a/sort/bubble-sort-test.mjs b/sort/bubble-sort-test.mjs index 96f71a2..3effac3 100644 --- a/sort/bubble-sort-test.mjs +++ b/sort/bubble-sort-test.mjs @@ -1,15 +1,18 @@ -import {generateRandomArray, measureTime} from "../util/index.mjs" -import { bubbleSort, bubbleSort2, bubbleSort3, cocktailSort } from "./bubble-sort.mjs" - - -const arr = generateRandomArray(10000) -const arr2 = generateRandomArray(10000) -// const arr3 = generateRandomArray(10000) -// measureTime(bubbleSort, arr) -// measureTime(bubbleSort2, arr2) -// measureTime(bubbleSort3, arr3) +import { generateRandomArray, measureTime } from '../util/index.mjs'; +import { + bubbleSort, + bubbleSort2, + bubbleSort3, + cocktailSort, +} from './bubble-sort.mjs'; +const arr = generateRandomArray(10000); +const arr2 = generateRandomArray(10000); +const arr3 = generateRandomArray(10000); +measureTime(bubbleSort, arr); +measureTime(bubbleSort2, arr2); +measureTime(bubbleSort3, arr3); // 鸡尾酒排序测试 -measureTime(cocktailSort, arr) -measureTime(bubbleSort, arr2) \ No newline at end of file +measureTime(cocktailSort, arr); +measureTime(bubbleSort, arr2); diff --git a/sort/bubble-sort.mjs b/sort/bubble-sort.mjs index 0e5a1f2..dac6b45 100644 --- a/sort/bubble-sort.mjs +++ b/sort/bubble-sort.mjs @@ -1,72 +1,78 @@ -import {swap} from "../util/index.mjs" +import { swap } from '../util/index.mjs'; export function bubbleSort(arr) { - let n = arr.length - for (let i = 1;i < n; i++) { // 理解为何i从1开始,因为冒泡排序是两两比较的,此循环只是限制比较的次数 - for(let j = 0;j < n-i;j++) { // 此循环才是排序的关键,当当前值大于后一个值时交换位置,没做完一遍最后一个值永远是本轮循环最大的 - if (arr[j] > arr[j+1]){ - swap(arr, j, j+1) - } - } + const n = arr.length; + for (let i = 1; i < n; i++) { + // 理解为何i从1开始,因为冒泡排序是两两比较的,此循环只是限制比较的次数 + for (let j = 0; j < n - i; j++) { + // 此循环才是排序的关键,当当前值大于后一个值时交换位置,没做完一遍最后一个值永远是本轮循环最大的 + if (arr[j] > arr[j + 1]) { + swap(arr, j, j + 1); + } } + } } // 优化方案1 export function bubbleSort2(arr) { - let n = arr.length; - for (let i = 1;i arr[j+1]) { - swap(arr, j, j+1) - hasSort = false // 交换元素之后,设置标志位为无序状态 - } - } - if (hasSort == true) { - break - } + const n = arr.length; + for (let i = 1; i < n; i++) { + let hasSort = true; // 添加标志位,默认是有序的,在一轮比较下来如果没有发生交换,说明整个数组就是有序的,直接结束排序 + for (let j = 0; j < n - i; j++) { + if (arr[j] > arr[j + 1]) { + swap(arr, j, j + 1); + hasSort = false; // 交换元素之后,设置标志位为无序状态 + } } + if (hasSort === true) { + break; + } + } } // 优化方案2 export function bubbleSort3(arr) { - let n = arr.length, k = n-1, swapPos = 0; - for (let i = 1;i arr[j+1]) { - swap(arr, j, j+1) - hasSort = false // 交换元素之后,设置标志位为无序状态 - swapPos = j // 记录最后一次交换的元素的位置 - } - } - if (hasSort == true) { - break - } - k = swapPos // 把最后一次交换的位置给k,之后的排序支队1 - n-k的元素进行比较,因为之后的元素都是有序的 + const n = arr.length; + let k = n - 1; + let swapPos = 0; + for (let i = 1; i < n; i++) { + let hasSort = true; // 添加标志位,默认是有序的,在一轮比较下来如果没有发生交换,说明整个数组就是有序的,直接结束排序 + for (let j = 0; j < k; j++) { + if (arr[j] > arr[j + 1]) { + swap(arr, j, j + 1); + hasSort = false; // 交换元素之后,设置标志位为无序状态 + swapPos = j; // 记录最后一次交换的元素的位置 + } } + if (hasSort === true) { + break; + } + k = swapPos; // 把最后一次交换的位置给k,之后的排序支队1 - n-k的元素进行比较,因为之后的元素都是有序的 + } } // 鸡尾酒排序(冒泡排序变形) export function cocktailSort(arr) { - let left = 0, right = arr.length - 1, index; - while(left < right) { - // 大的排在后面 - for (let i = left;i < right;i++) { - if (arr[i] > arr[i+1]) { - swap(arr, i, i+1) - index = i - } - } - // 第一轮排完之后index右侧的数字都是有序的,只需从index开始排就行(重点) - right = index - // 小的排前面 - for (let i = right;i > left;i--) { - if (arr[i] < arr[i-1]) { - swap(arr, i, i-1) - index = i - } - } - left = index + let left = 0; + let right = arr.length - 1; + let index; + while (left < right) { + // 大的排在后面 + for (let i = left; i < right; i++) { + if (arr[i] > arr[i + 1]) { + swap(arr, i, i + 1); + index = i; + } } + // 第一轮排完之后index右侧的数字都是有序的,只需从index开始排就行(重点) + right = index; + // 小的排前面 + for (let i = right; i > left; i--) { + if (arr[i] < arr[i - 1]) { + swap(arr, i, i - 1); + index = i; + } + } + left = index; + } } diff --git a/sort/insertion-sort.mjs b/sort/insertion-sort.mjs index 1885a02..a94c1ec 100644 --- a/sort/insertion-sort.mjs +++ b/sort/insertion-sort.mjs @@ -1,18 +1,18 @@ -import { generateRandomArray, isSort } from "../util/index.mjs"; - /** * 插入排序 - * @param {number[]} arr + * @param {number[]} arr */ -export function insertionSort(arr){ - let n = arr.length; - for (let i = 1; i=0 && arr[j] > currentElement) { - arr[j+1] = arr[j] - j-- - } - arr[j+1] = currentElement +function insertionSort(arr) { + const n = arr.length; + for (let i = 1; i < n; i++) { + const currentElement = arr[i]; + let j = i - 1; + while (j >= 0 && arr[j] > currentElement) { + arr[j + 1] = arr[j]; + j--; } + arr[j + 1] = currentElement; + } } + +export default insertionSort; diff --git a/sort/merge-sort.mjs b/sort/merge-sort.mjs index c51964e..ae74803 100644 --- a/sort/merge-sort.mjs +++ b/sort/merge-sort.mjs @@ -3,40 +3,42 @@ * @param {number[]} left - 需要合并的左数组 * @param {number[]} right - 需要合并的右数组 */ - export function mergeSort(arr) { - if (arr.length <= 1) { - return arr; // 已经有序 - } +export function mergeSort(arr) { + if (arr.length <= 1) { + return arr; // 已经有序 + } - // 分解 - const middle = Math.floor(arr.length / 2); - const left = arr.slice(0, middle); - const right = arr.slice(middle); + // 分解 + const middle = Math.floor(arr.length / 2); + const left = arr.slice(0, middle); + const right = arr.slice(middle); - // 递归地对左右两部分进行归并排序 - const sortedLeft = mergeSort(left); - const sortedRight = mergeSort(right); + // 递归地对左右两部分进行归并排序 + const sortedLeft = mergeSort(left); + const sortedRight = mergeSort(right); - // 合并 - return merge(sortedLeft, sortedRight); + // 合并 + return merge(sortedLeft, sortedRight); } function merge(left, right) { - let result = []; - let leftIndex = 0; - let rightIndex = 0; + const result = []; + let leftIndex = 0; + let rightIndex = 0; - // 比较两个数组的元素,将较小的元素加入结果数组 - while (leftIndex < left.length && rightIndex < right.length) { - if (left[leftIndex] < right[rightIndex]) { - result.push(left[leftIndex]); - leftIndex++; - } else { - result.push(right[rightIndex]); - rightIndex++; - } + // 比较两个数组的元素,将较小的元素加入结果数组 + while (leftIndex < left.length && rightIndex < right.length) { + if (left[leftIndex] < right[rightIndex]) { + result.push(left[leftIndex]); + leftIndex++; + } else { + result.push(right[rightIndex]); + rightIndex++; } + } - // 将剩余的元素加入结果数组 - return result.concat(left.slice(leftIndex), right.slice(rightIndex)); + // 将剩余的元素加入结果数组 + return result.concat(left.slice(leftIndex), right.slice(rightIndex)); } + +export default mergeSort; diff --git a/sort/quick-sort.mjs b/sort/quick-sort.mjs index 8f9e4a9..c0b5dd4 100644 --- a/sort/quick-sort.mjs +++ b/sort/quick-sort.mjs @@ -1,22 +1,21 @@ -import { swap } from "../util/index.mjs" +import { swap } from '../util/index.mjs'; /** - * - * @param {number[]} arr + * + * @param {number[]} array - 需要排序的数组 */ -export function quickSort(arr) { - function QuickSort(arr, left, right) { - if (left < right) { - let index = partition(arr, left, right) - QuickSort(arr, left, index-1) // index是中心轴无需参与排序,若写成index会造成堆栈益处 - QuickSort(arr, index+1, right) - } +export function quickSort(array) { + function QuickSort(arr, left, right) { + if (left < right) { + const index = partition(arr, left, right); + QuickSort(arr, left, index - 1); // index是中心轴无需参与排序,若写成index会造成堆栈益处 + QuickSort(arr, index + 1, right); } - QuickSort(arr,0, arr.length - 1) - return arr + } + QuickSort(array, 0, array.length - 1); + return array; } - /** * @description 使用左右指针法来分数组 * @param {number[]} arr - 需要分治的函数 @@ -24,37 +23,37 @@ export function quickSort(arr) { * @param {number} right - 结束的位置 */ function partition(arr, left, right) { - let pivot = arr[right] // 以right作为pivot - let pivotIndex = right + const pivot = arr[right]; // 以right作为pivot + const pivotIndex = right; - while (left < right) { - // left开始往右边找比pivot大的值 - while (left < right && arr[left] <= pivot) { - left++ - } - // right开始往左边找比pivot小的值 - while (left < right && arr[right] >= pivot) { - right-- - } - swap(arr, left, right) + while (left < right) { + // left开始往右边找比pivot大的值 + while (left < right && arr[left] <= pivot) { + left++; } - swap(arr, left, pivotIndex) - return left + // right开始往左边找比pivot小的值 + while (left < right && arr[right] >= pivot) { + right--; + } + swap(arr, left, right); + } + swap(arr, left, pivotIndex); + return left; } /** - * - * @param {number[]} arr - 需要快速排序的数组 + * + * @param {number[]} array - 需要快速排序的数组 */ -export function quickSort2(arr){ - function QuickSort(arr, left, right) { - if (left < right) { - const pivot = partition2(arr, left, right) - QuickSort(arr,left,pivot -1) - QuickSort(arr, pivot+1, right) - } +export function quickSort2(array) { + function QuickSort(arr, left, right) { + if (left < right) { + const pivot = partition2(arr, left, right); + QuickSort(arr, left, pivot - 1); + QuickSort(arr, pivot + 1, right); } - QuickSort(arr,0, arr.length-1) + } + QuickSort(array, 0, array.length - 1); } /** @@ -63,128 +62,128 @@ export function quickSort2(arr){ * @param {number} left - 开始的位置 * @param {number} right - 结束的位置 */ -function partition2(arr, left, right){ - let pivot = arr[right] - while(left < right) { - while(left= pivot) { - right-- - } - arr[left] = arr[right] +function partition2(arr, left, right) { + const pivot = arr[right]; + while (left < right) { + while (left < right && arr[left] <= pivot) { + left++; } - arr[left] = pivot - return left + arr[right] = arr[left]; + while (left < right && arr[right] >= pivot) { + right--; + } + arr[left] = arr[right]; + } + arr[left] = pivot; + return left; } /** * @description 使用前后指针法分数组 - * @param {number[]} arr - 需要排序的数组 + * @param {number[]} array - 需要排序的数组 */ -export function quickSort3(arr) { - function QuickSort(arr, left, right) { - if(left < right){ - const pivot = partition4(arr, left, right) - QuickSort(arr, left, pivot-1) - QuickSort(arr, pivot+1, right) - } +export function quickSort3(array) { + function QuickSort(arr, left, right) { + if (left < right) { + const pivot = partition4(arr, left, right); + QuickSort(arr, left, pivot - 1); + QuickSort(arr, pivot + 1, right); } - QuickSort(arr, 0, arr.length -1) + } + QuickSort(array, 0, array.length - 1); } /** * @description 使用前后指针法 - * @param {number[]} arr - * @param {number} left - 开始的位置 + * @param {number[]} arr + * @param {number} left - 开始的位置 * @param {number} right - 结束的位置 */ -function partition3(arr, left, right){ - let cur = left - let pre = left - 1 - const pivot = arr[right] // 选取最后一个元素作为中枢 - while(cur < right) { - if(arr[cur]< pivot) { - swap(arr, cur, ++pre) - } - cur++ +// eslint-disable-next-line no-unused-vars +function partition3(arr, left, right) { + let cur = left; + let pre = left - 1; + const pivot = arr[right]; // 选取最后一个元素作为中枢 + while (cur < right) { + if (arr[cur] < pivot) { + swap(arr, cur, ++pre); } - /* + cur++; + } + /* 循环结束之后pre所处的元素之前都是小于pivot(包含pre),右侧的元素都是大于或等于pivot的,交换cur+1和pviot的值 */ - swap(arr, pre +1, right) - return pre + 1 + swap(arr, pre + 1, right); + return pre + 1; } /** * @description 使用前后指针法(优化版) - * @param {number[]} arr - * @param {number} left - 开始的位置 + * @param {number[]} arr + * @param {number} left - 开始的位置 * @param {number} right - 结束的位置 */ - function partition4(arr, left, right){ - let cur = left - let pre = left - 1 - const pivot = arr[right] // 选取最后一个元素作为中枢 - while(cur < right) { - if(arr[cur] < pivot && ++pre != cur) { - swap(arr, pre, cur) - } - cur++ +function partition4(arr, left, right) { + let cur = left; + let pre = left - 1; + const pivot = arr[right]; // 选取最后一个元素作为中枢 + while (cur < right) { + if (arr[cur] < pivot && ++pre !== cur) { + swap(arr, pre, cur); } - /* + cur++; + } + /* 循环结束之后pre所处的元素之前都是小于pivot(包含pre),右侧的元素都是大于或等于pivot的,交换cur+1和pviot的值 */ - swap(arr, pre +1, right) - return pre + 1 + swap(arr, pre + 1, right); + return pre + 1; } /** * @description 使用非递归方式实现快速排序 - * @param {number[]} arr - 需要排序的数组 + * @param {number[]} arr - 需要排序的数组 */ -export function quickSort4(arr){ - if ( !arr.length || arr.length ==1 ) return // 数组为空,或者数组为1,不处理 - let left = 0 - let right = arr.length - 1 - const stack = [] // js的数组有push和pop符合栈结构 - stack.push(right) - stack.push(left) - while(stack.length) { - const l = stack.pop() // 弹出需要处理的左边l - const r = stack.pop() // 弹出需要处理的有边界 - const pivot = partition4(arr, l, r) - // l < pivot - 1 说明还可以分,当l = pivot -1 说明pivot左侧就一个数,说明左侧已经有序了无须再分 - if(l < pivot - 1) { - stack.push(pivot - 1) - stack.push(l) - } - if(right > pivot + 1) { - stack.push(r) - stack.push(pivot + 1) - } - +export function quickSort4(arr) { + if (!arr.length || arr.length === 1) return; // 数组为空,或者数组为1,不处理 + const left = 0; + const right = arr.length - 1; + const stack = []; // js的数组有push和pop符合栈结构 + stack.push(right); + stack.push(left); + while (stack.length) { + const l = stack.pop(); // 弹出需要处理的左边l + const r = stack.pop(); // 弹出需要处理的有边界 + const pivot = partition4(arr, l, r); + // l < pivot - 1 说明还可以分,当l = pivot -1 说明pivot左侧就一个数,说明左侧已经有序了无须再分 + if (l < pivot - 1) { + stack.push(pivot - 1); + stack.push(l); } + if (right > pivot + 1) { + stack.push(r); + stack.push(pivot + 1); + } + } } export function quickSort5(arr) { - let stack = [] - stack.push(arr.length - 1) - stack.push(0) - while(stack.length) { - let l = stack.pop() - let r = stack.pop() - let index = partition4(arr, l, r) - if (l < index - 1) { - stack.push(index-1) - stack.push(l) - } - if(r > index+1){ - stack.push(r) - stack.push(index + 1) - } + const stack = []; + stack.push(arr.length - 1); + stack.push(0); + while (stack.length) { + const l = stack.pop(); + const r = stack.pop(); + const index = partition4(arr, l, r); + if (l < index - 1) { + stack.push(index - 1); + stack.push(l); } + if (r > index + 1) { + stack.push(r); + stack.push(index + 1); + } + } } /* diff --git a/sort/select-sort.mjs b/sort/select-sort.mjs index ccf3c63..3dbb47f 100644 --- a/sort/select-sort.mjs +++ b/sort/select-sort.mjs @@ -1,69 +1,62 @@ -import { generateRandomArray, isSort, swap } from "../util/index.mjs" +import { swap } from '../util/index.mjs'; /** - * - * @param {number[]} arr + * + * @param {number[]} arr * @description 朴素的冒泡排序 */ export function selectSort(arr) { - let len = arr.length - for(let i = 0; i< len; i++) { - let minIndex = i - for(let j = i+1; j < len; j++) { // 找到本轮最小的值 - if(arr[j] < arr[minIndex]) { - minIndex = j - } - } - swap(arr, i, minIndex); // 交换最小值和选择值的下标 - } + const len = arr.length; + for (let i = 0; i < len; i++) { + let minIndex = i; + for (let j = i + 1; j < len; j++) { // 找到本轮最小的值 + if (arr[j] < arr[minIndex]) { + minIndex = j; + } + } + swap(arr, i, minIndex); // 交换最小值和选择值的下标 + } } /** - * - * @param {number[]} arr + * + * @param {number[]} arr * @description 两端同时排序,类似鸡尾酒排序 */ - function selectSort2(arr) { - let left = 0; - let right = arr.length - 1; +export function selectSort2(arr) { + let left = 0; + let right = arr.length - 1; - while (left < right) { - let minIndex = left; - let maxIndex = right; + while (left < right) { + let minIndex = left; + let maxIndex = right; - for (let i = left; i <= right; i++) { - // 寻找最小值的索引 - if (arr[i] < arr[minIndex]) { - minIndex = i; - } - // 寻找最大值的索引 - if (arr[i] > arr[maxIndex]) { - maxIndex = i; - } - } - - // 将最小值交换到左边 - [arr[left], arr[minIndex]] = [arr[minIndex], arr[left]]; - // 如果最大值的索引是左边的索引,由于上一步交换,所以更新为最小值的索引 - if (maxIndex === left) { - maxIndex = minIndex; - } - // 将最大值交换到右边 - [arr[right], arr[maxIndex]] = [arr[maxIndex], arr[right]]; - - // 更新左右指针 - left++; - right--; + for (let i = left; i <= right; i++) { + // 寻找最小值的索引 + if (arr[i] < arr[minIndex]) { + minIndex = i; + } + // 寻找最大值的索引 + if (arr[i] > arr[maxIndex]) { + maxIndex = i; + } } + + // 将最小值交换到左边 + [arr[left], arr[minIndex]] = [arr[minIndex], arr[left]]; + // 如果最大值的索引是左边的索引,由于上一步交换,所以更新为最小值的索引 + if (maxIndex === left) { + maxIndex = minIndex; + } + // 将最大值交换到右边 + [arr[right], arr[maxIndex]] = [arr[maxIndex], arr[right]]; + + // 更新左右指针 + left++; + right--; + } } +// 实际比较下来优化后的选择排序也没太大作用(实际开发不应使用这种排序,主要学习其解决思路) - - -let arr = generateRandomArray(10) -console.log(arr); -selectSort2(arr) -console.log(isSort(arr)); -console.log(arr); - -// 实际比较下来优化后的选择排序也没太大作用(实际开发不应使用这种排序,主要学习其解决思路) \ No newline at end of file +export default selectSort; diff --git a/sort/shell-sort.mjs b/sort/shell-sort.mjs index 21b12ae..4b5b276 100644 --- a/sort/shell-sort.mjs +++ b/sort/shell-sort.mjs @@ -1,4 +1,3 @@ -import { generateRandomArray, isSort } from "../util/index.mjs"; /* 希尔排序的性能与所选的间隔序列(shell序列)有关。以下是几种常见的希尔排序序列: Hibbard 序列: @@ -18,76 +17,75 @@ H(k) = 3^k + 1 */ /** - * - * @param {number[]} arr + * + * @param {number[]} arr */ -export function shellSort(arr){ - const n = arr.length - // 创建希尔序列,以克努特序列为例 - let gap = 1; - while (gap < n / 3) { - gap = gap*3 +1 - } +export function shellSort(arr) { + const n = arr.length; + // 创建希尔序列,以克努特序列为例 + let gap = 1; + while (gap < n / 3) { + gap = gap * 3 + 1; + } - while(gap>=1) { - // 正常插入排序 - for (let i = 0;i0 && arr[j-gap] > temp) { - arr[j] = arr[j-gap] - j-=gap - } - arr[j] = temp - } + while (gap >= 1) { + // 正常插入排序 + for (let i = 0; i < gap; i++) { + for (let j = i + gap; j < n; j += gap) { + const temp = arr[j]; + while (j > 0 && arr[j - gap] > temp) { + arr[j] = arr[j - gap]; + j -= gap; } - // 正常插入排序 - - gap = Math.floor(gap/3) + arr[j] = temp; + } } + // 正常插入排序 + gap = Math.floor(gap / 3); + } } /** * @description 使用希尔序列排序 - * @param {number[]} arr + * @param {number[]} arr */ export function shellSort2(arr) { - let n = arr.length - let gap = Math.floor(n / 2) - while (gap > 0) { - for(let i = 0;i 0 && arr[j-gap] > temp){ // 插入过程s - arr[j] = arr[j-gap] - j-=gap - } - arr[j] = temp - } + const n = arr.length; + let gap = Math.floor(n / 2); + while (gap > 0) { + for (let i = 0; i < gap; i++) { + for (let j = i + gap; j < n; j += gap) { + const temp = arr[j]; + while (j > 0 && arr[j - gap] > temp) { // 插入过程s + arr[j] = arr[j - gap]; + j -= gap; } - gap = Math.floor(gap / 2) - } + arr[j] = temp; + } + } + gap = Math.floor(gap / 2); + } } /** * 优化版本 - * @param {number[]} arr + * @param {number[]} arr */ -export function shellSort3(arr){ - const n = arr.length - let gap = Math.floor(n/2) - while (gap>0){ // 使用不同的gap对子数组排序 - for(let i = gap; i0 && arr[j-gap] > temp) { - arr[j] = arr[j-gap] - j-=gap - } - arr[j] = temp - } - gap = Math.floor(gap/2) +export function shellSort3(arr) { + const n = arr.length; + let gap = Math.floor(n / 2); + while (gap > 0) { // 使用不同的gap对子数组排序 + for (let i = gap; i < n; i++) { + // 插入过程 + const temp = arr[i]; + let j = i; + while (j > 0 && arr[j - gap] > temp) { + arr[j] = arr[j - gap]; + j -= gap; + } + arr[j] = temp; } + gap = Math.floor(gap / 2); + } } diff --git a/sort/test.mjs b/sort/test.mjs index c50a68e..626da8e 100644 --- a/sort/test.mjs +++ b/sort/test.mjs @@ -1,17 +1,21 @@ -import { generateRandomArray, measureTime, isSort } from "../util/index.mjs"; -import { shellSort } from "./shell-sort.mjs"; -import {insertionSort } from "./insertion-sort.mjs" -import { bubbleSort } from "./bubble-sort.mjs" -import { mergeSort } from "./merge-sort.mjs"; -import {quickSort, quickSort2, quickSort3, quickSort4, quickSort5} from "./quick-sort.mjs" +import { generateRandomArray, measureTime} from '../util/index.mjs'; +import { shellSort } from './shell-sort.mjs'; +import insertionSort from './insertion-sort.mjs'; +import { bubbleSort } from './bubble-sort.mjs'; +import { mergeSort } from './merge-sort.mjs'; +import { + quickSort, quickSort2, quickSort3, quickSort4, quickSort5, +} from './quick-sort.mjs'; -let arr = generateRandomArray(10) +const arr = generateRandomArray(100); +console.log(arr); -measureTime(bubbleSort,arr.slice()) -measureTime(insertionSort,arr.slice()) -measureTime(shellSort,arr.slice()) -let mergeArr = measureTime(mergeSort, arr.slice()) // 归并排序会返回一个新数组,不对原数组修改 -measureTime(quickSort,arr.slice()) -measureTime(quickSort2,arr.slice()) // 挖坑法分解数组 -measureTime(quickSort3,arr.slice()) // 前后指针分解数组 -measureTime(quickSort4,arr) // 不使用递归处理 +measureTime(bubbleSort, arr.slice()); +measureTime(insertionSort, arr.slice()); +measureTime(shellSort, arr.slice()); +measureTime(mergeSort, arr.slice()); // 归并排序会返回一个新数组,不对原数组修改 +measureTime(quickSort, arr.slice()); +measureTime(quickSort2, arr.slice()); // 挖坑法分解数组 +measureTime(quickSort3, arr.slice()); // 前后指针分解数组 +measureTime(quickSort4, arr.slice()); // 不使用递归处理 +measureTime(quickSort5, arr.slice()); // 去除不必要代码 diff --git a/util/index.mjs b/util/index.mjs index c295907..267ba4a 100644 --- a/util/index.mjs +++ b/util/index.mjs @@ -1,14 +1,14 @@ /** - * - * @param {number[]} array - 要操作的数组 - * @param {number} a - 要操作的下标 + * + * @param {number[]} array - 要操作的数组 + * @param {number} a - 要操作的下标 * @param {number} b - 要操作的下标 - * @description 交换数组中两个数的位置 + * @description 交换数组中两个数的位置 */ export function swap(array, a, b) { - let temp = array[a] - array[a] = array[b] - array[b] = temp + const temp = array[a]; + array[a] = array[b]; + array[b] = temp; } /** @@ -16,20 +16,20 @@ export function swap(array, a, b) { * @returns {number[]} - 打乱后的数组 * @description 打乱数组内容 */ - function shuffle(arr) { - // 创建数组的副本,以防止修改原始数组 - const shuffledArray = arr.slice(); +export function shuffle(arr) { + // 创建数组的副本,以防止修改原始数组 + const shuffledArray = arr.slice(); - // Fisher-Yates 洗牌算法 - for (let i = shuffledArray.length - 1; i > 0; i--) { - const a =Math.random() * (i + 1) - console.log(a); - const j = Math.floor(a); - // 交换元素 - [shuffledArray[i], shuffledArray[j]] = [shuffledArray[j], shuffledArray[i]]; - } + // Fisher-Yates 洗牌算法 + for (let i = shuffledArray.length - 1; i > 0; i--) { + const a = Math.random() * (i + 1); + console.log(a); + const j = Math.floor(a); + // 交换元素 + [shuffledArray[i], shuffledArray[j]] = [shuffledArray[j], shuffledArray[i]]; + } - return shuffledArray; + return shuffledArray; } /** @@ -40,59 +40,58 @@ export function swap(array, a, b) { * @param {number} decimals - 保留的小数位数,默认为 2。 * @returns {number[]} - 生成的随机数组。 */ - export function generateRandomArray(len, decimal = false, decimals = 2) { - const randomArray = []; - - for (let i = 0; i < len; i++) { - const randomNumber = decimal - ? parseFloat((Math.random() * 10000).toFixed(decimals)) - : Math.floor(Math.random() * 10000); - - randomArray.push(randomNumber); - } - - return randomArray; +export function generateRandomArray(len, decimal = false, decimals = 2) { + const randomArray = []; + + for (let i = 0; i < len; i++) { + const randomNumber = decimal + ? parseFloat((Math.random() * 10000).toFixed(decimals)) + : Math.floor(Math.random() * 10000); + + randomArray.push(randomNumber); } - export function measureTime(func, ...args) { - // 记录函数开始执行的时间戳 - const startTime = performance.now(); - - // 执行传入的函数,使用 apply 或 call 传入参数 - const result = func(...args); - - // 计算函数执行所花费的时间 - const endTime = performance.now(); - const elapsedTime = endTime - startTime; - - // 打印执行时间 - console.log(`Function execution time: ${elapsedTime} milliseconds`); - return result - } - + return randomArray; +} - export function isSort(arr) { - const len = arr.length - let isInverted = false - if(len <2) { - return true - } - // 判断数组是否倒序 - if(arr[0] > arr[1]) { - isInverted = true - } - if(isInverted) { - for(let i=0;iarr[i+1]){ - return false - } - } - } - return true +export function measureTime(func, ...args) { + // 记录函数开始执行的时间戳 + const startTime = performance.now(); + + // 执行传入的函数,使用 apply 或 call 传入参数 + const result = func(...args); + + // 计算函数执行所花费的时间 + const endTime = performance.now(); + const elapsedTime = endTime - startTime; + + // 打印执行时间 + console.log(`Function execution time: ${elapsedTime} milliseconds`); + return result; +} + +export function isSort(arr) { + const len = arr.length; + let isInverted = false; + if (len < 2) { + return true; } + // 判断数组是否倒序 + if (arr[0] > arr[1]) { + isInverted = true; + } + if (isInverted) { + for (let i = 0; i < len - 1; i++) { + if (arr[i] < arr[i + 1]) { + return false; + } + } + } else { + for (let i = 0; i < len - 1; i++) { + if (arr[i] > arr[i + 1]) { + return false; + } + } + } + return true; +}