Skip to content

Commit dc5e9cb

Browse files
committed
complete algo sorting
1 parent 005855d commit dc5e9cb

File tree

6 files changed

+240
-18
lines changed

6 files changed

+240
-18
lines changed

src/algorithms/sorting/counting-sort/CountingSort.js

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export default class CountingSort extends Sort {
77
* @param {number} [biggestElement]
88
*/
99
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.
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 sau này.
1111
let detectedSmallestElement = smallestElement || 0;
1212
let detectedBiggestElement = biggestElement || 0;
1313

@@ -29,7 +29,7 @@ export default class CountingSort extends Sort {
2929
}
3030

3131
// 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.
32+
// Mảng này sẽ giữ tần số của mỗi số từ originalArray.
3333
const buckets = Array(detectedBiggestElement - detectedSmallestElement + 1).fill(0);
3434

3535
originalArray.forEach((element) => {
@@ -39,40 +39,40 @@ export default class CountingSort extends Sort {
3939
buckets[element - detectedSmallestElement] += 1;
4040
});
4141

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.
42+
// Thêm tần số trước đó vào tần số hiện tại cho mỗi số trong bucket
43+
// để phát hiện có bao nhiêu số nhỏ hơn số hiện tại thì chúng sẽ đứng
44+
// bên trái của số hiện tại.
4545
for (let bucketIndex = 1; bucketIndex < buckets.length; bucketIndex += 1) {
4646
buckets[bucketIndex] += buckets[bucketIndex - 1];
4747
}
4848

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.
49+
// Giờ ta hãy chuyển các tần số sang bên phải để chúng hiển thị các số chính xác.
50+
// Vd: nếu ta không dịch chuyển sang phải, thì các giá trị của buckets[5] sẽ hiển thị
51+
// số phần tử nhỏ hơn 5 được đặt bên trái của 5 trong mảng đã sắp xếp KỂ CẢ 5
52+
// Sau khi dịch chuyển, con số này sẽ không bao gồm 5 nữa.
5353
buckets.pop();
5454
buckets.unshift(0);
5555

56-
// Now let's assemble sorted array.
56+
// Bây giờ ta sẽ sắp xếp mảng.
5757
const sortedArray = Array(originalArray.length).fill(null);
5858
for (let elementIndex = 0; elementIndex < originalArray.length; elementIndex += 1) {
59-
// Get the element that we want to put into correct sorted position.
59+
// Lấy phần tử mà ta muốn đặt vào đúng vị trí đã sắp xếp.
6060
const element = originalArray[elementIndex];
6161

62-
// Visit element.
62+
// Truy cập phần tử.
6363
this.callbacks.visitingCallback(element);
6464

65-
// Get correct position of this element in sorted array.
65+
// Lấy vị trí đúng của phần tử trong mảng đã sắp xếp.
6666
const elementSortedPosition = buckets[element - detectedSmallestElement];
6767

68-
// Put element into correct position in sorted array.
68+
// Đặt phần tử vào vị trí đúng trong mảng đã sắp xếp
6969
sortedArray[elementSortedPosition] = element;
7070

71-
// Increase position of current element in the bucket for future correct placements.
71+
// Tăng vị trí của phần tử hiện tại trong bucket cho các vị trí đúng trong tương lai.
7272
buckets[element - detectedSmallestElement] += 1;
7373
}
7474

75-
// Return sorted array.
75+
// Trả về mảng đã sắp xếp.
7676
return sortedArray;
7777
}
7878
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Sắp xếp đếm phân phối
2+
3+
Trong khoa học máy tính, **sắp xếp đếm phân phối** là một giải thuật để sắp xếp một tập hợp các đối tượng có khoá là các số nguyên nhỏ, tức là một giải thuật sắp xếp số nguyên. Nó hoạt động bằng cách đếm số lượng đối tượng có giá trị khoá riêng biệt và sử dụng tính nhẩm trên các số đếm đó để xác định vị trí của từng giá trị khóa trong chuỗi đầu ra.
4+
5+
Thời gian thực thi của nó là tuyến tính trên số lượng mục, vì vậy nó chỉ thích hợp sử dụng trong các tình hướng mà sự thay đổi về số lượng khoá không lớn hơn đáng kể với số lượng mục. Tuy nhiên, nó thường được dùng như một chương trình con trong một giải thuật sắp xếp khác, như *sắp xếp theo cơ số*, có thể xử lý các khoá lớn hơn một cách hiệu quả.
6+
7+
Bởi vì sắp xếp đếm phân phối sử dụng các giá trị khóa làm chỉ mục cho mảng, nó không thuộc loại sắp xếp so sánh và cận dưới `Ω (n log n)` nên sắp xếp so sánh không áp dụng cho nó. Sắp xếp theo nhóm(bucket) có thể được sử dụng cho nhiều tác vụ tương tự như sắp xếp đếm phân phối, với phân tích thời gian tương tự; tuy nhiên, so với sắp xếp đếm, sắp xếp theo nhóm yêu cầu danh sách được liên kết, mảng động hoặc một lượng lớn bộ nhớ được phân bổ trước để chứa các tập hợp mục trong mỗi nhóm, trong khi sắp xếp đếm phân đó chỉ cần lưu trữ một số duy nhất (số mục) trên mỗi nhóm.
8+
9+
Sắp xếp đếm phân phối hoạt động tốt nhất khi phạm vi số cho mỗi phần tử trong mảng là rất nhỏ.
10+
11+
# Giải thuật
12+
13+
**Bước 1**
14+
15+
Trong bước đầu tiên, ta tính tổng số tất cả các phần tử của mảng đầu vào `A`. Sau đó lưu kết quả vào mảng đếm `C`. Cách đếm được mô tả dưới đây.
16+
17+
![Counting Sort](https://3.bp.blogspot.com/-jJchly1BkTc/WLGqCFDdvCI/AAAAAAAAAHA/luljAlz2ptMndIZNH0KLTTuQMNsfzDeFQCLcB/s1600/CSortUpdatedStepI.gif)
18+
19+
**Bước 2**
20+
21+
Trong bước thứ hai, ta tính toán có bao nhiêu phần tử tồn tại trong mảng đầu vào `A` nhỏ hơn hoặc bằng với chỉ số đã cho.
22+
23+
`Ci` = số phần tử nhỏ hơn hoặc bằng `i` trong mảng đầu vào.
24+
25+
![Counting Sort](https://1.bp.blogspot.com/-1vFu-VIRa9Y/WLHGuZkdF3I/AAAAAAAAAHs/8jKu2dbQee4ap9xlVcNsILrclqw0UxAVACLcB/s1600/Step-II.png)
26+
27+
**Bước 3**
28+
29+
Trong bước này, ta đặt phần tử mảng đầu vào `A` ở vị trí đã sắp xếp bằng cách sử dụng trợ giúp của mảng đếm `C`, tức là những gì ta đã xây dựng ở bước hai. Chúng ta đã sử dụng mảng kết quả `B` để lưu trữ các phần tử đã được sắp xếp. Ở đây ta đã xử lý chỉ mục của `B` bắt đầu từ số không.
30+
31+
![Counting Sort](https://1.bp.blogspot.com/-xPqylngqASY/WLGq3p9n9vI/AAAAAAAAAHM/JHdtXAkJY8wYzDMBXxqarjmhpPhM0u8MACLcB/s1600/ResultArrayCS.gif)
32+
33+
## Độ phức tạp
34+
35+
| Name | Best | Average | Worst | Memory | Stable | Comments |
36+
| --------------------- | :-------------: | :-----------------: | :-----------------: | :-------: | :-------: | :-------- |
37+
| **Counting sort** | n + r | n + r | n + r | n + r | Yes | r - biggest number in array |
38+
39+
## Liên kết
40+
41+
- [Wikipedia](https://en.wikipedia.org/wiki/Counting_sort)
42+
- [YouTube](https://www.youtube.com/watch?v=OKd534EWcdk&index=61&t=0s&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
43+
- [EfficientAlgorithms](https://efficientalgorithms.blogspot.com/2016/09/lenear-sorting-counting-sort.html)
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Sắp xếp theo cơ số
2+
3+
Trong khoa học máy tính, **sắp xếp theo cơ số** là một thuật toán sắp xếp không so sánh. Thay vào đó nó sắp xếp dữ liệu với các khoá số nguyên bằng cách nhóm các khoá theo các chữ số riêng lẻ có cùng vị trí và giá trị. Cần có ký hiệu cho từng vị trí, nhưng vì số nguyên có thể đại diện cho chuỗi ký từ (vd: tên hoặc ngày tháng) và cả số dấu phẩy động được định dạng đặc biệt, nên sắp xếp cơ số không giới hạn ở số nguyên.
4+
5+
*Ý nghĩa của tên gọi ?*
6+
7+
Trong các hệ đếm toán học, *cơ số* là số các chữ số của hệ đếm, bao gồm cả số 0 được dùng để biểu diễn số trong hệ đếm. Ví dụ, với hệ thập phân (hệ đếm sử dụng phổ biến nhất hiện này) cơ số là 10, vì hệ đếm này dùng mười chữ số từ 0 đến 9.
8+
9+
## Hiệu quả
10+
11+
Vấn đề về hiệu năng của sắp xếp cơ số so với các thuật toán sắp xếp khác khá là phức tạp và có nhiều hiểu nhầm. Việc sắp xếp theo cơ số có hiệu quả kém hơn, bằng hay tốt hơn các thuật toán sắp xếp so sánh phụ thuộc vào từng vấn đề được đưa ra. Độ phức tạp sắp xếp theo cơ số là `O (wn)` với `n` là số lượng khoá và `w` là kích thước khoá. Đôi khi `w` được biểu diễn là một hàng số, điều này giúp sắp xếp thuận lợi hơn (với `n` đủ lớn) so với các thuật toán sắp xếp so sánh tốt nhất, tất cả đều thực hiện các phép so sánh `O (n log n)` để sắp xếp `n` chìa khóa. Tuy nhiên `w` vẫn không phải là hằng số: nếu tất cả các khóa` n` là khác biệt, thì `w` phải có ít nhất `log n` để máy truy cập ngẫu nhiên có thể lưu trữ chúng trong bộ nhớ, cung cấp độ phức tạp thời gian tốt nhất là `O (n log n)`. Điều đó dường như làm cho sắp xếp theo cơ số có hiệu quả không kém so với sắp xếp so sánh trong trường hợp tốt nhất (và tệ hơn nếu các khóa dài hơn nhiều so với `log n`)
12+
13+
![Radix Sort](https://www.researchgate.net/publication/291086231/figure/fig1/AS:614214452404240@1523451545568/Simplistic-illustration-of-the-steps-performed-in-a-radix-sort-In-this-example-the.png)
14+
15+
## Độ phức tạp
16+
17+
| Name | Best | Average | Worst | Memory | Stable | Comments |
18+
| --------------------- | :-------------: | :-----------------: | :-----------------: | :-------: | :-------: | :-------- |
19+
| **Radix sort** | n * k | n * k | n * k | n + k | Yes | k - length of longest key |
20+
21+
## Liên kết
22+
23+
- [Wikipedia](https://en.wikipedia.org/wiki/Radix_sort)
24+
- [YouTube](https://www.youtube.com/watch?v=XiuSW_mEn7g&index=62&t=0s&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
25+
- [ResearchGate](https://www.researchgate.net/figure/Simplistic-illustration-of-the-steps-performed-in-a-radix-sort-In-this-example-the_fig1_291086231)
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
import Sort from '../Sort';
2+
3+
// Sử dụng charCode (a = 97, b = 98, v.v.), ta có thể ánh xạ ký tự thành các nhóm từ 0 - 25
4+
const BASE_CHAR_CODE = 97;
5+
const NUMBER_OF_POSSIBLE_DIGITS = 10;
6+
const ENGLISH_ALPHABET_LENGTH = 26;
7+
8+
export default class RadixSort extends Sort {
9+
/**
10+
* @param {*[]} originalArray
11+
* @return {*[]}
12+
*/
13+
sort(originalArray) {
14+
// Giả sử tất cả các phần tử của mảng có cùng kiểu
15+
const isArrayOfNumbers = this.isArrayOfNumbers(originalArray);
16+
17+
let sortedArray = [...originalArray];
18+
const numPasses = this.determineNumPasses(sortedArray);
19+
20+
for (let currentIndex = 0; currentIndex < numPasses; currentIndex += 1) {
21+
const buckets = isArrayOfNumbers
22+
? this.placeElementsInNumberBuckets(sortedArray, currentIndex)
23+
: this.placeElementsInCharacterBuckets(sortedArray, currentIndex, numPasses);
24+
25+
// Làm bẹt các bucket thành sortedArray và lặp lại ở chỉ mục tiếp theo
26+
sortedArray = buckets.reduce((acc, val) => {
27+
return [...acc, ...val];
28+
}, []);
29+
}
30+
31+
return sortedArray;
32+
}
33+
34+
/**
35+
* @param {*[]} array
36+
* @param {number} index
37+
* @return {*[]}
38+
*/
39+
placeElementsInNumberBuckets(array, index) {
40+
// Xem bên dưới. Chúng được sử dụng để xác định chữ số nào sẽ sử dụng để phân bổ bucket
41+
const modded = 10 ** (index + 1);
42+
const divided = 10 ** index;
43+
const buckets = this.createBuckets(NUMBER_OF_POSSIBLE_DIGITS);
44+
45+
array.forEach((element) => {
46+
this.callbacks.visitingCallback(element);
47+
if (element < divided) {
48+
buckets[0].push(element);
49+
} else {
50+
/**
51+
* Giả sử ta có phần tử là 1,052 và hiện đang ở chỉ mục 1 (bắt đầu từ 0).
52+
* Điều này có nghĩa là ta sử dụng '5' làm nhóm. `modded` sẽ là 10 ** (1 + 1), là 100.
53+
* Vì vậy, ta lấy 1,052% 100 (52) và chia nó cho 10 (5,2) và làm tròn nó (5).
54+
*/
55+
const currentDigit = Math.floor((element % modded) / divided);
56+
buckets[currentDigit].push(element);
57+
}
58+
});
59+
60+
return buckets;
61+
}
62+
63+
/**
64+
* @param {*[]} array
65+
* @param {number} index
66+
* @param {number} numPasses
67+
* @return {*[]}
68+
*/
69+
placeElementsInCharacterBuckets(array, index, numPasses) {
70+
const buckets = this.createBuckets(ENGLISH_ALPHABET_LENGTH);
71+
72+
array.forEach((element) => {
73+
this.callbacks.visitingCallback(element);
74+
const currentBucket = this.getCharCodeOfElementAtIndex(element, index, numPasses);
75+
buckets[currentBucket].push(element);
76+
});
77+
78+
return buckets;
79+
}
80+
81+
/**
82+
* @param {string} element
83+
* @param {number} index
84+
* @param {number} numPasses
85+
* @return {number}
86+
*/
87+
getCharCodeOfElementAtIndex(element, index, numPasses) {
88+
// Đặt phần tử vào bucket cuối cùng nếu chưa sẵn sàng để sắp xếp.
89+
if ((numPasses - index) > element.length) {
90+
return ENGLISH_ALPHABET_LENGTH - 1;
91+
}
92+
93+
/**
94+
* Nếu mỗi ký tự đã được sắp xếp, hãy sử dụng ký tự đầu tiên để xác định bucket,
95+
* nếu không thì lặp lại qua các phần tử.
96+
*/
97+
const charPos = index > element.length - 1 ? 0 : element.length - index - 1;
98+
99+
return element.toLowerCase().charCodeAt(charPos) - BASE_CHAR_CODE;
100+
}
101+
102+
/**
103+
* Số lần vượt qua được xác định bởi độ dài của phần tử dài nhất trong mảng.
104+
* Đối với số nguyên, là log10(num) và đối với chuỗi, sẽ là độ dài của chuỗi.
105+
*/
106+
determineNumPasses(array) {
107+
return this.getLengthOfLongestElement(array);
108+
}
109+
110+
/**
111+
* @param {*[]} array
112+
* @return {number}
113+
*/
114+
getLengthOfLongestElement(array) {
115+
if (this.isArrayOfNumbers(array)) {
116+
return Math.floor(Math.log10(Math.max(...array))) + 1;
117+
}
118+
119+
return array.reduce((acc, val) => {
120+
return val.length > acc ? val.length : acc;
121+
}, -Infinity);
122+
}
123+
124+
/**
125+
* @param {*[]} array
126+
* @return {boolean}
127+
*/
128+
isArrayOfNumbers(array) {
129+
// Giả sử tất cả các phần tử của mảng có cùng kiểu
130+
return this.isNumber(array[0]);
131+
}
132+
133+
/**
134+
* @param {number} numBuckets
135+
* @return {*[]}
136+
*/
137+
createBuckets(numBuckets) {
138+
/**
139+
* Việc ánh xạ các bucket vào một mảng thay vì lấp đầy chúng bằng một mảng,
140+
* nhằm ngăn không cho mỗi bucket chứa một tham chiếu đến cùng một mảng.
141+
*/
142+
return new Array(numBuckets).fill(null).map(() => []);
143+
}
144+
145+
/**
146+
* @param {*} element
147+
* @return {boolean}
148+
*/
149+
isNumber(element) {
150+
return Number.isInteger(element);
151+
}
152+
}

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
# Shell Sort
22

3-
Shell Sort còn gọi là phương thức Shell là loại sắp xếp so sánh tại chỗ. Nó có thể được xem là tổng quát của sắp xếp nổi bọt (bubble sort) hoặc sắp xếp chèn (insertion sort). Giải thuật này tránh các trường hợp phải hoán đổi vị trí của hai phần tử xa nhau trong giải thuật sắp xếp chọn. Phương pháp bắt đầu bằng cách sắp xếp các cặp phần tử cách xa nhau, sau đó giảm dần khoảng cách giữa các phần tử được so sánh. Bắt đầu với các phần tử xa nhau, nó có thể di chuyển một số phần tử không đúng vị trí vào vị trí nhanh hơn so với hoán đổi các phần tử liền kề.
3+
Shell Sort còn gọi là phương thức Shell là loại sắp xếp so sánh tại chỗ. Nó có thể được xem là tổng quát của sắp xếp nổi bọt (bubble sort) hoặc sắp xếp chèn (insertion sort). Giải thuật này tránh các trường hợp phải tráo đổi vị trí của hai phần tử xa nhau trong giải thuật sắp xếp chọn (nếu như phần tử nhỏ hơn ở vị trí bên phải khá xa so với phần tử lớn hơn bên trái).
44

55
![Shellsort](https://upload.wikimedia.org/wikipedia/commons/d/d8/Sorting_shellsort_anim.gif)
66

77
## Hoạt động
88

9+
Khi bắt đầu, giải thuật này sử dụng giải thuật sắp xếp chọn trên các phần tử có khoảng cách xa nhau, sau đó sắp xếp các phần tử có khoảng cách hẹp hơn. Khoảng cách này còn được gọi là **khoảng (interval)**.
10+
911
Lấy ví dụ dễ hiểu, ta lấy một khoảng là `4`. Tạo một danh sách con ảo gồm tất cả các giá trị nằm trong khoảng 4 vị trí. Ở đây các giá trị này là `{35, 14}`, `{33, 19}`, `{42, 27}``{10, 44}`.
1012

1113
![Shellsort](https://www.tutorialspoint.com/data_structures_algorithms/images/shell_sort_gap_4.jpg)

src/algorithms/sorting/shell-sort/ShellSort.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export default class ShellSort extends Sort {
88
// Xác định khoảng cách gap.
99
let gap = Math.floor(array.length / 2);
1010

11-
// Khi gap lớn hơn thì 0 thực hiện so sánh và hoán đổi các phần tử.
11+
// Khi gap lớn hơn 0 thì thực hiện so sánh và hoán đổi các phần tử.
1212
while (gap > 0) {
1313
// Đi đến và so sánh tất cả các cặp phần tử ở xa.
1414
for (let i = 0; i < (array.length - gap); i += 1) {

0 commit comments

Comments
 (0)