Skip to content

Commit 005855d

Browse files
committed
Add basic algo sorting
1 parent 1c3c7e2 commit 005855d

File tree

19 files changed

+706
-0
lines changed

19 files changed

+706
-0
lines changed

src/algorithms/sorting/Sort.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import Comparator from '../../utils/Comparator';
2+
3+
/**
4+
* @typedef {Object} SorterCallbacks
5+
* @property {function(a: *, b: *)} compareCallback - Nếu được cung cấp,
6+
* thì tất cả phần tử sẽ được so sánh thông qua lệnh callback.
7+
* @property {function(a: *)} visitingCallback - Nếu được cung cấp,
8+
* thì nó sẽ được gọi mỗi khi hàm sắp xếp truy cập vào phần tử tiếp theo.
9+
*/
10+
11+
export default class Sort {
12+
constructor(originalCallbacks) {
13+
this.callbacks = Sort.initSortingCallbacks(originalCallbacks);
14+
this.comparator = new Comparator(this.callbacks.compareCallback);
15+
}
16+
17+
/**
18+
* @param {SorterCallbacks} originalCallbacks
19+
* @returns {SorterCallbacks}
20+
*/
21+
static initSortingCallbacks(originalCallbacks) {
22+
const callbacks = originalCallbacks || {};
23+
const stubCallback = () => { };
24+
25+
callbacks.compareCallback = callbacks.compareCallback || undefined;
26+
callbacks.visitingCallback = callbacks.visitingCallback || stubCallback;
27+
28+
return callbacks;
29+
}
30+
31+
sort() {
32+
throw new Error('sort method must be implemented');
33+
}
34+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
export const sortedArr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20];
2+
export const reverseArr = [20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1];
3+
export const notSortedArr = [15, 8, 5, 12, 10, 1, 16, 9, 11, 7, 20, 3, 2, 6, 17, 18, 4, 13, 14, 19];
4+
export const equalArr = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1];
5+
export const negativeArr = [-1, 0, 5, -10, 20, 13, -7, 3, 2, -3];
6+
export const negativeArrSorted = [-10, -7, -3, -1, 0, 2, 3, 5, 13, 20];
7+
8+
export class SortTester {
9+
static testSort(SortingClass) {
10+
const sorter = new SortingClass();
11+
12+
expect(sorter.sort([])).toEqual([]);
13+
expect(sorter.sort([1])).toEqual([1]);
14+
expect(sorter.sort([1, 2])).toEqual([1, 2]);
15+
expect(sorter.sort([2, 1])).toEqual([1, 2]);
16+
expect(sorter.sort([3, 4, 2, 1, 0, 0, 4, 3, 4, 2])).toEqual([0, 0, 1, 2, 2, 3, 3, 4, 4, 4]);
17+
expect(sorter.sort(sortedArr)).toEqual(sortedArr);
18+
expect(sorter.sort(reverseArr)).toEqual(sortedArr);
19+
expect(sorter.sort(notSortedArr)).toEqual(sortedArr);
20+
expect(sorter.sort(equalArr)).toEqual(equalArr);
21+
}
22+
23+
static testNegativeNumbersSort(SortingClass) {
24+
const sorter = new SortingClass();
25+
expect(sorter.sort(negativeArr)).toEqual(negativeArrSorted);
26+
}
27+
28+
static testSortWithCustomComparator(SortingClass) {
29+
const callbacks = {
30+
compareCallback: (a, b) => {
31+
if (a.length === b.length) {
32+
return 0;
33+
}
34+
return a.length < b.length ? -1 : 1;
35+
},
36+
};
37+
38+
const sorter = new SortingClass(callbacks);
39+
40+
expect(sorter.sort([''])).toEqual(['']);
41+
expect(sorter.sort(['a'])).toEqual(['a']);
42+
expect(sorter.sort(['aa', 'a'])).toEqual(['a', 'aa']);
43+
expect(sorter.sort(['aa', 'q', 'bbbb', 'ccc'])).toEqual(['q', 'aa', 'ccc', 'bbbb']);
44+
expect(sorter.sort(['aa', 'aa'])).toEqual(['aa', 'aa']);
45+
}
46+
47+
static testSortStability(SortingClass) {
48+
const callbacks = {
49+
compareCallback: (a, b) => {
50+
if (a.length === b.length) {
51+
return 0;
52+
}
53+
return a.length < b.length ? -1 : 1;
54+
},
55+
};
56+
57+
const sorter = new SortingClass(callbacks);
58+
59+
expect(sorter.sort(['bb', 'aa', 'c'])).toEqual(['c', 'bb', 'aa']);
60+
expect(sorter.sort(['aa', 'q', 'a', 'bbbb', 'ccc'])).toEqual(['q', 'a', 'aa', 'ccc', 'bbbb']);
61+
}
62+
63+
static testAlgorithmTimeComplexity(SortingClass, arrayToBeSorted, numberOfVisits) {
64+
const visitingCallback = jest.fn();
65+
const callbacks = { visitingCallback };
66+
const sorter = new SortingClass(callbacks);
67+
68+
sorter.sort(arrayToBeSorted);
69+
70+
expect(visitingCallback).toHaveBeenCalledTimes(numberOfVisits);
71+
}
72+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import Sort from '../Sort';
2+
3+
export default class BubbleSort extends Sort {
4+
sort(originalArray) {
5+
// Gắn cờ lưu giữ thông tin về việc hoán đổi có xảy ra hay không.
6+
let swapped = false;
7+
// Sao chép mảng ban đầu để tránh việc thay đổi nó.
8+
const array = [...originalArray];
9+
10+
for (let i = 1; i < array.length; i += 1) {
11+
swapped = false;
12+
13+
// Gọi hàm callback.
14+
this.callbacks.visitingCallback(array[i]);
15+
16+
for (let j = 0; j < array.length - i; j += 1) {
17+
// Gọi hàm callback.
18+
this.callbacks.visitingCallback(array[j]);
19+
20+
// Hoán đổi phần tử nếu nó sai thứ tự.
21+
if (this.comparator.lessThan(array[j + 1], array[j])) {
22+
[array[j], array[j + 1]] = [array[j + 1], array[j]];
23+
24+
// Đánh dấu đã đổi.
25+
swapped = true;
26+
}
27+
}
28+
29+
// Nếu không có hoán đổi thì mảng đã được sắp xếp và không cần tiếp tục.
30+
if (!swapped) {
31+
return array;
32+
}
33+
}
34+
35+
return array;
36+
}
37+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Sắp xếp nổi bọt
2+
3+
Sắp xếp nổi bọt là một thuật toán sắp xếp đơn giản bằng việc lặp đi lặp các bước so sánh các phần tử liền kề và hoán đổi chúng nếu chúng không theo đúng thứ tự (sắp xếp tăng dần hoặc giảm dần). Các hành động này được lặp lại cho đến khi không cần phải hoán đổi nữa, tức là lúc này danh sách đã được sắp xếp.
4+
5+
![Algorithm Visualization](https://upload.wikimedia.org/wikipedia/commons/c/c8/Bubble-sort-example-300px.gif)
6+
7+
## Độ phức tạp
8+
9+
| Name | Best | Average | Worst | Memory | Stable | Comments |
10+
| --------------------- | :-------------: | :-----------------: | :-----------------: | :-------: | :-------: | :-------- |
11+
| **Bubble sort** | n | n<sup>2</sup> | n<sup>2</sup> | 1 | Yes | |
12+
13+
## Liên kết
14+
15+
- [Wikipedia](https://en.wikipedia.org/wiki/Bubble_sort)
16+
- [YouTube](https://www.youtube.com/watch?v=6Gv8vg0kcHc&index=27&t=0s&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import Sort from '../Sort';
2+
3+
export default class CountingSort extends Sort {
4+
/**
5+
* @param {number[]} originalArray
6+
* @param {number} [smallestElement]
7+
* @param {number} [biggestElement]
8+
*/
9+
sort(originalArray, smallestElement = undefined, biggestElement = undefined) {
10+
// Khởi tạo phần tử lớn nhất và nhỏ nhất trong mảng để xây dựng mảng bucker số sau này.
11+
let detectedSmallestElement = smallestElement || 0;
12+
let detectedBiggestElement = biggestElement || 0;
13+
14+
if (smallestElement === undefined || biggestElement === undefined) {
15+
originalArray.forEach((element) => {
16+
// Truy cập phần tử.
17+
this.callbacks.visitingCallback(element);
18+
19+
// Phát hiện phần tử lớn nhất.
20+
if (this.comparator.greaterThan(element, detectedBiggestElement)) {
21+
detectedBiggestElement = element;
22+
}
23+
24+
// Phát hiện phần tử nhỏ nhất.
25+
if (this.comparator.lessThan(element, detectedSmallestElement)) {
26+
detectedSmallestElement = element;
27+
}
28+
});
29+
}
30+
31+
// Khởi tạo mảng bucket.
32+
// Mảng này sẽ giữ tần suất của mỗi số từ originalArray.
33+
const buckets = Array(detectedBiggestElement - detectedSmallestElement + 1).fill(0);
34+
35+
originalArray.forEach((element) => {
36+
// Truy cập phần tử.
37+
this.callbacks.visitingCallback(element);
38+
39+
buckets[element - detectedSmallestElement] += 1;
40+
});
41+
42+
// Add previous frequencies to the current one for each number in bucket
43+
// to detect how many numbers less then current one should be standing to
44+
// the left of current one.
45+
for (let bucketIndex = 1; bucketIndex < buckets.length; bucketIndex += 1) {
46+
buckets[bucketIndex] += buckets[bucketIndex - 1];
47+
}
48+
49+
// Now let's shift frequencies to the right so that they show correct numbers.
50+
// I.e. if we won't shift right than the value of buckets[5] will display how many
51+
// elements less than 5 should be placed to the left of 5 in sorted array
52+
// INCLUDING 5th. After shifting though this number will not include 5th anymore.
53+
buckets.pop();
54+
buckets.unshift(0);
55+
56+
// Now let's assemble sorted array.
57+
const sortedArray = Array(originalArray.length).fill(null);
58+
for (let elementIndex = 0; elementIndex < originalArray.length; elementIndex += 1) {
59+
// Get the element that we want to put into correct sorted position.
60+
const element = originalArray[elementIndex];
61+
62+
// Visit element.
63+
this.callbacks.visitingCallback(element);
64+
65+
// Get correct position of this element in sorted array.
66+
const elementSortedPosition = buckets[element - detectedSmallestElement];
67+
68+
// Put element into correct position in sorted array.
69+
sortedArray[elementSortedPosition] = element;
70+
71+
// Increase position of current element in the bucket for future correct placements.
72+
buckets[element - detectedSmallestElement] += 1;
73+
}
74+
75+
// Return sorted array.
76+
return sortedArray;
77+
}
78+
}

src/algorithms/sorting/counting-sort/README.md

Whitespace-only changes.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import Sort from '../Sort';
2+
import MinHeap from '../../../data-structures/heap/MinHeap';
3+
4+
export default class HeapSort extends Sort {
5+
sort(originalArray) {
6+
const sortedArray = [];
7+
const minHeap = new MinHeap(this.callbacks.compareCallback);
8+
9+
// Chèn tất cả phần tử trong mảng vào heap.
10+
originalArray.forEach((element) => {
11+
// Gọi hàm callback.
12+
this.callbacks.visitingCallback(element);
13+
14+
minHeap.add(element);
15+
});
16+
17+
// Giờ ta có min heap với phần tử nhỏ nhất luôn ở trên cùng.
18+
// Kiểm tra vòng từng phần tử cực tiểu và tạo thành mảng đã sắp xếp.
19+
while (!minHeap.isEmpty()) {
20+
const nextMinElement = minHeap.poll();
21+
22+
// Gọi hàm callback.
23+
this.callbacks.visitingCallback(nextMinElement);
24+
25+
sortedArray.push(nextMinElement);
26+
}
27+
28+
return sortedArray;
29+
}
30+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Sắp xếp vun đống
2+
3+
Sắp xếp vun đống thuộc loại sắp xếp so sánh.
4+
Sắp xếp vun đống có thể coi là một cải tiến của sắp xếp chọn: nó chia đầu vào thành vùng đã sắp xếp và vùng chưa được sắp xếp, sau đó thu nhỏ vùng chưa sắp xếp bằng cách lấy phần tử lớn nhất và thêm nó vào mảng đã sắp xếp. Lặp đi lặp lại nhiều lần để có được mảng đã sắp xếp hoàn toàn. Cải tiến ở đây nằm ở việc sử dụng cấu trúc dữ liệu heap chứ vẫn là tìm kiếm giá trị lớn nhất theo thời gian tuyến tính.
5+
6+
![Algorithm Visualization](https://upload.wikimedia.org/wikipedia/commons/1/1b/Sorting_heapsort_anim.gif)
7+
8+
![Algorithm Visualization](https://upload.wikimedia.org/wikipedia/commons/4/4d/Heapsort-example.gif)
9+
10+
## Độ phức tạp
11+
12+
| Name | Best | Average | Worst | Memory | Stable | Comments |
13+
| --------------------- | :-------------: | :-----------------: | :-----------------: | :-------: | :-------: | :-------- |
14+
| **Heap sort** | n&nbsp;log(n) | n&nbsp;log(n) | n&nbsp;log(n) | 1 | No | |
15+
16+
## Liên kết
17+
18+
[Wikipedia](https://en.wikipedia.org/wiki/Heapsort)
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import Sort from '../Sort';
2+
3+
export default class InsertionSort extends Sort {
4+
sort(originalArray) {
5+
const array = [...originalArray];
6+
7+
// Đi qua tất cả phần tử của mảng...
8+
for (let i = 1; i < array.length; i += 1) {
9+
let currentIndex = i;
10+
11+
// Gọi hàm callback.
12+
this.callbacks.visitingCallback(array[i]);
13+
14+
// Kiểm tra xem phần tử trước đó có lớn hơn phần tử hiện tại hay không.
15+
// Nếu có, hãy hoán đổi hai phần tử.
16+
while (
17+
array[currentIndex - 1] !== undefined
18+
&& this.comparator.lessThan(array[currentIndex], array[currentIndex - 1])
19+
) {
20+
// Gọi hàm callback.
21+
this.callbacks.visitingCallback(array[currentIndex - 1]);
22+
23+
// Hoán đổi phần tử.
24+
[
25+
array[currentIndex - 1],
26+
array[currentIndex],
27+
] = [
28+
array[currentIndex],
29+
array[currentIndex - 1],
30+
];
31+
32+
// Chuyển sang chỉ mục bên trái.
33+
currentIndex -= 1;
34+
}
35+
}
36+
37+
return array;
38+
}
39+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Sắp xếp chèn
2+
3+
Sắp xếp chèn là thuật toán sắp xếp đơn giản để xây dựng một mảng (danh sách) cuối cùng bằng việc thêm từng mục tại một thời điểm. Nó kém hiệu quả hơn nhiều trên các danh sách lớn so với các thuật toán nâng cao hơn như quicksort, heapsort hoặc merge sort.
4+
5+
![Algorithm Visualization](https://upload.wikimedia.org/wikipedia/commons/4/42/Insertion_sort.gif)
6+
7+
![Algorithm Visualization](https://upload.wikimedia.org/wikipedia/commons/0/0f/Insertion-sort-example-300px.gif)
8+
9+
## Độ phức tạp
10+
11+
| Name | Best | Average | Worst | Memory | Stable | Comments |
12+
| --------------------- | :-------------: | :-----------------: | :-----------------: | :-------: | :-------: | :-------- |
13+
| **Insertion sort** | n | n<sup>2</sup> | n<sup>2</sup> | 1 | Yes | |
14+
15+
## Liên kết
16+
17+
[Wikipedia](https://en.wikipedia.org/wiki/Insertion_sort)

0 commit comments

Comments
 (0)