Skip to content

Feature-sort #28

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Dec 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions src/sort/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# 排序

这里实现了各种常见的排序方法。

本模块致力于用最简洁的代码实现各种排序方式。

## 名词解释

1.稳定

列表中两个相等的值排序后的位置不变,则是稳定。

## 分类

- 选择排序([selection-sort](./selection-sort.ts))
- 插入排序([insertion-sort](./insertion-sort.ts))
- 冒泡排序([bubble-sort](./bubble-sort.ts))
- 快速排序([quick-sort](./quick-sort.ts))
- 希尔排序([shell-sort](./shell-sort.ts))
- 基数排序([radix-sort](radix-sort))
- 堆排序([heap-sort](./heap-sort.ts))
- 桶排序([bucket-sort](./bucket-sort.ts))
- 计数排序([counting-sort](./counting-sort.ts))
- 归并排序([merge-sort](./merge-sort.ts))

## 资源

- 《算法与数据结构--C 语言描述》
- [Algo-Practice/heapSort.md at master · qcer/Algo-Practice · GitHub](https://github.com/qcer/Algo-Practice/blob/master/Sort/heapSort.md)
- [视频 | 手撕九大经典排序算法,看我就够了! - 知乎](https://zhuanlan.zhihu.com/p/52884590?utm_source=wechat_session&utm_medium=social&utm_oi=37938096242688)
21 changes: 21 additions & 0 deletions src/sort/bubble-sort.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// 冒泡排序算法描述
// 1.假定第一个元素(min)最小,索引值为index
// 2.0 选择第二个元素(索引值为next)和最小的元素进行比较
// 2.1 如果next小于min,则交换两者位置;如果大于或者等于,则不变
// 2.2 此时最小值的index不变(因为这个index是用来保存列表里最小值的)
// 3.0 改变next的索引值,让arr[index]和arr[next]对比,重复2的各个步骤
// 3.1 内存循环完毕后会退出
// 4.0 外层循环index+1,并重复2的各个步骤
// 5.0 外层循环结束,数组已排序好

export function bubbleSort(arr: number[]) {
const len = arr.length
for (let i = 0; i < len; i++) {
for (let j = i + 1; j < len; j++) {
if (arr[i] > arr[j]) {
;[arr[i], arr[j]] = [arr[j], arr[i]]
}
}
}
return arr
}
43 changes: 43 additions & 0 deletions src/sort/bucket-sort.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { insertionSort } from './insertion_sort'

// 桶排序
export function bucketSort(arr: number[], bucketSize: number) {
if (arr.length === 0) {
return arr
}

let i
let minValue = arr[0]
let maxValue = arr[0]
for (i = 1; i < arr.length; i++) {
if (arr[i] < minValue) {
minValue = arr[i] // 输入数据的最小值
} else if (arr[i] > maxValue) {
maxValue = arr[i] // 输入数据的最大值
}
}

// 桶的初始化
const DEFAULT_BUCKET_SIZE = 5 // 设置桶的默认数量为5
bucketSize = bucketSize || DEFAULT_BUCKET_SIZE
const bucketCount = Math.floor((maxValue - minValue) / bucketSize) + 1
const buckets = new Array(bucketCount)
for (i = 0; i < buckets.length; i++) {
buckets[i] = []
}

// 利用映射函数将数据分配到各个桶中
for (i = 0; i < arr.length; i++) {
buckets[Math.floor((arr[i] - minValue) / bucketSize)].push(arr[i])
}

arr.length = 0
for (i = 0; i < buckets.length; i++) {
insertionSort(buckets[i]) // 对每个桶进行排序,这里使用了插入排序
for (const item of buckets[i]) {
arr.push(item)
}
}

return arr
}
32 changes: 32 additions & 0 deletions src/sort/counting-sort.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// 计数排序
// 较好的阐述:https://segmentfault.com/a/1190000012923917#articleHeader0

/**
* 计数排序
* @param arr 待排序的数组
* @param maxValue 数组中的最大值
* @returns {Array} 传入的数组
* @attention 目前只支持大于数值大于 0
*/
export function countingSort(arr: number[], maxValue: number) {
const bucket = new Array(maxValue + 1)
let sortedIndex = 0
const arrLen = arr.length
const bucketLen = maxValue + 1

for (let i = 0; i < arrLen; i++) {
if (!bucket[arr[i]]) {
bucket[arr[i]] = 0
}
bucket[arr[i]]++
}

for (let j = 0; j < bucketLen; j++) {
while (bucket[j] > 0) {
arr[sortedIndex++] = j
bucket[j]--
}
}

return arr
}
54 changes: 54 additions & 0 deletions src/sort/heap-sort.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// 堆排序

// 堆是一个完全二叉树

function swap(arr: number[], a: number, b: number) {
if (a === b) {
return
}
const c = arr[a]
arr[a] = arr[b]
arr[b] = c
}

export function heapSort(arr: number[]) {
const n = arr.length
// 若只有一个或者没有,则返回
if (n <= 1) {
return arr
}
// 若有多个,则建最大堆
else {
// 建堆(Build-Max-Heap)
for (let i = Math.floor(n / 2); i >= 0; i--) {
maxHeapify(arr, i, n)
}

// 堆排序
for (let j = 0; j < n; j++) {
swap(arr, 0, n - 1 - j)
maxHeapify(arr, 0, n - 2 - j)
}
return arr
}
}

function maxHeapify(arr: number[], i: number, size: number) {
// 左子节点为2i + 1,右子节点为2i + 2
const l = 2 * i + 1
const r = 2 * i + 2
let largest = i

// 若子节点比节点大,则标记
if (l <= size && arr[l] > arr[largest]) {
largest = l
}
if (r <= size && arr[r] > arr[largest]) {
largest = r
}
// 若标记有子节点,则交换父子位置,并递归计算
if (largest !== i) {
swap(arr, i, largest)
maxHeapify(arr, largest, size)
}
}
75 changes: 75 additions & 0 deletions src/sort/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { bubbleSort } from './bubble-sort'
import { insertionSort } from './insertion_sort'
import { selectionSort } from './selection-sort'
import { countingSort } from './counting-sort'
import { quickSort } from './quick-sort'
import { mergeSort } from './merge-sort'
import { bucketSort } from './bucket-sort'
import { shellSort } from './shell-sort'
import { radixSort } from './radix-sort'
import { heapSort } from './heap-sort'

// 生成浮动数组
function createArray(max: number) {
const arr: number[] = []
for (let i = 0; i < max; i++) {
arr[i] = Math.floor(Math.random() * (max + 1))
}
return arr
}

describe('sort ways', () => {
const emptyArray: number[] = []
const size = 20000
const arr = createArray(size)
let source: number[]
let sorted: number[]

beforeEach(() => {
source = [...arr]
sorted = [...arr].sort((a, b) => a - b)
})

it('bubble sorting', () => {
expect(bubbleSort(source)).toEqual(sorted)
})

it('insertion sorting', () => {
expect(insertionSort(source)).toEqual(sorted)
})

it('selection sorting ', () => {
expect(selectionSort(source)).toEqual(sorted)
})

it('quick sorting ', () => {
expect(quickSort(source)).toEqual(sorted)
})

it('counting sorting ', () => {
expect(countingSort(source, size)).toEqual(sorted)
})

it('merge sorting ', () => {
expect(mergeSort(source)).toEqual(sorted)
})

it('bucket sorting ', () => {
expect(bucketSort(emptyArray, 5)).toBe(emptyArray)
expect(bucketSort(source, 5)).toEqual(sorted)
})

it('shell sorting ', () => {
expect(shellSort(source)).toEqual(sorted)
})

it('radix sorting ', () => {
radixSort(source)
expect(source).toEqual(sorted)
})

it('heap sorting ', () => {
expect(heapSort(emptyArray)).toBe(emptyArray)
expect(heapSort(source)).toEqual(sorted)
})
})
18 changes: 18 additions & 0 deletions src/sort/insertion_sort.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// 插入排序
// [插入排序的简单理解](https://www.jianshu.com/p/2e12409c086e)

export function insertionSort(arr: number[]) {
const len = arr.length

let i
let j
for (i = 1; i < len; i++) {
const temp = arr[i]
for (j = i - 1; j >= 0 && temp < arr[j]; j--) {
arr[j + 1] = arr[j]
}
arr[j + 1] = temp
}

return arr
}
42 changes: 42 additions & 0 deletions src/sort/merge-sort.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// 归并排序

// https://segmentfault.com/a/1190000017833332

/**
*
* @param p 数组的开始下标
* @param r 数组的结束下标
*/
export function divide(p: number, r: number) {
return Math.floor((p + r) / 2)
}

function merge(A: number[], p: number, q: number, r: number) {
const A1 = A.slice(p, q)
const A2 = A.slice(q, r)
A1.push(Number.MAX_SAFE_INTEGER)
A2.push(Number.MAX_SAFE_INTEGER)

for (let i = p, j = 0, k = 0; i < r; i++) {
if (A1[j] < A2[k]) {
A[i] = A1[j]
j++
} else {
A[i] = A2[k]
k++
}
}
}

export function mergeSort(A: number[], p = 0, r?: number) {
r = r || A.length
if (r - p === 1) {
return
}
const q = divide(p, r)
mergeSort(A, p, q)
mergeSort(A, q, r)
merge(A, p, q, r)

return A
}
31 changes: 31 additions & 0 deletions src/sort/quick-sort.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// 快速排序

// 快速排序使用分治法把一个串(list)分为两个子串(sub-lists)。具体算法实现
// 1.从数组中挑出一个元素,成为基准
// 2.重新排列数组,所有元素比基准值小的摆放在基准前面,所有元素比基准大的摆在基准后面(相同的可以任意一边
// 这个分区退出之后,该基准就处于数列的中间位置。成为分区操作
// 3.递归的把小于基准值元素的子数列和大于基准值元素的子数列排序

// 如何把递归去掉?

export function quickSort(arr: number[]): number[] {
// 如果数组长度小于等于1,直接返回
if (arr.length <= 1) {
return arr
}
// 选择一个基准
const pivotIndex = Math.floor(arr.length / 2)
// 将基准与原数组分离
const pivot = arr.splice(pivotIndex, 1)[0]
// 定义左右两个空数组用来存放左右分区
const left = []
const right = []
for (const item of arr) {
if (item < pivot) {
left.push(item)
} else {
right.push(item)
}
}
return quickSort(left).concat([pivot], quickSort(right))
}
Loading