├── .gitignore
├── .prettierignore
├── README.md
├── algorithms
└── sorts
│ ├── bubble-sort.js
│ ├── heap-sort.js
│ ├── index.js
│ ├── insertion-sort.js
│ ├── merge-sort.js
│ ├── odd-even-sort.js
│ ├── quick-sort.js
│ ├── radix-sort.js
│ ├── selection-sort.js
│ └── stupid-sort.js
├── assets
├── icons
│ ├── favicon.png
│ └── logo.png
└── images
│ └── sort
│ ├── max-heap.jpg
│ ├── merge-sort-illustration.jpg
│ ├── radix-lsd.jpg
│ └── rebuild-max-heap.jpg
├── index.html
├── scripts
├── contants.js
├── helper.js
├── main.js
├── menu.js
└── sort-visualizer
│ ├── bubble-sort.js
│ ├── constant.js
│ ├── heap-sort.js
│ ├── index.js
│ ├── insertion-sort.js
│ ├── merge-sort.js
│ ├── odd-even-sort.js
│ ├── prepare-sort.js
│ ├── quick-sort.js
│ ├── radix-sort.js
│ ├── selection-sort.js
│ └── stupid-sort.js
├── sort-visualizer.html
├── styles
├── css
│ ├── atomic.css
│ ├── home.css
│ ├── menu.css
│ ├── reset.css
│ ├── sort-realtime.css
│ ├── sort-visualizer.css
│ └── utils.css
└── scss
│ ├── atomic.scss
│ ├── home.scss
│ ├── menu.scss
│ ├── mixin.scss
│ ├── sort-realtime.scss
│ ├── sort-visualizer.scss
│ └── utils.scss
└── vendors
├── jquery
└── jquery.min.js
└── prism
├── prism.min.css
└── prism.min.js
/.gitignore:
--------------------------------------------------------------------------------
1 | *.css.map
2 | node_modules/
3 | yarn.lock
4 | note.txt
5 | demo-modal.html
6 | mixin.css
7 | .env
8 | website/.gitignore
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | *.min.*
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # STUDY ALGORITHMS THROUGH DYNO VISUALIZER
4 |
5 |
6 |
7 | ## Live Demo: https://tuannguyen2504.github.io/dyno-visualizer/
8 |
--------------------------------------------------------------------------------
/algorithms/sorts/bubble-sort.js:
--------------------------------------------------------------------------------
1 | /*
2 | Đầu vào:
3 | - arr: mảng cần sắp xếp.
4 | - n: số lượng phần tử của mảng.
5 | - i: chỉ số index vòng lặp ngoài (0 -> n-1).
6 | - j: chỉ số vòng lặp trong (0 -> n - 1 - i)
7 |
8 | Ý tưởng:
9 | - Sử dụng 2 vòng lặp lồng nhau.
10 | - Vòng lặp bên ngoài để duyệt qua mảng.
11 | - Vòng lặp bên trong để đẩy phần tử max (min) lên đầu mảng (bubble).
12 | + Nếu phần tử sau > phần tử trước thì swap 2 phần tử đó (sắp tăng dần) và ngược lại.
13 | - Để tối ưu thì ta chỉ nên cho vòng lặp trong chạy đến (n - 1 - i) mà không cần đến (n - 1).
14 |
15 | */
16 |
17 | /*
18 | Độ phức tạp (BigO):
19 | - Best Case (Mảng đã sắp xếp): O(n).
20 | - Average: O (n^2)
21 | - Worst Case (Mảng sắp xếp ngươc): O(n^2).
22 | - Không gian biến phụ: O(1).
23 | Note:
24 | - Không nên dùng cho mảng kích thước lớn.
25 | - Chỉ nên sử dụng khi mảng đã sắp xếp như bảng Highscore...
26 | */
27 |
28 | function basicBubbleSort(arr = []) {
29 | const n = arr.length;
30 |
31 | // outer loop
32 | for (let i = 0; i < n - 1; ++i) {
33 | // inner loop
34 | for (let j = 0; j < n - 1; ++j) {
35 | if (arr[j] > arr[j + 1]) {
36 | // swap (*)
37 | [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
38 | }
39 | }
40 | }
41 |
42 | return arr;
43 | }
44 |
45 | function enhancedBubbleSort(arr = []) {
46 | const n = arr.length;
47 |
48 | // outer loop
49 | for (let i = 0; i < n - 1; ++i) {
50 | let swapped = false;
51 |
52 | // inner loop (Mỗi lần loop trừ đi 1)
53 | for (let j = 0; j < n - 1 - i; ++j) {
54 | if (arr[j] > arr[j + 1]) {
55 | // swap (*)
56 | [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
57 | swapped = true;
58 | }
59 | }
60 |
61 | // Nếu inner loop không có cặp nào swap thì ngưng (đã xếp xong)
62 | if (!swapped) break;
63 | }
64 |
65 | return arr;
66 | }
67 |
68 | module.exports = {
69 | enhancedBubbleSort,
70 | basicBubbleSort,
71 | };
72 |
--------------------------------------------------------------------------------
/algorithms/sorts/heap-sort.js:
--------------------------------------------------------------------------------
1 | /*
2 | Đầu vào:
3 | - arr: mảng cần sắp xếp.
4 | - n: số lượng phần tử của mảng.
5 |
6 | Ý tưởng:
7 | - Sử dụng tính chất của cấu trúc dữ liệu heap (max heap, min heap).
8 | - Đầu tiên xây dựng một max-heap (min-heap).
9 | - Hoán vị phần tử root của heap với phần tử cuối.
10 | - Xoá phần tử cuối ra khỏi heap.
11 | - Xây dựng lại heap tại phần tử root.
12 | */
13 |
14 | /*
15 | Độ phức tạp (BigO):
16 | - Best Case (Mảng đã sắp xếp): O(n*log(n)).
17 | - Average: O(n*log(n)).
18 | - Worst Case (Mảng sắp xếp ngươc): O(n*log(n)).
19 | - Không gian biến phụ: O(1).
20 | Note:
21 | - Thuật toán khá ổn định trong mọi trường hớp.
22 | */
23 |
24 | // @fn: Xây dựng max heap từ 1 node
25 | function maxHeapify(arr = [], len, nodeIndex) {
26 | // Nếu là node lá thì dừng
27 | if (nodeIndex > Math.floor(len / 2 - 1)) return;
28 |
29 | const leftNode = 2 * nodeIndex + 1,
30 | rightNode = 2 * nodeIndex + 2;
31 | let maxIndex = nodeIndex;
32 |
33 | // Tìm node lớn nhất trong 3 node
34 | if (leftNode < len && arr[leftNode] > arr[maxIndex]) maxIndex = leftNode;
35 | if (rightNode < len && arr[rightNode] > arr[maxIndex]) maxIndex = rightNode;
36 |
37 | // Hoán vị node cha hiện tại với con lớn nhất
38 | if (maxIndex !== nodeIndex) {
39 | [arr[maxIndex], arr[nodeIndex]] = [arr[nodeIndex], arr[maxIndex]];
40 |
41 | // Tiếp tục đệ quy kiểm tra tại node lớn nhất này
42 | maxHeapify(arr, len, maxIndex);
43 | }
44 | }
45 |
46 | // @fn: Xây dụng max-heap
47 | function buildMaxHeap(arr = []) {
48 | const len = arr.length;
49 | // Parent node cuối cùng
50 | const lastParentNode = Math.floor(len / 2 - 1);
51 |
52 | // Từ node cha cuối trở lên đều là parent node
53 | for (let i = lastParentNode; i >= 0; --i) maxHeapify(arr, len, i);
54 | }
55 |
56 | // @fn: Heap sort
57 | function heapSort(arr = []) {
58 | // Xây dựng max heap
59 | buildMaxHeap(arr);
60 |
61 | const len = arr.length;
62 | for (let i = len - 1; i >= 0; --i) {
63 | // Hoán vị node root với node cuối cùng
64 | [arr[0], arr[i]] = [arr[i], arr[0]];
65 |
66 | // Xây lại max heap tại vị trí root
67 | maxHeapify(arr, i, 0);
68 | }
69 | }
70 |
71 | module.exports = heapSort;
72 |
--------------------------------------------------------------------------------
/algorithms/sorts/index.js:
--------------------------------------------------------------------------------
1 | const bubbleSort = require('./bubble-sort');
2 | const selectionSort = require('./selection-sort');
3 | const insertionSort = require('./insertion-sort');
4 | const quickSort = require('./quick-sort');
5 | const mergeSort = require('./merge-sort');
6 | const heapSort = require('./heap-sort');
7 | const stupidSort = require('./stupid-sort');
8 | const radixSort = require('./radix-sort');
9 | const oddEvenSort = require('./odd-even-sort');
10 |
11 | // @fn: random a number array
12 | // type = 0: random, 1: ascending, 2: decending
13 | const generateRandomData = (length = 10, type = 0, max = 1000) => {
14 | let arr = [];
15 | for (let i = 0; i < length; ++i) arr.push(Math.round(Math.random() * max));
16 | switch (type) {
17 | case 1:
18 | arr.sort((a, b) => a - b);
19 | // swap 2 last postion => generate ~sorted list
20 | if (length > 1)
21 | [arr[length - 1], arr[length - 2]] = [arr[length - 2], arr[length - 1]];
22 | break;
23 | case 2:
24 | arr.sort((a, b) => b - a);
25 | if (length > 1)
26 | [arr[length - 1], arr[length - 2]] = [arr[length - 2], arr[length - 1]];
27 | break;
28 | default:
29 | break;
30 | }
31 | return arr;
32 | };
33 |
34 | // @initial data
35 | const len = 100_000;
36 | const randomArr = generateRandomData(len, 0);
37 | const ascenArr = generateRandomData(len, 1);
38 | const descenArr = generateRandomData(len, 2);
39 |
40 | // @fn: time logger
41 | const timeLogger = (title = 'timer', sortFn) => {
42 | let arr = [...randomArr];
43 | console.time(`${title} --> RANDOM`);
44 | sortFn(arr);
45 | console.timeEnd(`${title} --> RANDOM`);
46 |
47 | arr = [...ascenArr];
48 | console.time(`${title} --> ASCENDING`);
49 | sortFn(arr);
50 | console.timeEnd(`${title} --> ASCENDING`);
51 |
52 | arr = [...descenArr];
53 | console.time(`${title} --> DESCENDING`);
54 | sortFn(arr);
55 | console.timeEnd(`${title} --> DESCENDING`);
56 |
57 | console.log('---------------------------------------');
58 | };
59 |
60 | // @testing
61 | function testing() {
62 | console.log('LENGTH: ', len);
63 | console.log('---------------------------------------');
64 |
65 | // 1) NODEJS SORT
66 | // timeLogger('NODEJS SORT', function (arr) {
67 | // arr.sort((a, b) => a - b);
68 | // });
69 |
70 | // // 2) BUUBLE SORT
71 | // timeLogger('BASIC BUBBLE SORT', bubbleSort.basicBubbleSort);
72 | timeLogger('ENHANCED BUBBLE SORT', bubbleSort.enhancedBubbleSort);
73 |
74 | // // 3) SELECTION SORT
75 | // timeLogger('SELECTION SORT', selectionSort);
76 |
77 | // // 4) INSERTION SORT
78 | // timeLogger('INSERTION SORT', insertionSort);
79 |
80 | // // 5) QUICK SORT
81 | // timeLogger('QUICK SORT', (arr) => quickSort(arr, 0, len - 1));
82 |
83 | // // 6) MERGE SORT
84 | // timeLogger('MERGE SORT', (arr) => mergeSort(arr, 0, arr.length - 1));
85 |
86 | // // 7) HEAP SORT
87 | // timeLogger('HEAP SORT', heapSort);
88 |
89 | // 8) STUPID SORT
90 | // timeLogger('STUPID SORT', stupidSort);
91 |
92 | // // 9) LSD RADIX SORT
93 | // timeLogger('LSD RADIX SORT', radixSort.radixSortLSD);
94 |
95 | // 10) ODD EVEN SORT
96 | timeLogger('ODD EVEN SORT', oddEvenSort);
97 | }
98 |
99 | testing();
100 |
--------------------------------------------------------------------------------
/algorithms/sorts/insertion-sort.js:
--------------------------------------------------------------------------------
1 | /*
2 | Đầu vào:
3 | - arr: mảng cần sắp xếp.
4 | - n: số lượng phần tử của mảng.
5 | - i: chỉ số index vòng lặp ngoài (1 -> n).
6 | - j: chỉ số vòng lặp trong (i - 1 -> 0)
7 |
8 | Ý tưởng:
9 | - Sử dụng 2 vòng lặp lồng nhau.
10 | - Vòng lặp bên ngoài để duyệt qua mảng.
11 | - Vòng lặp bên trong để chọn vị trí chèn thích hợp cho phần tử i (insertion).
12 | */
13 |
14 | /*
15 | Độ phức tạp (BigO):
16 | - Best Case (Mảng đã sắp xếp): O(n).
17 | - Average: O (n^2)
18 | - Worst Case (Mảng sắp xếp ngươc): O(n^2).
19 | - Không gian biến phụ: O(1).
20 | Note:
21 | - Không nên dùng cho mảng kích thước lớn.
22 | - Chỉ nên sử dụng khi mảng đã sắp xếp như bảng Highscore...
23 | */
24 |
25 | function insertionSort(arr = []) {
26 | let n = arr.length;
27 |
28 | let i = 1,
29 | j;
30 |
31 | while (i < n) {
32 | if (arr[i] >= arr[i - 1]) ++i;
33 | else {
34 | const current = arr[i];
35 | j = i - 1;
36 |
37 | // insert item into suitable position
38 | while (j >= 0 && arr[j] > current) {
39 | arr[j + 1] = arr[j];
40 | --j;
41 | }
42 |
43 | arr[j + 1] = current;
44 | ++i;
45 | }
46 | }
47 |
48 | return arr;
49 | }
50 |
51 | module.exports = insertionSort;
52 |
--------------------------------------------------------------------------------
/algorithms/sorts/merge-sort.js:
--------------------------------------------------------------------------------
1 | /*
2 | Đầu vào:
3 | - arr: mảng cần sắp xếp.
4 | - l: index phần tử bắt đầu duyệt.
5 | - r: index phần tử cuối cần duyệt.
6 |
7 | Ý tưởng:
8 | - Tách mảng ban đầu thành 2 mảng dựa trên phần tử middle: (n - 1)/2.
9 | - Cứ thế đệ quy, tách đến khi các mảng còn 1 phần tử.
10 | - Gộp các mảng trên lại, vừa gộp vừa sắp xếp.
11 | */
12 |
13 | /*
14 | Độ phức tạp (BigO):
15 | - Best Case: O( n*log(n) ).
16 | - Average: O( n*log(n) )
17 | - Worst Case: O( n*log(n) ).
18 | - Không gian biến phụ: O(n).
19 | Note:
20 | - Nên dùng cho mảng kích thước lớn.
21 | - Thuật toán khá ổn định trong mọi trường hợp.
22 | - Nhược: sử dụng không gian phụ khá nhiều.
23 | */
24 |
25 | // @fn: Hàm gộp 2 mảng lại với nhau
26 | function mergeArr(arr, l, middle, r) {
27 | // tạo 2 mảng trái, phải đã sắp xếp
28 | let leftArr = [...arr.slice(l, middle + 1)],
29 | rightArr = [...arr.slice(middle + 1, r + 1)];
30 |
31 | let mergeIndex = l,
32 | i = 0,
33 | j = 0,
34 | n1 = middle + 1 - l, // số phần tử mảng trái
35 | n2 = r - middle; // số phấn tử mảng phải
36 |
37 | // gộp 2 mảng lại
38 | while (i < n1 && j < n2)
39 | arr[mergeIndex++] = leftArr[i] < rightArr[j] ? leftArr[i++] : rightArr[j++];
40 |
41 | // gán hết phần tử mảng trái nếu còn
42 | while (i < n1) arr[mergeIndex++] = leftArr[i++];
43 |
44 | // gán hết phần tử mảng phải nếu còn
45 | while (j < n2) arr[mergeIndex++] = rightArr[j++];
46 | }
47 |
48 | // @fn: merge sort
49 | function mergeSort(arr = [], l, r) {
50 | // điều kiện dừng đệ quy
51 | if (l >= r) return;
52 |
53 | // tìm phần tử trung gian để tách mảng
54 | let middle = parseInt((l + r) / 2);
55 |
56 | mergeSort(arr, l, middle); // tách nửa mảng trái
57 | mergeSort(arr, middle + 1, r); // tách nửa mảng phải
58 | mergeArr(arr, l, middle, r); // gộp 2 mảng trái phải
59 | }
60 |
61 | module.exports = mergeSort;
62 |
--------------------------------------------------------------------------------
/algorithms/sorts/odd-even-sort.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Ý tưởng:
4 | - Còn gọi là brick sort
5 | - Là phiên bản khác cải thiện của bubble sort, chia mảng làm 2 phase chẵn và lẻ, sau đó dùng bubble sort.
6 |
7 | */
8 |
9 | function oddEvenSort(arr = []) {
10 | const n = arr.length;
11 | let isSorted = false;
12 |
13 | while (!isSorted) {
14 | isSorted = true;
15 |
16 | // Sắp xếp mảng chẵn
17 | for (let i = 0; i <= n - 2; i += 2) {
18 | if (arr[i] > arr[i + 1]) {
19 | [arr[i], arr[i + 1]] = [arr[i + 1], arr[i]];
20 | isSorted = false;
21 | }
22 | }
23 |
24 | // Sắp xếp mảng lẻ
25 | for (let i = 1; i <= n - 2; i += 2) {
26 | if (arr[i] > arr[i + 1]) {
27 | [arr[i], arr[i + 1]] = [arr[i + 1], arr[i]];
28 | isSorted = false;
29 | }
30 | }
31 | }
32 | }
33 |
34 | module.exports = oddEvenSort;
35 |
--------------------------------------------------------------------------------
/algorithms/sorts/quick-sort.js:
--------------------------------------------------------------------------------
1 | /*
2 | Đầu vào:
3 | - arr: mảng cần sắp xếp.
4 | - low: index phần tử bắt đầu duyệt.
5 | - high: index phần tử cuối cần duyệt.
6 |
7 | Ý tưởng:
8 | - Lựa chọn một phần tử vách ngăn (pivot).
9 | - Xây dựng một hàm với nhiệm vụ chia mảng thành 2 mảng con sao cho:
10 | + Mảng bên trái bao gồm các phần tử nhỏ hơn pivot.
11 | + Mảng bên phải bao gồm các phần tử lớn hơn pivot.
12 | - Lặp lại 2 bước trên với nửa mảng bên trái và nửa mảng bên phải.
13 | - Kết thúc khi mảng chỉ còn 1 phần tử (không thể chia mảng ra nữa).
14 | */
15 |
16 | /*
17 | Độ phức tạp (BigO):
18 | - Best Case: O( n*log(n) ).
19 | - Average: O( n*log(n) )
20 | - Worst Case (mảng đã sắp xếp hay pivot phần tử lớn nhất (nhỏ nhất) của mảng): O(n^2).
21 | - Không gian biến phụ: O(log(n)).
22 | Note:
23 | - Nên dùng cho mảng kích thước lớn.
24 | - Không nên dùng khi mảng đã sắp xếp...
25 | */
26 |
27 | function partitionFunc(arr = [], low, high) {
28 | let pivot = arr[high];
29 | let l = low;
30 | let r = high - 1;
31 |
32 | while (true) {
33 | // Tìm phần tử bên trái bị sai vị trí
34 | while (l <= r && arr[l] < pivot) l++;
35 |
36 | // Tìm phần tử bên phải bị sai vị trí
37 | while (r >= l && arr[r] > pivot) r--;
38 |
39 | // Nếu đã hoàn tất duyệt thì dừng
40 | if (l >= r) break;
41 |
42 | // Ngược lại thì swap 2 phần tử bị sai, sau đó tăng trái và phải lên 1
43 | [arr[l], arr[r]] = [arr[r], arr[l]];
44 | l++;
45 | r--;
46 | }
47 |
48 | // Đưa phần tử pivot vào đúng vị trí
49 | [arr[l], arr[high]] = [arr[high], arr[l]];
50 |
51 | // Trả về vị trí pivot hiện tại
52 | return l;
53 | }
54 |
55 | function quickSort(arr, low, high) {
56 | if (low < high) {
57 | // Tìm phần tử pivot
58 | const pivot = partitionFunc(arr, low, high);
59 |
60 | // Đệ quy nửa mảng bên trái
61 | quickSort(arr, low, pivot - 1);
62 |
63 | // Đệ quy nửa mảng bên phải
64 | quickSort(arr, pivot + 1, high);
65 | }
66 | }
67 |
68 | module.exports = quickSort;
69 |
--------------------------------------------------------------------------------
/algorithms/sorts/radix-sort.js:
--------------------------------------------------------------------------------
1 | // Hàm lấy chữ số tại vị trí posDigit
2 | // posDigit = 0 là hàng đơn vị
3 | function getDigit(num = 0, posDigit = 0) {
4 | // ex: num = -1234, posDigit = 2, expected = 2
5 | let numInt = Math.abs(parseInt(num / 10 ** posDigit)); // numInt = | -1234 / 10^2 | = 12;
6 | let digit = numInt % 10; // digit = 2;
7 |
8 | // Nếu num < 0 và digit = 0 thì trả về -1, ngược lại trả về digit
9 | return num < 0 && digit === 0 ? -1 : digit;
10 | }
11 |
12 | // Tìm số chữ số lớn nhất của mảng
13 | // Chú ý số âm thì cộng thêm 1 chữ số
14 | function getMaxExp(arr = []) {
15 | let exp = 0;
16 |
17 | arr.forEach((num) => {
18 | const len = num.toString().length;
19 | if (len > exp) exp = len;
20 | });
21 |
22 | return exp;
23 | }
24 |
25 | // Hàm radix sort LSD ( Least Significant Digit )
26 | function radixSortLSD(arr = []) {
27 | // Tạo 11 cái xô chứa các digit từ -1 đến 9
28 | let buckets = Array.from({ length: 11 }, () => new Array());
29 |
30 | // Số chữ số nhiều nhất, cũng là số lần lặp duyệt mảng
31 | let exp = getMaxExp(arr) || 0;
32 |
33 | // LSD lấy từ chữ số cuối
34 | let i = 0;
35 | while (i < exp) {
36 | // Lặp qua mảng và cho vào các xô
37 | arr.forEach((num) => {
38 | const digitIndex = getDigit(num, i) + 1; // Do buckets có index từ 0 -> 10, getDigit() trả về -1 -> 10
39 | buckets[digitIndex].push(num);
40 | });
41 |
42 | // Lấy từ xô trả về mảng
43 | let j = 0;
44 | buckets.forEach((numArr, index) => {
45 | // Nếu là lần cuối và xô là xô chứa số âm thì lấy ngược lại các p.tử trong xô
46 | if (i === exp - 1 && index === 0) {
47 | const n = numArr.length;
48 | for (let k = n - 1; k >= 0; --k) arr[j++] = numArr[k];
49 | }
50 | // Ngược lại, thì lấy xuôi từ trong xô
51 | else numArr.forEach((num) => (arr[j++] = num));
52 |
53 | // Làm trống xô
54 | numArr.length = 0;
55 | });
56 |
57 | ++i;
58 | }
59 |
60 | return arr;
61 | }
62 |
63 | // Hàm radix sort MSD ( Most Significant Digit )
64 | function radixSortMSD(arr = []) {}
65 |
66 | module.exports = { radixSortLSD, radixSortMSD };
67 |
--------------------------------------------------------------------------------
/algorithms/sorts/selection-sort.js:
--------------------------------------------------------------------------------
1 | /*
2 | Đầu vào:
3 | - arr: mảng cần sắp xếp.
4 | - n: số lượng phần tử của mảng.
5 | - i: chỉ số index vòng lặp ngoài (0 -> n-1).
6 | - j: chỉ số vòng lặp trong (i+1 -> n)
7 |
8 | Ý tưởng:
9 | - Sử dụng 2 vòng lặp lồng nhau để chia mảng thành 2 mảng con (1 đã sắp xếp, 1 không).
10 | - Vòng lặp bên ngoài để duyệt qua mảng.
11 | - Vòng lặp bên trong duyệt qua mảng con chưa sắp xếp để tìm phần tử min (max).
12 | + Nếu min (max) trong mảng con đó nhỏ (lớn) hơn phần tử cuối mảng đã sắp xếp thì swap 2 phần từ đó.
13 |
14 | */
15 |
16 | /*
17 | Độ phức tạp (BigO):
18 | - Best Case (Mảng đã sắp xếp): O(n^2).
19 | - Average: O (n^2)
20 | - Worst Case (Mảng sắp xếp ngươc): O(n^2).
21 | - Không gian biến phụ: O(1).
22 | Note:
23 | - Không nên dùng cho mảng kích thước lớn.
24 | - Chỉ nên sử dụng khi mảng gần như đã sắp xếp như bảng Highscore
25 | list...
26 | */
27 |
28 | function selectionSort(arr = []) {
29 | let n = arr.length;
30 |
31 | for (let i = 0; i < n - 1; ++i) {
32 | let min = arr[i];
33 | let index = i;
34 |
35 | // find min
36 | for (let j = i + 1; j < n; ++j) {
37 | if (arr[j] < min) {
38 | min = arr[j];
39 | index = j;
40 | }
41 | }
42 |
43 | // swap if min != initial min
44 | if (index !== i) [arr[i], arr[index]] = [arr[index], arr[i]];
45 | }
46 | }
47 |
48 | module.exports = selectionSort;
49 |
--------------------------------------------------------------------------------
/algorithms/sorts/stupid-sort.js:
--------------------------------------------------------------------------------
1 | /*
2 | Đầu vào:
3 | - arr: mảng cần sắp xếp.
4 | - Stupid sort hay còn gọi là BogoSort, Permutation sort, slow sort
5 |
6 | Ý tưởng:
7 | - Xáo trộn mảng, kiểm tra mảng vừa xáo
8 | - Nếu đã xếp thì dừng, còn không thì lặp lại :">
9 | */
10 |
11 | /*
12 | Độ phức tạp (BigO): O(∞) nhân phẩm.
13 | Note:
14 | - Dùng để kiểm tra nhân phẩm người dùng.
15 | - Chán quá thì dùng cho vui chứ không có ý nghĩa gì.
16 |
17 | */
18 |
19 | // Hàm kiểm tra mảng đã sắp xếp chưa?
20 | function isSortedArr(arr = []) {
21 | for (let i = 0; i < arr.length - 1; ++i)
22 | if (arr[i] > arr[i + 1]) return false;
23 |
24 | return true;
25 | }
26 |
27 | // Hàm xáo trộn mảng
28 | function shuffleArray(arr = []) {
29 | return arr.sort(() => Math.random() - 0.5);
30 | }
31 |
32 | // stupid sort
33 | function stupidSort(arr = []) {
34 | while (!isSortedArr(arr)) shuffleArray(arr);
35 | }
36 |
37 | module.exports = stupidSort;
38 |
--------------------------------------------------------------------------------
/assets/icons/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dynonguyen/dyno-visualizer/6db674acbd80fc50194d17a4df0f5678d27f5c3e/assets/icons/favicon.png
--------------------------------------------------------------------------------
/assets/icons/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dynonguyen/dyno-visualizer/6db674acbd80fc50194d17a4df0f5678d27f5c3e/assets/icons/logo.png
--------------------------------------------------------------------------------
/assets/images/sort/max-heap.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dynonguyen/dyno-visualizer/6db674acbd80fc50194d17a4df0f5678d27f5c3e/assets/images/sort/max-heap.jpg
--------------------------------------------------------------------------------
/assets/images/sort/merge-sort-illustration.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dynonguyen/dyno-visualizer/6db674acbd80fc50194d17a4df0f5678d27f5c3e/assets/images/sort/merge-sort-illustration.jpg
--------------------------------------------------------------------------------
/assets/images/sort/radix-lsd.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dynonguyen/dyno-visualizer/6db674acbd80fc50194d17a4df0f5678d27f5c3e/assets/images/sort/radix-lsd.jpg
--------------------------------------------------------------------------------
/assets/images/sort/rebuild-max-heap.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dynonguyen/dyno-visualizer/6db674acbd80fc50194d17a4df0f5678d27f5c3e/assets/images/sort/rebuild-max-heap.jpg
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Algorithms With Dyno
9 |
10 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
37 |
38 |
39 |
61 |
65 |
66 |
67 |
68 |
69 | Học lập trình với Dyno
70 |
71 | ABOUT ME
72 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
--------------------------------------------------------------------------------
/scripts/contants.js:
--------------------------------------------------------------------------------
1 | const ARRAY_TYPES = [
2 | {
3 | title: 'Random',
4 | value: 'random',
5 | },
6 | {
7 | title: '~Sorted',
8 | value: 'sorted',
9 | },
10 | {
11 | title: '~Reverse Sorted',
12 | value: 'reverse',
13 | },
14 | ];
15 |
--------------------------------------------------------------------------------
/scripts/helper.js:
--------------------------------------------------------------------------------
1 | // debounce
2 | function debounce(timer = null, delay = 250, cbFn) {
3 | if (timer) clearTimeout(timer);
4 | timer = setTimeout(cbFn, delay);
5 | return timer;
6 | }
7 |
8 | // render option select
9 | function renderOptionSelect(optionList = []) {
10 | let result = '';
11 | optionList.forEach((option) => {
12 | result += `${option.title} `;
13 | });
14 | return result;
15 | }
16 |
17 | // render physical array
18 | const generateRandomData = (length = 10, type = 0, max = 1000) => {
19 | let arr = [];
20 | for (let i = 0; i < length; ++i) arr.push(Math.round(Math.random() * max));
21 | switch (type) {
22 | case 1:
23 | arr.sort((a, b) => a - b);
24 | // swap 2 last postion => generate ~sorted list
25 | if (length > 2) {
26 | [arr[length - 1], arr[length - 2]] = [arr[length - 2], arr[length - 1]];
27 | [arr[length - 2], arr[length - 3]] = [arr[length - 3], arr[length - 2]];
28 | }
29 | break;
30 | case 2:
31 | arr.sort((a, b) => b - a);
32 | if (length > 2) {
33 | [arr[length - 1], arr[length - 2]] = [arr[length - 2], arr[length - 1]];
34 | [arr[length - 2], arr[length - 3]] = [arr[length - 3], arr[length - 2]];
35 | }
36 |
37 | break;
38 | default:
39 | break;
40 | }
41 | return arr;
42 | };
43 |
--------------------------------------------------------------------------------
/scripts/main.js:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | function getAndSetTheme() {
4 | const isLight = localStorage.getItem('theme') === 'light' ? true : false;
5 | if (isLight) {
6 | $(':root').attr('data-theme', 'light');
7 | $('#themeBtn').addClass('fa-moon');
8 | } else {
9 | $(':root').attr('data-theme', 'dark');
10 | $('#themeBtn').addClass('fa-sun');
11 | }
12 | }
13 |
14 | $(document).ready(function () {
15 | // get & set theme
16 | getAndSetTheme();
17 | });
18 |
--------------------------------------------------------------------------------
/scripts/menu.js:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | $(document).ready(function () {
4 | //show slide menu
5 | $('#slideMenuIcon').click(() => {
6 | $('#overlay').css('display', 'block');
7 | $('#slideNav').removeClass('hide-slide-menu').addClass('show-slide-menu');
8 | });
9 |
10 | // close slide menu
11 | $('#closeSlideMenuIcon').click(() => {
12 | $('#slideNav').removeClass('show-slide-menu').addClass('hide-slide-menu');
13 | $('#overlay').css('display', 'none');
14 | });
15 |
16 | // show sub slide menu
17 | $('.slide-menu-item').click(function () {
18 | const subId = $(this).attr('data-toggle-id');
19 | if (subId) {
20 | const subMenu = $(`#${subId}`);
21 | if (subMenu.hasClass('hide')) {
22 | subMenu.removeClass('hide').addClass('show');
23 | $(this).children('span').removeClass('arrow-down').addClass('arrow-up');
24 | subMenu.show(250);
25 | } else {
26 | subMenu.removeClass('show').addClass('hide');
27 | $(this).children('span').removeClass('arrow-up').addClass('arrow-down');
28 | subMenu.hide(250);
29 | }
30 | }
31 | });
32 |
33 | // change theme
34 | $('#themeBtn').click(function () {
35 | const nextTheme =
36 | localStorage.getItem('theme') === 'light' ? 'dark' : 'light';
37 |
38 | $(':root').attr('data-theme', nextTheme);
39 | localStorage.setItem('theme', nextTheme);
40 |
41 | // change icon
42 | if ($(this).hasClass('fa-sun'))
43 | $(this).removeClass('fa-sun').addClass('fa-moon');
44 | else $(this).removeClass('fa-moon').addClass('fa-sun');
45 | });
46 | });
47 |
--------------------------------------------------------------------------------
/scripts/sort-visualizer/bubble-sort.js:
--------------------------------------------------------------------------------
1 | async function basicBubbleSort(arr = []) {
2 | let n = arr.length,
3 | nSwap = 0,
4 | nCompare = 0,
5 | nArrAccess = 0;
6 | for (let i = 0; i < n - 1; ++i) {
7 | for (let j = 0; j < n - 1; ++j) {
8 | ++nCompare;
9 | nArrAccess += 2;
10 | if (!isSorting) return;
11 | await preSwap(j, j + 1);
12 | if (arr[j] > arr[j + 1]) {
13 | [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
14 | nArrAccess += 2;
15 | await swapEle(j, j + 1);
16 | ++nSwap;
17 | }
18 | await endSwap(j, j + 1);
19 | }
20 | }
21 |
22 | return { nCompare, nArrAccess, nSwap };
23 | }
24 |
25 | async function enhancedBubbleSort(arr = []) {
26 | let n = arr.length,
27 | nSwap = 0,
28 | nCompare = 0,
29 | nArrAccess = 0;
30 | for (let i = 0; i < n - 1; ++i) {
31 | let isSwap = false;
32 | for (let j = 0; j < n - 1 - i; ++j) {
33 | ++nCompare;
34 | nArrAccess += 2;
35 | if (!isSorting) return { nCompare, nArrAccess, nSwap };
36 | await preSwap(j, j + 1);
37 | if (arr[j] > arr[j + 1]) {
38 | nArrAccess += 2;
39 | isSwap = true;
40 | [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
41 | await swapEle(j, j + 1);
42 | ++nSwap;
43 | }
44 | await endSwap(j, j + 1);
45 | }
46 | await changeItemColor(n - 1 - i, DONE_ITEM_COLOR);
47 | if (!isSwap) {
48 | return { nCompare, nArrAccess, nSwap };
49 | }
50 | }
51 |
52 | return { nCompare, nArrAccess, nSwap };
53 | }
54 |
55 | // description
56 | // basic
57 | const bsCode = Prism.highlight(
58 | `function basicBubbleSort(arr = []) {
59 | const n = arr.length;
60 |
61 | // Vòng lặp ngoài -> lặp qua mảng
62 | for (let i = 0; i < n - 1; ++i) {
63 | // Vòng lặp trong -> nổi bọt phần tử
64 | for (let j = 0; j < n - 1; ++j) {
65 | if (arr[j] > arr[j + 1]) {
66 | // Hoán vị
67 | [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
68 | }
69 | }
70 | }
71 |
72 | return arr;
73 | }`,
74 | Prism.languages.javascript,
75 | 'javascript',
76 | );
77 |
78 | const basicBubbleSortDesc = {
79 | title: '🎈 BASIC BUBBLE SORT 💭',
80 | sortNotes: [
81 | {
82 | title: 'P.tử hiện tại',
83 | color: CURRENT_ITEM_COLOR,
84 | },
85 | {
86 | title: 'P.tử kế tiếp',
87 | color: SECOND_ITEM_COLOR,
88 | },
89 | ],
90 | htmlContent: `
91 | 1) Độ phức tạp thuật toán (Algorithm Complexity BigO):
92 |
93 |
94 |
95 |
- Độ phức tạp thời gian (Time Complexity):
96 |
97 |
98 | Best case: O(n)
99 |
100 | Average case: O(n2 )
101 |
102 | Worst case: O(n2 )
103 |
104 |
105 |
106 |
- Độ phức tạp bộ nhớ (Menory Complexity):
107 |
108 |
Worst case: O(1)
109 |
110 | 2) Ý tưởng thuật toán (Algorithm Idea):
111 |
112 |
113 |
- Đầu vào:
114 |
115 |
116 | - arr : mảng cần sắp xếp.
117 | - n : số lượng phần tử của mảng.
118 | - i : chỉ số index vòng lặp ngoài (0 -> n-1).
119 | - j : chỉ số vòng lặp trong (0 -> n - 1)
120 |
121 |
- Ý tưởng:
122 |
123 |
124 | - Sử dụng 2 vòng lặp lồng nhau.
125 | - Vòng lặp bên ngoài để duyệt qua mảng.
126 | - Vòng lặp bên trong để đẩy phần tử max (min) lên đầu mảng
127 | (bubble).
128 | - Nếu phần tử sau > phần tử trước thì swap 2 phần tử đó (sắp tăng
129 | dần) và ngược lại.
130 |
131 |
132 | 3) Triển khai (Implement Algorithm):
133 |
136 | 4) Ghi chú (Note):
137 |
138 | - Không nên dùng cho mảng kích thước lớn.
139 | - Chỉ nên sử dụng khi mảng gần như đã sắp xếp như bảng Highscore
140 | list...
141 |
`,
142 | };
143 |
144 | // enhanced
145 | const ebsCode = Prism.highlight(
146 | `function enhancedBubbleSort(arr = []) {
147 | const n = arr.length;
148 |
149 | // Vòng lặp ngoài -> lặp qua mảng
150 | for (let i = 0; i < n - 1; ++i) {
151 | let swapped = false;
152 |
153 | // Vòng lặp trong -> nổi bọt phần tử
154 | // Mỗi lần lặp trừ đi 1 là phần tử đã nổi bọt trước đó
155 | for (let j = 0; j < n - 1 - i; ++j) {
156 | if (arr[j] > arr[j + 1]) {
157 | // Hoán vị
158 | [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
159 | swapped = true; // Đổi cờ swap
160 | }
161 | }
162 |
163 | // Nếu vòng lặp trong không có cặp nào swap thì dừng (đã xếp xong)
164 | if (!swapped) break;
165 | }
166 |
167 | return arr;
168 | }`,
169 | Prism.languages.javascript,
170 | 'javascript',
171 | );
172 | const enhancedBubbleSortDesc = {
173 | title: '💭 ENHANCED BUBBLE SORT 🎈',
174 | sortNotes: [
175 | {
176 | title: 'P.tử hiện tại',
177 | color: CURRENT_ITEM_COLOR,
178 | },
179 | {
180 | title: 'P.tử kế tiếp',
181 | color: SECOND_ITEM_COLOR,
182 | },
183 | {
184 | title: 'P.tử nổi bọt',
185 | color: DONE_ITEM_COLOR,
186 | },
187 | ],
188 | htmlContent: `
189 | 1) Độ phức tạp thuật toán (Algorithm Complexity BigO):
190 |
191 |
192 |
193 |
- Độ phức tạp thời gian (Time Complexity):
194 |
195 |
196 | Best case: O(n)
197 |
198 | Average case: O(n2 )
199 |
200 | Worst case: O(n2 )
201 |
202 |
203 |
204 |
- Độ phức tạp bộ nhớ (Menory Complexity):
205 |
206 |
Worst case: O(1)
207 |
208 | 2) Ý tưởng thuật toán (Algorithm Idea):
209 |
210 |
211 |
- Đầu vào:
212 |
213 |
214 | - arr : mảng cần sắp xếp.
215 | - n : số lượng phần tử của mảng.
216 | - i : chỉ số index vòng lặp ngoài (0 -> n-1).
217 | - j : chỉ số vòng lặp trong (0 -> n - 1 - i)
218 |
219 |
- Ý tưởng:
220 |
221 |
222 | - Sử dụng 2 vòng lặp lồng nhau.
223 | - Vòng lặp bên ngoài để duyệt qua mảng.
224 | - Vòng lặp bên trong để đẩy phần tử max (min) lên đầu mảng
225 | (bubble).
226 | - Nếu phần tử sau > phần tử trước thì swap 2 phần tử đó (sắp tăng
227 | dần) và ngược lại.
228 | - Để tối ưu thì ta chỉ nên cho vòng lặp trong chạy đến (n - 1 - i) mà không cần đến (n - 1).
229 | - Kiểm tra nếu trong suốt vòng lặp bên trong không có cặp nào được swap thì dừng thuật toán.
230 |
231 |
232 | 3) Triển khai (Implement Algorithm):
233 |
236 | 4) Ghi chú (Note):
237 |
238 | - Không nên dùng cho mảng kích thước lớn.
239 | - Chỉ nên sử dụng khi mảng gần như đã sắp xếp như bảng Highscore
240 | list...
241 | - Nên dùng thay thế cho thuật toán basic bubble sort.
242 |
`,
243 | };
244 |
--------------------------------------------------------------------------------
/scripts/sort-visualizer/constant.js:
--------------------------------------------------------------------------------
1 | const ITEM_COLOR = '#393A59';
2 | const CURRENT_ITEM_COLOR = '#BD93F9';
3 | const SECOND_ITEM_COLOR = '#48D06D';
4 | const DONE_ITEM_COLOR = '#FFB332';
5 |
--------------------------------------------------------------------------------
/scripts/sort-visualizer/heap-sort.js:
--------------------------------------------------------------------------------
1 | const PARENT_NODE_COLOR = '#F24A0E';
2 |
3 | let nHSCompare = 0,
4 | nHSArrAccess = 0,
5 | nHSSwap = 0;
6 |
7 | // @fn: Xây dựng max heap từ 1 node
8 | async function maxHeapify(arr = [], len, nodeIndex) {
9 | // Nếu là node lá thì dừng
10 | nHSCompare++;
11 | if (nodeIndex > Math.floor(len / 2 - 1))
12 | return { nSwap: nHSSwap, nCompare: nHSCompare, nArrAccess: nHSArrAccess };
13 |
14 | const leftNode = 2 * nodeIndex + 1,
15 | rightNode = 2 * nodeIndex + 2;
16 | let maxIndex = nodeIndex;
17 |
18 | // Tìm node lớn nhất trong 3 node
19 | if (leftNode < len && arr[leftNode] > arr[maxIndex]) {
20 | nHSArrAccess += 2;
21 | nHSCompare++;
22 | maxIndex = leftNode;
23 | }
24 | if (rightNode < len && arr[rightNode] > arr[maxIndex]) {
25 | nHSArrAccess += 2;
26 | nHSCompare++;
27 | maxIndex = rightNode;
28 | }
29 |
30 | // Hoán vị node cha hiện tại với con lớn nhất
31 | nHSCompare += 3;
32 | if (maxIndex !== nodeIndex) {
33 | await preSwap(nodeIndex, maxIndex);
34 | await swapEle(nodeIndex, maxIndex);
35 | [arr[maxIndex], arr[nodeIndex]] = [arr[nodeIndex], arr[maxIndex]];
36 | nHSSwap++;
37 | await endSwap(nodeIndex, maxIndex);
38 |
39 | // Tiếp tục đệ quy kiểm tra tại node lớn nhất này
40 | await maxHeapify(arr, len, maxIndex);
41 | }
42 | }
43 |
44 | // @fn: Xây dụng max-heap
45 | async function buildMaxHeap(arr = []) {
46 | const len = arr.length;
47 | // Parent node cuối cùng
48 | const lastParentNode = Math.floor(len / 2 - 1);
49 |
50 | // Từ node cha cuối trở lên đều là parent node
51 | for (let i = lastParentNode; i >= 0; --i) {
52 | if (!isSorting)
53 | return { nSwap: nHSSwap, nCompare: nHSCompare, nArrAccess: nHSArrAccess };
54 | nHSCompare++;
55 | await changeItemColor(i, PARENT_NODE_COLOR);
56 | await maxHeapify(arr, len, i);
57 | await changeItemColor(i, ITEM_COLOR);
58 | }
59 | }
60 |
61 | // @fn: Heap sort
62 | async function heapSort(arr = []) {
63 | // Xây dựng max heap
64 | await buildMaxHeap(arr);
65 |
66 | const len = arr.length;
67 | for (let i = len - 1; i >= 0; --i) {
68 | if (!isSorting)
69 | return { nSwap: nHSSwap, nCompare: nHSCompare, nArrAccess: nHSArrAccess };
70 |
71 | nHSCompare++;
72 | nHSSwap++;
73 | nHSArrAccess += 2;
74 | // Hoán vị node root với node cuối cùng
75 | await preSwap(0, i);
76 | [arr[0], arr[i]] = [arr[i], arr[0]];
77 | await swapEle(0, i);
78 | await endSwap(0, i);
79 |
80 | await changeItemColor(i, DONE_ITEM_COLOR);
81 | // Xây lại max heap tại vị trí root
82 | await maxHeapify(arr, i, 0);
83 | }
84 |
85 | return { nSwap: nHSSwap, nCompare: nHSCompare, nArrAccess: nHSArrAccess };
86 | }
87 |
88 | // description
89 | const maxHeapCode = Prism.highlight(
90 | `// Hàm xây dựng max heap từ 1 node
91 | function maxHeapify(arr = [], len, nodeIndex) {
92 | // Nếu là node lá thì dừng
93 | if (nodeIndex > Math.floor(len / 2 - 1)) return;
94 |
95 | const leftNode = 2 * nodeIndex + 1,
96 | rightNode = 2 * nodeIndex + 2;
97 | let maxIndex = nodeIndex;
98 |
99 | // Tìm node lớn nhất trong 3 node
100 | if (leftNode < len && arr[leftNode] > arr[maxIndex]) maxIndex = leftNode;
101 | if (rightNode < len && arr[rightNode] > arr[maxIndex]) maxIndex = rightNode;
102 |
103 | // Hoán vị node cha hiện tại với con lớn nhất
104 | if (maxIndex !== nodeIndex) {
105 | [arr[maxIndex], arr[nodeIndex]] = [arr[nodeIndex], arr[maxIndex]];
106 |
107 | // Tiếp tục đệ quy kiểm tra tại node lớn nhất này
108 | maxHeapify(arr, len, maxIndex);
109 | }
110 | }
111 |
112 | // Hàm xây dụng max-heap
113 | function buildMaxHeap(arr = []) {
114 | const len = arr.length;
115 | // Parent node cuối cùng
116 | const lastParentNode = Math.floor(len / 2 - 1);
117 |
118 | // Từ node cha cuối trở lên đều là parent node
119 | for (let i = lastParentNode; i >= 0; --i) maxHeapify(arr, len, i);
120 | }
121 | `,
122 | Prism.languages.javascript,
123 | 'javascript',
124 | );
125 |
126 | const hSCode = Prism.highlight(
127 | `function heapSort(arr = []) {
128 | // Xây dựng max heap
129 | buildMaxHeap(arr);
130 |
131 | const len = arr.length;
132 | for (let i = len - 1; i >= 0; --i) {
133 | // Hoán vị node root với node cuối cùng
134 | [arr[0], arr[i]] = [arr[i], arr[0]];
135 |
136 | // Xây lại max heap tại vị trí root
137 | maxHeapify(arr, i, 0);
138 | }
139 | }`,
140 | Prism.languages.javascript,
141 | 'javascript',
142 | );
143 |
144 | const heapSortDesc = {
145 | title: 'HEAP SORT ALGORITHM',
146 | sortNotes: [
147 | {
148 | title: 'Node Cha',
149 | color: '#F24A0E',
150 | },
151 | {
152 | title: 'P.tử hoán vị',
153 | color: CURRENT_ITEM_COLOR,
154 | },
155 | {
156 | title: 'P.tử hoán vị',
157 | color: SECOND_ITEM_COLOR,
158 | },
159 | {
160 | title: 'P.tử đã sắp xếp',
161 | color: DONE_ITEM_COLOR,
162 | },
163 | ],
164 | htmlContent: `
165 | 1) Độ phức tạp thuật toán (Algorithm Complexity BigO):
166 |
167 |
168 |
169 |
- Độ phức tạp thời gian (Time Complexity):
170 |
171 |
172 | Best case: O( n log(n) )
173 |
174 | Average case: O( n log(n) )
175 |
176 | Worst case: O( n log(n) )
177 |
178 |
179 |
180 |
- Độ phức tạp bộ nhớ (Menory Complexity):
181 |
182 |
Worst case: O(1)
183 |
184 | 2) Ý tưởng thuật toán (Algorithm Idea):
185 |
186 |
187 |
- Đầu vào:
188 |
189 |
190 | - arr : mảng cần sắp xếp.
191 | - n : số lượng phần tử của mảng.
192 |
193 |
- Ý tưởng:
194 |
195 |
196 | - Sử dụng tính chất của cấu trúc dữ liệu heap (max heap, min heap).
197 | - Đầu tiên xây dựng một max-heap (min-heap).
198 | - Hoán vị phần tử root của heap với phần tử cuối.
199 | - Xoá phần tử cuối ra khỏi heap.
200 | - Xây dựng lại heap tại phần tử root.
201 |
202 |
203 | 3) Triển khai (Implement Algorithm):
204 |
205 |
3.1 - Minh hoạ ý tưởng:
206 |
Mọi người xem chi tiết cấu trúc "Heap" là gì và cách xây dựng heap tại đây nhé
207 |
213 |
219 |
220 |
3.2 - Xây dựng max-heap:
221 |
${maxHeapCode}
222 |
223 |
3.3 - Hàm heap sort:
224 |
${hSCode}
225 |
226 |
227 | 4) Ghi chú (Note):
228 |
229 | - Nên dùng cho mảng kích thước lớn.
230 | - Thuật toán khá ổn định trong mọi trường hợp.
231 |
`,
232 | };
233 |
--------------------------------------------------------------------------------
/scripts/sort-visualizer/index.js:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | // render visualize array
4 | function renderArray(arr = []) {
5 | let xml = '';
6 | if (arr.length <= 20) {
7 | arr.forEach((item, index) => {
8 | xml += `${item} `;
9 | });
10 | } else {
11 | arr.forEach((item, index) => {
12 | xml += ` `;
13 | });
14 | }
15 |
16 | return xml;
17 | }
18 |
19 | // render sort notes
20 | const renderSortNotes = (sortNotes = []) => {
21 | let xml = '';
22 | sortNotes.forEach(
23 | (note) =>
24 | (xml += `
25 | ${note.title}:
26 |
27 | `),
28 | );
29 | $('#sortNote').html(xml);
30 | };
31 |
32 | // generate array visualizer
33 | function generateVisualizer() {
34 | const type = typeAlg === 'random' ? 0 : typeAlg === 'sorted' ? 1 : 2;
35 |
36 | // max value of item in is max height graph
37 | const maxValue = $('#graph').height() - 100;
38 |
39 | // generate array
40 | initArr = generateRandomData(size, type, maxValue);
41 |
42 | // render array
43 | toggleSortAnalysis(false);
44 | $('#graph').empty();
45 | $('#graph').html(renderArray(initArr));
46 | }
47 |
48 | // get description algorithm (*)
49 | function getDescAlg(key = 'bubble') {
50 | switch (key) {
51 | case 'bubble':
52 | return basicBubbleSortDesc;
53 | case 'enBubble':
54 | return enhancedBubbleSortDesc;
55 | case 'oe':
56 | return oeSortDesc;
57 | case 'selection':
58 | return selectionSortDesc;
59 | case 'insertion':
60 | return insertionSortDesc;
61 | case 'quick':
62 | return quickSortDesc;
63 | case 'merge':
64 | return mergeSortDesc;
65 | case 'heap':
66 | return heapSortDesc;
67 | case 'stupid':
68 | return stupidSortDesc;
69 | case 'lsd':
70 | return radixSortLSDDesc;
71 | default:
72 | return basicBubbleSortDesc;
73 | }
74 | }
75 |
76 | // render sort analysis
77 | function renderSortAnalysis(
78 | status = 'Sorting ...',
79 | comparision = 'Processing ...',
80 | swap = 'Processing ...',
81 | arrAccess = 'Processing ...',
82 | ) {
83 | $('#sortStatus').text(status);
84 | $('#numOfComparision').text(comparision);
85 | $('#numOfSwap').text(swap);
86 | $('#numOfArrAccess').text(arrAccess);
87 | }
88 |
89 | // toggle sort analysis
90 | function toggleSortAnalysis(open = false) {
91 | if (open) $('#sortAnalysis').css('display', 'flex');
92 | else $('#sortAnalysis').css('display', 'none');
93 | }
94 |
95 | // animation end sort
96 | function changeOpacity(item) {
97 | return new Promise((resolve) => {
98 | setTimeout(() => {
99 | item.style.opacity = 0.4;
100 | resolve();
101 | }, 10);
102 | });
103 | }
104 |
105 | async function endSort() {
106 | const arr = $('#graph').children();
107 | let n = arr.length;
108 | for (let i = 0; i < n; ++i) {
109 | await changeOpacity(arr[i]);
110 | }
111 | $('#graph').children().css('opacity', 1);
112 | }
113 |
114 | // constant
115 | const MAX_SIZE = 2000,
116 | MAX_DELAY = 2000;
117 |
118 | // (*)
119 | const OPTION_ALGORITHMS = [
120 | {
121 | title: 'Basic Bubble Sort',
122 | value: 'bubble',
123 | },
124 | {
125 | title: 'Enhanced Bubble Sort',
126 | value: 'enBubble',
127 | },
128 | {
129 | title: 'Odd Even / Brick Sort',
130 | value: 'oe',
131 | },
132 | {
133 | title: 'Selection Sort',
134 | value: 'selection',
135 | },
136 | {
137 | title: 'Insertion Sort',
138 | value: 'insertion',
139 | },
140 | {
141 | title: 'Quick Sort',
142 | value: 'quick',
143 | },
144 | {
145 | title: 'Merge Sort',
146 | value: 'merge',
147 | },
148 | {
149 | title: 'Heap Sort',
150 | value: 'heap',
151 | },
152 | {
153 | title: 'Radix Sort (LSD)',
154 | value: 'lsd',
155 | },
156 | {
157 | title: 'Stupid Sort',
158 | value: 'stupid',
159 | },
160 | ];
161 |
162 | // initial
163 | let size = 1,
164 | delay = 0,
165 | typeAlg = 'random',
166 | algorithm = OPTION_ALGORITHMS[0].value,
167 | initArr = generateRandomData(size, 0, 300),
168 | isSorting = false,
169 | descAlg = getDescAlg(algorithm);
170 |
171 | // @render
172 | $(document).ready(() => {
173 | // web display
174 | $('#hideControlBtn').click(function () {
175 | const thisEle = $(this);
176 |
177 | if (thisEle.hasClass('more')) {
178 | thisEle.removeClass('more').addClass('less');
179 | thisEle
180 | .children('.fas')
181 | .removeClass('fa-chevron-circle-up')
182 | .addClass('fa-chevron-circle-down');
183 |
184 | $('.sort-input').hide(250);
185 | $('.sort-btn').hide(250);
186 | $('#hideControlBtn').css({
187 | top: 2,
188 | });
189 | $('#sortControl').css('border-bottom', 'none');
190 | } else {
191 | thisEle.removeClass('less').addClass('more');
192 | thisEle
193 | .children('.fas')
194 | .removeClass('fa-chevron-circle-down')
195 | .addClass('fa-chevron-circle-up');
196 | $('.sort-input').show(250);
197 | $('.sort-btn').show(250);
198 | $('#hideControlBtn').css({
199 | top: 18,
200 | });
201 | $('#sortControl').css(
202 | 'border-bottom',
203 | 'solid 1px var(--secondary-color)',
204 | );
205 | }
206 | });
207 |
208 | renderSortNotes(basicBubbleSortDesc.sortNotes);
209 |
210 | // show/close modal
211 | $('#descAlgBtn').click(() => {
212 | $('#descTitle').text(descAlg.title);
213 | $('#descContent').html(descAlg.htmlContent);
214 | $('#modalOverlay').show();
215 | $('#descAlgModal').fadeIn(350);
216 | });
217 |
218 | $('#closeModal').click(() => {
219 | $('#modalOverlay').hide();
220 | $('#descAlgModal').fadeOut(350);
221 | });
222 |
223 | // initial select
224 | $('#algorithm').append(renderOptionSelect(OPTION_ALGORITHMS));
225 | $('#type').append(renderOptionSelect(ARRAY_TYPES));
226 | $('#graph').html(renderArray(initArr));
227 |
228 | // check size input change
229 | $('#size').change(function () {
230 | isSorting = false;
231 |
232 | const val = parseInt($(this).val());
233 |
234 | if (val > MAX_SIZE) {
235 | $(this).val(MAX_SIZE);
236 | size = MAX_SIZE;
237 | } else if (val < 1 || isNaN(val)) {
238 | $(this).val(1);
239 | size = 1;
240 | } else size = val;
241 |
242 | generateVisualizer();
243 |
244 | // enable button sort
245 | $('#sortBtn').removeClass('disabled');
246 | });
247 |
248 | // check delay input change
249 | $('#delay').change(function () {
250 | const val = parseInt($(this).val());
251 | if (val > MAX_DELAY) {
252 | $(this).val(MAX_DELAY);
253 | delay = MAX_DELAY;
254 | } else if (val < 0 || isNaN(val)) {
255 | $(this).val(0);
256 | delay = 0;
257 | } else delay = val;
258 | });
259 |
260 | // check algorithm change
261 | $('#algorithm').change(function () {
262 | if (isSorting) {
263 | isSorting = false;
264 | $('#graph').html(renderArray(initArr));
265 | toggleSortAnalysis(false);
266 | // enable button sort
267 | $('#sortBtn').removeClass('disabled');
268 | }
269 | algorithm = $(this).val();
270 | descAlg = getDescAlg(algorithm);
271 | renderSortNotes(descAlg.sortNotes);
272 | });
273 |
274 | // check type algorithm change
275 | $('#type').change(function () {
276 | $('#sortBtn').removeClass('disabled');
277 | isSorting = false;
278 | typeAlg = $(this).val();
279 | generateVisualizer();
280 | });
281 |
282 | // generate array
283 | $('#generateBtn').click(() => {
284 | // Stop sorting if it's sorting
285 | isSorting = false;
286 |
287 | generateVisualizer();
288 |
289 | // enable button sort
290 | $('#sortBtn').removeClass('disabled');
291 | });
292 |
293 | // get last unsorted array
294 | $('#oldUnsortedBtn').click(function () {
295 | if (isSorting) {
296 | isSorting = false;
297 | toggleSortAnalysis(false);
298 | $('#graph').html(renderArray(initArr));
299 | // enable button sort
300 | $('#sortBtn').removeClass('disabled');
301 | }
302 | });
303 |
304 | // ! sorting (*)
305 | $('#sortBtn').click(async function () {
306 | $(this).addClass('disabled');
307 | renderSortAnalysis();
308 | toggleSortAnalysis(true);
309 | isSorting = true;
310 | let resultAnalys = null;
311 | switch (algorithm) {
312 | case 'bubble':
313 | resultAnalys = await basicBubbleSort([...initArr]);
314 | break;
315 | case 'enBubble':
316 | resultAnalys = await enhancedBubbleSort([...initArr]);
317 | break;
318 | case 'oe':
319 | resultAnalys = await oddEvenSort([...initArr]);
320 | break;
321 | case 'selection':
322 | resultAnalys = await selectionSort([...initArr]);
323 | break;
324 | case 'insertion':
325 | resultAnalys = await insertionSort([...initArr]);
326 | break;
327 | case 'quick':
328 | resultAnalys = await quickSort([...initArr], 0, initArr.length - 1);
329 | nQSCompare = 0;
330 | nQSArrAccess = 0;
331 | nQSSwap = 0;
332 | break;
333 | case 'merge':
334 | resultAnalys = await mergeSort([...initArr], 0, initArr.length - 1);
335 | nMSCompare = 0;
336 | nMSArrAccess = 0;
337 | nMSSwap = 0;
338 | break;
339 | case 'heap':
340 | resultAnalys = await heapSort([...initArr]);
341 | nHSCompare = 0;
342 | nHSArrAccess = 0;
343 | nHSSwap = 0;
344 | break;
345 | case 'stupid':
346 | resultAnalys = await stupidSort([...initArr]);
347 | nStCompare = 0;
348 | nStArrAccess = 0;
349 | nStSwap = 0;
350 | break;
351 | case 'lsd':
352 | resultAnalys = await radixSortLSD([...initArr]);
353 | break;
354 | default:
355 | break;
356 | }
357 |
358 | const { nSwap, nCompare, nArrAccess } = resultAnalys;
359 | endSort();
360 | renderSortAnalysis('Done', nCompare, nSwap, nArrAccess);
361 | });
362 | });
363 |
--------------------------------------------------------------------------------
/scripts/sort-visualizer/insertion-sort.js:
--------------------------------------------------------------------------------
1 | const INSERTED_ITEM = SECOND_ITEM_COLOR;
2 |
3 | async function insertionSort(arr = []) {
4 | let n = arr.length,
5 | nSwap = 0,
6 | nCompare = 0,
7 | nArrAccess = 0;
8 | let type = 0;
9 | if (n <= 20) type = 1;
10 |
11 | let i = 1,
12 | j;
13 |
14 | while (i < n) {
15 | if (!isSorting) return { nCompare, nArrAccess, nSwap };
16 | await changeItemColor(i, CURRENT_ITEM_COLOR);
17 | ++nCompare;
18 | nArrAccess += 2;
19 | if (arr[i] >= arr[i - 1]) ++i;
20 | else {
21 | const current = arr[i];
22 | j = i - 1;
23 | // insert item into suitable position
24 | while (j >= 0 && arr[j] > current) {
25 | ++nCompare;
26 | ++nSwap;
27 | nArrAccess += 2;
28 | await changeItemColor(j + 1, '#fff');
29 | await swapEle(j, j + 1);
30 | if (j + 1 === i) await changeItemColor(j + 1, CURRENT_ITEM_COLOR);
31 | arr[j + 1] = arr[j];
32 | --j;
33 | }
34 |
35 | arr[j + 1] = current;
36 | ++nArrAccess;
37 |
38 | await changeValue(j + 1, current, type);
39 | await changeItemColor(j + 1, ITEM_COLOR);
40 | ++i;
41 | }
42 | await changeItemColor(i - 1, ITEM_COLOR);
43 | }
44 |
45 | return { nCompare, nArrAccess, nSwap };
46 | }
47 |
48 | // description
49 | const insertionSortCode = Prism.highlight(
50 | `function insertionSort(arr = []) {
51 | let n = arr.length;
52 |
53 | let i = 1, j = 0;
54 |
55 | while (i < n) {
56 | if (arr[i] >= arr[i - 1]) ++i;
57 | else {
58 | const current = arr[i];
59 | j = i - 1;
60 |
61 | // Chèn phần tử vào vị trí thích hợp
62 | while (j >= 0 && arr[j] > current) {
63 | arr[j + 1] = arr[j];
64 | --j;
65 | }
66 |
67 | arr[j + 1] = current;
68 | ++i;
69 | }
70 | }
71 |
72 | return arr;
73 | }`,
74 | Prism.languages.javascript,
75 | 'javascript',
76 | );
77 | const insertionSortDesc = {
78 | title: 'INSERTION SORT ALGORITHM',
79 | sortNotes: [
80 | {
81 | title: 'P.tử hiện tại',
82 | color: CURRENT_ITEM_COLOR,
83 | },
84 | ],
85 | htmlContent: `
86 | 1) Độ phức tạp thuật toán (Algorithm Complexity BigO):
87 |
88 |
89 |
90 |
- Độ phức tạp thời gian (Time Complexity):
91 |
92 |
93 | Best case: O(n)
94 |
95 | Average case: O(n2 )
96 |
97 | Worst case: O(n2 )
98 |
99 |
100 |
101 |
- Độ phức tạp bộ nhớ (Menory Complexity):
102 |
103 |
Worst case: O(1)
104 |
105 | 2) Ý tưởng thuật toán (Algorithm Idea):
106 |
107 |
108 |
- Đầu vào:
109 |
110 |
111 | - arr : mảng cần sắp xếp.
112 | - n : số lượng phần tử của mảng.
113 | - i : chỉ số index vòng lặp ngoài (1 -> n).
114 | - j : chỉ số vòng lặp trong (i - 1 -> 0)
115 |
116 |
- Ý tưởng:
117 |
118 |
119 | - Sử dụng 2 vòng lặp lồng nhau để chia mảng thành 2 mảng con (1 đã
120 | sắp xếp, 1 không).
121 | - Vòng lặp bên ngoài để duyệt qua mảng.
122 | - Vòng lặp bên trong để chọn vị trí chèn thích hợp cho phần tử i
123 | (insertion).
124 |
125 |
126 | 3) Triển khai (Implement Algorithm):
127 |
128 |
${insertionSortCode}
129 |
130 | 4) Ghi chú (Note):
131 |
132 | - Không nên dùng cho mảng kích thước lớn.
133 | - Chỉ nên sử dụng khi mảng gần như đã sắp xếp như bảng Highscore
134 | list...
135 |
`,
136 | };
137 |
--------------------------------------------------------------------------------
/scripts/sort-visualizer/merge-sort.js:
--------------------------------------------------------------------------------
1 | const MIDDLE_ITEM_COLOR = CURRENT_ITEM_COLOR,
2 | MERGE_INDEX_COLOR = SECOND_ITEM_COLOR;
3 |
4 | let nMSCompare = 0,
5 | nMSArrAccess = 0,
6 | nMSSwap = 0;
7 |
8 | // @fn: Hàm gộp 2 mảng lại với nhau
9 | async function mergeArr(arr, l, middle, r) {
10 | let flag = arr.length <= 20;
11 | if (!isSorting)
12 | return { nSwap: nMSSwap, nCompare: nMSCompare, nArrAccess: nMSArrAccess };
13 |
14 | // tạo 2 mảng trái, phải đã sắp xếp
15 | let leftArr = [...arr.slice(l, middle + 1)],
16 | rightArr = [...arr.slice(middle + 1, r + 1)];
17 |
18 | let mergeIndex = l,
19 | i = 0,
20 | j = 0,
21 | n1 = middle + 1 - l, // số phần tử mảng trái
22 | n2 = r - middle; // số phấn tử mảng phải
23 |
24 | nMSArrAccess += n1 + n2;
25 |
26 | // gộp 2 mảng lại
27 | while (i < n1 && j < n2) {
28 | let newVal = leftArr[i] < rightArr[j] ? leftArr[i++] : rightArr[j++];
29 | nMSCompare += 3;
30 | await changeItemColor(mergeIndex, MERGE_INDEX_COLOR);
31 | await changeValue(mergeIndex, newVal, flag);
32 | await changeItemColor(mergeIndex, ITEM_COLOR);
33 | arr[mergeIndex++] = newVal;
34 | nMSArrAccess += 4;
35 | }
36 | nMSCompare += 2;
37 |
38 | // gán hết phần tử mảng trái nếu còn
39 | while (i < n1) {
40 | let t = leftArr[i++];
41 | await changeItemColor(mergeIndex, MERGE_INDEX_COLOR);
42 | await changeValue(mergeIndex, t, flag);
43 | await changeItemColor(mergeIndex, ITEM_COLOR);
44 | arr[mergeIndex++] = t;
45 | nMSArrAccess += 2;
46 | }
47 | nMSCompare++;
48 |
49 | // gán hết phần tử mảng phải nếu còn
50 | while (j < n2) {
51 | let t = rightArr[j++];
52 | await changeItemColor(mergeIndex, MERGE_INDEX_COLOR);
53 | await changeValue(mergeIndex, t, flag);
54 | await changeItemColor(mergeIndex, ITEM_COLOR);
55 | arr[mergeIndex++] = t;
56 | nMSArrAccess += 2;
57 | }
58 | nMSCompare++;
59 | }
60 |
61 | // @fn: merge sort
62 | async function mergeSort(arr = [], l, r) {
63 | // điều kiện dừng đệ quy
64 | if (l < r) {
65 | nMSCompare++;
66 |
67 | if (!isSorting)
68 | return { nSwap: nMSSwap, nCompare: nMSCompare, nArrAccess: nMSArrAccess };
69 |
70 | // tìm phần tử trung gian để tách mảng
71 | let middle = parseInt((l + r) / 2);
72 | await changeItemColor(middle, MIDDLE_ITEM_COLOR);
73 | await changeItemColor(middle, ITEM_COLOR);
74 |
75 | await mergeSort(arr, l, middle); // tách nửa mảng trái
76 | await mergeSort(arr, middle + 1, r); // tách nửa mảng phải
77 | await mergeArr(arr, l, middle, r); // gộp 2 mảng trái phải
78 |
79 | return { nSwap: nMSSwap, nCompare: nMSCompare, nArrAccess: nMSArrAccess };
80 | }
81 | }
82 |
83 | // description
84 | const mSCode = Prism.highlight(
85 | `// Hàm gộp 2 mảng lại với nhau
86 | function mergeArr(arr, l, middle, r) {
87 | // tạo 2 mảng trái, phải đã sắp xếp
88 | let leftArr = [...arr.slice(l, middle + 1)],
89 | rightArr = [...arr.slice(middle + 1, r + 1)];
90 |
91 | let mergeIndex = l,
92 | i = 0,
93 | j = 0,
94 | n1 = middle + 1 - l, // số phần tử mảng trái
95 | n2 = r - middle; // số phấn tử mảng phải
96 |
97 | // gộp 2 mảng lại
98 | while (i < n1 && j < n2)
99 | arr[mergeIndex++] = leftArr[i] < rightArr[j] ? leftArr[i++] : rightArr[j++];
100 |
101 | // gán hết phần tử mảng trái nếu còn
102 | while (i < n1) arr[mergeIndex++] = leftArr[i++];
103 |
104 | // gán hết phần tử mảng phải nếu còn
105 | while (j < n2) arr[mergeIndex++] = rightArr[j++];
106 | }
107 |
108 | // Merge sort
109 | function mergeSort(arr = [], l, r) {
110 | // điều kiện dừng đệ quy
111 | if (l >= r) return;
112 |
113 | // tìm phần tử trung gian để tách mảng
114 | let middle = parseInt((l + r) / 2);
115 |
116 | mergeSort(arr, l, middle); // tách nửa mảng trái
117 | mergeSort(arr, middle + 1, r); // tách nửa mảng phải
118 | mergeArr(arr, l, middle, r); // gộp 2 mảng trái phải
119 | }`,
120 | Prism.languages.javascript,
121 | 'javascript',
122 | );
123 | const mergeSortDesc = {
124 | title: 'MERGE SORT ALGORITHM',
125 | sortNotes: [
126 | {
127 | title: 'P.tử giữa',
128 | color: CURRENT_ITEM_COLOR,
129 | },
130 | {
131 | title: 'Vị trí gộp',
132 | color: SECOND_ITEM_COLOR,
133 | },
134 | ],
135 | htmlContent: `
136 | 1) Độ phức tạp thuật toán (Algorithm Complexity BigO):
137 |
138 |
139 |
140 |
- Độ phức tạp thời gian (Time Complexity):
141 |
142 |
143 | Best case: O( n log(n) )
144 |
145 | Average case: O( n log(n) )
146 |
147 | Worst case: O( n log(n) )
148 |
149 |
150 |
151 |
- Độ phức tạp bộ nhớ (Menory Complexity):
152 |
153 |
Worst case: O(n)
154 |
155 | 2) Ý tưởng thuật toán (Algorithm Idea):
156 |
157 |
158 |
- Đầu vào:
159 |
160 |
161 | - arr : mảng cần sắp xếp.
162 | - n : số lượng phần tử của mảng.
163 | - l : index phần tử bắt đầu duyệt.
164 | - r : index phần tử cuối cùng cần duyệt.
165 |
166 |
- Ý tưởng:
167 |
168 |
169 | - Tách mảng ban đầu thành 2 mảng dựa trên phần tử middle: (n - 1)/2.
170 | - Cứ thế đệ quy, tách đến khi các mảng còn 1 phần tử.
171 | - Gộp các mảng trên lại, vừa gộp vừa sắp xếp.
172 |
173 |
174 | 3) Triển khai (Implement Algorithm):
175 |
176 |
3.1 - Minh hoạ ý tưởng:
177 |
183 |
Nguồn: https://www.programiz.com/
184 |
3.2 - Hàm merge sort:
185 |
${mSCode}
186 |
187 |
188 | 4) Ghi chú (Note):
189 |
190 | - Nên dùng cho mảng kích thước lớn.
191 | - Thuật toán khá ổn định trong mọi trường hợp.
192 | - Nhược điểm:
193 | + Tốn khá nhiều không gian phụ để gộp mảng
194 |
`,
195 | };
196 |
--------------------------------------------------------------------------------
/scripts/sort-visualizer/odd-even-sort.js:
--------------------------------------------------------------------------------
1 | async function oddEvenSort(arr = []) {
2 | const n = arr.length;
3 | let nSwap = 0,
4 | nCompare = 0,
5 | nArrAccess = 0,
6 | isSorted = false;
7 |
8 | while (!isSorted) {
9 | isSorted = true;
10 | if (!isSorting) return { nSwap, nArrAccess, nCompare };
11 |
12 | // Sắp xếp mảng chẵn
13 | for (let i = 0; i <= n - 2; i += 2) {
14 | if (!isSorting) return { nSwap, nArrAccess, nCompare };
15 |
16 | nArrAccess += 2;
17 | nCompare++;
18 |
19 | if (arr[i] > arr[i + 1]) {
20 | await preSwap(i, i + 1);
21 | [arr[i], arr[i + 1]] = [arr[i + 1], arr[i]];
22 | isSorted = false;
23 | await swapEle(i, i + 1);
24 |
25 | nSwap++;
26 | nArrAccess += 2;
27 | await endSwap(i, i + 1);
28 | }
29 | }
30 |
31 | // Sắp xếp mảng lẻ
32 | for (let i = 1; i <= n - 2; i += 2) {
33 | if (!isSorting) return { nSwap, nArrAccess, nCompare };
34 |
35 | nArrAccess += 2;
36 | nCompare++;
37 | if (arr[i] > arr[i + 1]) {
38 | await preSwap(i, i + 1);
39 |
40 | [arr[i], arr[i + 1]] = [arr[i + 1], arr[i]];
41 | isSorted = false;
42 | await swapEle(i, i + 1);
43 |
44 | nSwap++;
45 | nArrAccess += 2;
46 | await endSwap(i, i + 1);
47 | }
48 | }
49 | }
50 |
51 | return { nSwap, nArrAccess, nCompare };
52 | }
53 |
54 | // description
55 | const oeSortCode = Prism.highlight(
56 | `function oddEvenSort(arr = []) {
57 | const n = arr.length;
58 | let isSorted = false;
59 |
60 | while (!isSorted) {
61 | isSorted = true;
62 |
63 | // Sắp xếp mảng chẵn
64 | for (let i = 0; i <= n - 2; i += 2) {
65 | if (arr[i] > arr[i + 1]) {
66 | [arr[i], arr[i + 1]] = [arr[i + 1], arr[i]];
67 | isSorted = false;
68 | }
69 | }
70 |
71 | // Sắp xếp mảng lẻ
72 | for (let i = 1; i <= n - 2; i += 2) {
73 | if (arr[i] > arr[i + 1]) {
74 | [arr[i], arr[i + 1]] = [arr[i + 1], arr[i]];
75 | isSorted = false;
76 | }
77 | }
78 | }
79 |
80 | return arr;
81 | }
82 | `,
83 | Prism.languages.javascript,
84 | 'javascript',
85 | );
86 |
87 | const oeSortDesc = {
88 | title: '0️⃣1️⃣ ODD EVEN SORT / BRICK SORT 🧱',
89 | sortNotes: [
90 | {
91 | title: 'P.tử hiện tại',
92 | color: CURRENT_ITEM_COLOR,
93 | },
94 | {
95 | title: 'P.tử kế tiếp',
96 | color: SECOND_ITEM_COLOR,
97 | },
98 | ],
99 | htmlContent: `
100 | 1) Độ phức tạp thuật toán (Algorithm Complexity BigO):
101 |
102 |
103 |
104 |
- Độ phức tạp thời gian (Time Complexity):
105 |
106 |
107 | Best case: O(n)
108 |
109 | Average case: O(n2 )
110 |
111 | Worst case: O(n2 )
112 |
113 |
114 |
115 |
- Độ phức tạp bộ nhớ (Menory Complexity):
116 |
117 |
Worst case: O(1)
118 |
119 | 2) Ý tưởng thuật toán (Algorithm Idea):
120 |
121 |
122 |
- Ý tưởng:
123 |
124 |
125 | - Là phiên bản khác cải thiện của bubble sort, chia mảng làm 2 pha chẵn và lẻ, sau đó dùng bubble sort.
126 |
127 |
128 | 3) Triển khai (Implement Algorithm):
129 |
130 |
${oeSortCode}
131 |
132 | 4) Ghi chú (Note):
133 |
134 | - Còn gọi là Brick sort.
135 | - Không nên dùng cho mảng kích thước lớn.
136 | - Chỉ nên sử dụng khi mảng gần như đã sắp xếp như bảng Highscore
137 | list...
138 |
`,
139 | };
140 |
--------------------------------------------------------------------------------
/scripts/sort-visualizer/prepare-sort.js:
--------------------------------------------------------------------------------
1 | // change background color pre swap ele
2 | async function preSwap(left, right) {
3 | return new Promise((resolve, reject) => {
4 | setTimeout(() => {
5 | $('#graph').children()[left].style.backgroundColor = CURRENT_ITEM_COLOR;
6 | $('#graph').children()[right].style.backgroundColor = SECOND_ITEM_COLOR;
7 |
8 | resolve();
9 | }, delay);
10 | });
11 | }
12 |
13 | // swap 2 element i, j
14 | async function swapEle(i, j) {
15 | return new Promise((resolve, reject) => {
16 | setTimeout(() => {
17 | const node1 = $('#graph').children()[i],
18 | node2 = $('#graph').children()[j];
19 |
20 | const afterNode2 = node2.nextElementSibling;
21 | const parent = node2.parentNode;
22 |
23 | node1.replaceWith(node2);
24 | parent.insertBefore(node1, afterNode2);
25 |
26 | resolve();
27 | }, delay);
28 | });
29 | }
30 |
31 | // end swap
32 | async function endSwap(left, right) {
33 | return new Promise((resolve, reject) => {
34 | setTimeout(() => {
35 | $('#graph').children()[left].style.backgroundColor = ITEM_COLOR;
36 | $('#graph').children()[right].style.backgroundColor = ITEM_COLOR;
37 |
38 | resolve();
39 | }, delay);
40 | });
41 | }
42 |
43 | // change color item
44 | async function changeItemColor(index, color) {
45 | return new Promise((resolve, reject) => {
46 | setTimeout(() => {
47 | let item = $('#graph').children()[index];
48 | if (item) item.style.backgroundColor = color;
49 | resolve();
50 | }, delay);
51 | });
52 | }
53 |
54 | // change value item i -> value item j
55 | async function changeValue(i, value, type = 0) {
56 | return new Promise((resolve, reject) => {
57 | setTimeout(() => {
58 | let item = $('.arr-item')[i];
59 | item.style.height = `${value}px`;
60 | if (type) item.textContent = value;
61 | resolve();
62 | }, delay);
63 | });
64 | }
65 |
--------------------------------------------------------------------------------
/scripts/sort-visualizer/quick-sort.js:
--------------------------------------------------------------------------------
1 | const PIVOT_COLOR = DONE_ITEM_COLOR;
2 | let nQSCompare = 0,
3 | nQSArrAccess = 0,
4 | nQSSwap = 0;
5 |
6 | async function partitionFunc(arr = [], low, high) {
7 | await changeItemColor(high, PIVOT_COLOR);
8 | let pivot = arr[high];
9 | let l = low;
10 | let r = high - 1;
11 |
12 | while (true) {
13 | while (l <= r && arr[l] < pivot) {
14 | l++;
15 | ++nQSArrAccess;
16 | nQSCompare += 2;
17 | }
18 | while (r >= l && arr[r] > pivot) {
19 | r--;
20 | ++nQSArrAccess;
21 | nQSCompare += 2;
22 | }
23 | if (l >= r) break;
24 | await preSwap(l, r);
25 | [arr[l], arr[r]] = [arr[r], arr[l]];
26 | ++nQSCompare;
27 | nQSArrAccess += 2;
28 | nQSSwap++;
29 | await swapEle(l, r);
30 | await endSwap(l, r);
31 | l++;
32 | r--;
33 | }
34 |
35 | await preSwap(l, high);
36 | [arr[l], arr[high]] = [arr[high], arr[l]];
37 | await swapEle(l, high);
38 | await endSwap(l, high);
39 |
40 | await changeItemColor(l, PIVOT_COLOR);
41 | return l;
42 | }
43 |
44 | async function quickSort(arr, low, high) {
45 | if (low < high) {
46 | if (!isSorting) return { nQSSwap, nQSCompare, nQSArrAccess };
47 | const pivot = await partitionFunc(arr, low, high);
48 | await changeItemColor(pivot, ITEM_COLOR);
49 | await quickSort(arr, low, pivot - 1);
50 | await quickSort(arr, pivot + 1, high);
51 | }
52 | return { nSwap: nQSSwap, nCompare: nQSCompare, nArrAccess: nQSArrAccess };
53 | }
54 |
55 | // description
56 | const qSPivotCode = Prism.highlight(
57 | `function partitionFunc(arr = [], low, high) {
58 | let pivot = arr[high];
59 | let l = low;
60 | let r = high - 1;
61 |
62 | while (true) {
63 | // Tìm phần tử bên trái bị sai vị trí
64 | while (l <= r && arr[l] < pivot) l++;
65 |
66 | // Tìm phần tử bên phải bị sai vị trí
67 | while (r >= l && arr[r] > pivot) r--;
68 |
69 | // Nếu đã hoàn tất duyệt thì dừng
70 | if (l >= r) break;
71 |
72 | // Ngược lại thì swap 2 phần tử bị sai, sau đó tăng trái và phải lên 1
73 | [arr[l], arr[r]] = [arr[r], arr[l]];
74 | l++;
75 | r--;
76 | }
77 |
78 | // Đưa phần tử pivot vào đúng vị trí
79 | [arr[l], arr[high]] = [arr[high], arr[l]];
80 |
81 | // Trả về vị trí pivot hiện tại
82 | return l;
83 | }`,
84 | Prism.languages.javascript,
85 | 'javascript',
86 | );
87 | const qSCode = Prism.highlight(
88 | `function quickSort(arr, low, high) {
89 | if (low < high) {
90 | // Tìm phần tử pivot
91 | const pivot = partitionFunc(arr, low, high);
92 |
93 | // Đệ quy nửa mảng bên trái
94 | quickSort(arr, low, pivot - 1);
95 |
96 | // Đệ quy nửa mảng bên phải
97 | quickSort(arr, pivot + 1, high);
98 | }
99 | }`,
100 | Prism.languages.javascript,
101 | 'javascript',
102 | );
103 | const quickSortDesc = {
104 | title: '⚡ QUICK SORT ALGORITHM 🐢',
105 | sortNotes: [
106 | {
107 | title: 'P.tử trái',
108 | color: CURRENT_ITEM_COLOR,
109 | },
110 | {
111 | title: 'P.tử phải',
112 | color: SECOND_ITEM_COLOR,
113 | },
114 | {
115 | title: 'Pivot',
116 | color: DONE_ITEM_COLOR,
117 | },
118 | ],
119 | htmlContent: `
120 | 1) Độ phức tạp thuật toán (Algorithm Complexity BigO):
121 |
122 |
123 |
124 |
- Độ phức tạp thời gian (Time Complexity):
125 |
126 |
127 | Best case: O( n log(n) )
128 |
129 | Average case: O( n log(n) )
130 |
131 | Worst case (mảng đã sắp xếp hay pivot phần tử lớn nhất (nhỏ nhất) của mảng): O(n2 )
132 |
133 |
134 |
135 |
- Độ phức tạp bộ nhớ (Menory Complexity):
136 |
137 |
Worst case: O( log(n) )
138 |
139 | 2) Ý tưởng thuật toán (Algorithm Idea):
140 |
141 |
142 |
- Đầu vào:
143 |
144 |
145 | - arr : mảng cần sắp xếp.
146 | - n : số lượng phần tử của mảng.
147 | - low : index phần tử bắt đầu duyệt.
148 | - high : index phần tử cuối cùng cần duyệt.
149 |
150 |
- Ý tưởng:
151 |
152 |
153 | - Lựa chọn một phần tử vách ngăn (pivot).
154 | - Xây dựng một hàm với nhiệm vụ chia mảng thành 2 mảng con sao cho:
155 | + Mảng bên trái bao gồm các phần tử nhỏ hơn pivot.
156 | + Mảng bên phải bao gồm các phần tử lớn hơn pivot.
157 | - Lặp lại 2 bước trên với nửa mảng bên trái và nửa mảng bên phải.
158 | - Kết thúc khi mảng chỉ còn 1 phần tử (không thể chia mảng ra nữa).
159 |
160 |
161 | 3) Triển khai (Implement Algorithm):
162 |
163 |
3.1 - Hàm tìm vách ngăn pivot (partition function):
164 |
${qSPivotCode}
165 |
3.2 - Hàm quick sort:
166 |
${qSCode}
167 |
168 | 4) Ghi chú (Note):
169 |
170 | - Nên dùng cho mảng kích thước lớn.
171 | - Không nên dùng khi mảng đã sắp xếp..
172 | - Nhược điểm:
173 | + Thuật toán bị chậm khi chọn pivot không tốt (nhân phẩm kém 😂🙂).
174 | + Tốt thêm bộ nhớ để lưu trữ đệ quy.
175 | - Ưu điểm: Nếu pivot là phần tử gần trung bình cộng của các phần tử mảng thì thuật toán chạy rất nhanh. S
176 |
`,
177 | };
178 |
--------------------------------------------------------------------------------
/scripts/sort-visualizer/radix-sort.js:
--------------------------------------------------------------------------------
1 | // Hàm lấy chữ số tại vị trí posDigit
2 | // posDigit = 0 là hàng đơn vị
3 | function getDigit(num = 0, posDigit = 0) {
4 | // ex: num = -1234, posDigit = 2, expected = 2
5 | let numInt = Math.abs(parseInt(num / 10 ** posDigit)); // numInt = | -1234 / 10^2 | = 12;
6 | let digit = numInt % 10; // digit = 2;
7 |
8 | // Nếu num < 0 và digit = 0 thì trả về -1, ngược lại trả về digit
9 | return num < 0 && digit === 0 ? -1 : digit;
10 | }
11 |
12 | // Tìm số chữ số lớn nhất của mảng
13 | // Chú ý số âm thì cộng thêm 1 chữ số
14 | function getMaxExp(arr = []) {
15 | let exp = 0;
16 |
17 | arr.forEach((num) => {
18 | const len = num.toString().length;
19 | if (len > exp) exp = len;
20 | });
21 |
22 | return exp;
23 | }
24 |
25 | // Hàm radix sort LSD ( Least Significant Digit )
26 | async function radixSortLSD(arr = []) {
27 | let nSwap = 0,
28 | nCompare = 0,
29 | nArrAccess = 0;
30 |
31 | // Tạo 11 cái xô chứa các digit từ -1 đến 9
32 | let buckets = Array.from({ length: 11 }, () => new Array());
33 |
34 | // Số chữ số nhiều nhất, cũng là số lần lặp duyệt mảng
35 | let exp = getMaxExp(arr) || 0;
36 | nArrAccess += arr.length;
37 |
38 | // LSD lấy từ chữ số cuối
39 | let i = 0;
40 | while (i < exp) {
41 | if (!isSorting) return { nSwap, nCompare, nArrAccess };
42 |
43 | // Lặp qua mảng và cho vào các xô
44 | const arrLen = arr.length;
45 | for (let index = 0; index < arrLen; ++index) {
46 | if (!isSorting) return { nSwap, nCompare, nArrAccess };
47 |
48 | const num = arr[index];
49 | await changeItemColor(index, CURRENT_ITEM_COLOR);
50 | const digitIndex = getDigit(num, i) + 1;
51 | buckets[digitIndex].push(num);
52 | await changeItemColor(index, ITEM_COLOR);
53 |
54 | nArrAccess += 3;
55 | }
56 |
57 | // Lấy từ xô trả về mảng
58 | let j = 0;
59 | for (let index = 0; index < 11; ++index) {
60 | if (!isSorting) return { nSwap, nCompare, nArrAccess };
61 |
62 | let numArr = buckets[index];
63 | nArrAccess++;
64 |
65 | // Nếu là lần cuối và xô là xô chứa số âm thì lấy ngược lại các p.tử trong xô
66 | if (i === exp - 1 && index === 0) {
67 | const n = numArr.length;
68 | for (let k = n - 1; k >= 0; --k) {
69 | await changeItemColor(j, SECOND_ITEM_COLOR);
70 | await changeValue(j, numArr[k]);
71 | await changeItemColor(j, ITEM_COLOR);
72 | arr[j++] = numArr[k];
73 | nArrAccess += 2;
74 | }
75 | }
76 | // Ngược lại, thì lấy xuôi từ trong xô
77 | else {
78 | const l = numArr.length;
79 | for (let index = 0; index < l; ++index) {
80 | if (!isSorting) return { nSwap, nCompare, nArrAccess };
81 |
82 | const num = numArr[index];
83 | await changeValue(j, num);
84 | await changeItemColor(j, SECOND_ITEM_COLOR);
85 | await changeItemColor(j, ITEM_COLOR);
86 |
87 | arr[j++] = num;
88 | nArrAccess += 2;
89 | }
90 | }
91 | // Làm trống xô
92 | numArr.length = 0;
93 | }
94 |
95 | ++i;
96 | }
97 |
98 | return { nSwap, nCompare, nArrAccess };
99 | }
100 |
101 | // description
102 | const radixSortLSDCode = Prism.highlight(
103 | `// Hàm lấy chữ số tại vị trí posDigit
104 | // posDigit = 0 là hàng đơn vị
105 | function getDigit(num = 0, posDigit = 0) {
106 | // ex: num = -1234, posDigit = 2, expected = 2
107 | let numInt = Math.abs(parseInt(num / 10 ** posDigit)); // numInt = | -1234 / 10^2 | = 12;
108 | let digit = numInt % 10; // digit = 2;
109 |
110 | // Nếu num < 0 và digit = 0 thì trả về -1, ngược lại trả về digit
111 | return num < 0 && digit === 0 ? -1 : digit;
112 | }
113 |
114 | // Tìm số chữ số lớn nhất của mảng
115 | // Chú ý số âm thì cộng thêm 1 chữ số
116 | function getMaxExp(arr = []) {
117 | let exp = 0;
118 |
119 | arr.forEach((num) => {
120 | const len = num.toString().length;
121 | if (len > exp) exp = len;
122 | });
123 |
124 | return exp;
125 | }
126 |
127 | // Hàm radix sort LSD ( Least Significant Digit )
128 | function radixSortLSD(arr = []) {
129 | // Tạo 11 cái xô chứa các digit từ -1 đến 9
130 | let buckets = Array.from({ length: 11 }, () => new Array());
131 |
132 | // Số chữ số nhiều nhất, cũng là số lần lặp duyệt mảng
133 | let exp = getMaxExp(arr) || 0;
134 |
135 | // LSD lấy từ chữ số cuối
136 | let i = 0;
137 | while (i < exp) {
138 | // Lặp qua mảng và cho vào các xô
139 | arr.forEach((num) => {
140 | const digitIndex = getDigit(num, i) + 1; // Do buckets có index từ 0 -> 10, getDigit() trả về -1 -> 10
141 | buckets[digitIndex].push(num);
142 | });
143 |
144 | // Lấy từ xô trả về mảng
145 | let j = 0;
146 | buckets.forEach((numArr, index) => {
147 | // Nếu là lần cuối và xô là xô chứa số âm thì lấy ngược lại các p.tử trong xô
148 | if (i === exp - 1 && index === 0) {
149 | const n = numArr.length;
150 | for (let k = n - 1; k >= 0; --k) arr[j++] = numArr[k];
151 | }
152 | // Ngược lại, thì lấy xuôi từ trong xô
153 | else numArr.forEach((num) => (arr[j++] = num));
154 |
155 | // Làm trống xô
156 | numArr.length = 0;
157 | });
158 |
159 | ++i;
160 | }
161 |
162 | return arr;
163 | }
164 | `,
165 | Prism.languages.javascript,
166 | 'javascript',
167 | );
168 |
169 | const radixSortLSDDesc = {
170 | title: '1️⃣ RADIX SORT (LSD) ALGORITHM 💯',
171 | sortNotes: [
172 | {
173 | title: 'P.tử hiện tại',
174 | color: CURRENT_ITEM_COLOR,
175 | },
176 | {
177 | title: 'P.tử trong xô',
178 | color: SECOND_ITEM_COLOR,
179 | },
180 | ],
181 | htmlContent: `
182 | 1) Độ phức tạp thuật toán (Algorithm Complexity BigO):
183 |
184 |
185 |
186 |
- Độ phức tạp thời gian (Time Complexity):
187 |
188 |
189 |
190 |
191 | Giả sử ta có 1 mảng các số nguyên có số phần tử n, base (số lượng xô (bucket) chứa các phần tử)
192 | và digit là số chữ số của số lớn nhất trong mảng. Thì độ phức tạp sẽ là:
193 |
194 |
195 |
196 | Best case: O( digit * (n + base) )
197 |
198 | Average case: O( digit * (n + base) )
199 |
200 | Worst case: O( digit * (n + base) )
201 |
202 |
203 |
- Độ phức tạp bộ nhớ (Menory Complexity):
204 |
205 |
Worst case: O( n + base )
206 |
207 | 2) Ý tưởng thuật toán (Algorithm Idea):
208 |
209 |
210 | Số lượng base (bucket) khác nhau thì cách tách phần tử cũng khác nhau.
211 | Ví dụ, ta chọn base = 10 (các xô từ 0 đến 9) thì mỗi lần tách sẽ là 1 chữ số. Còn base = 100 thì mỗi lần tách sẽ là 2 số.
212 |
213 |
214 |
215 |
- Đầu vào:
216 |
217 |
218 | - arr : mảng cần sắp xếp.
219 | - n : số lượng phần tử của mảng.
220 | - buckets : Mảng 2 chiều chứa các số theo cơ số của nó.
221 |
222 |
- Ý tưởng:
223 |
224 |
225 | - Sử dụng một mảng 2 chiều để chứa các số được phân loại theo từng chữ số của chúng.
226 | - Số lần lặp bên ngoài là số lượng chữ số của phần tử lớn nhất trong mảng.
227 | - Mỗi lần lặp ta sẽ tách các chữ số theo từng hàng. Radix LSD (Least Significant Digit) nên ta sẽ bắt đầu từ hàng đơn vị đến hàng chục ...
228 | - Mỗi chữ số sẽ vào 1 xô tương ứng.
229 | - Lấy các xô theo thứ tự trên xuống gán ngược lại mảng ban đầu.
230 | - Tiếp tục như thế đến hàng cuối cùng.
231 |
232 |
233 | 3) Triển khai (Implement Algorithm):
234 |
235 |
3.1 - Mô tả ý tưởng:
236 |
242 |
245 |
246 |
3.2 - Hàm Radix LSD sort:
247 |
${radixSortLSDCode}
248 |
249 | 4) Ghi chú (Note):
250 |
251 | - Là một thuật toán sắp xếp phân tán, ổn định, không dùng phép so sánh.
252 | - Còn được gọi là Digital sort hay Postmans sort.
253 | - Radix còn một phiên bản Radix MSD (Most significant digit).
254 | - Thích hợp cho việc sắp xếp mảng số hoặc chuỗi (chỉ đối với mảng chuỗi cùng chiều dài).
255 | - Nhược điểm:
256 | + Mảng có các phần tử quá chênh lệch nhau về số lượng chữ số. VD: [2, 3, 21, 100000000]
257 | + Tốn không gian tạo bucket.
258 |
`,
259 | };
260 |
--------------------------------------------------------------------------------
/scripts/sort-visualizer/selection-sort.js:
--------------------------------------------------------------------------------
1 | const MIN_ITEM_COLOR = SECOND_ITEM_COLOR;
2 | const SORTED_ITEM_COLOR = DONE_ITEM_COLOR;
3 |
4 | async function selectionSort(arr = []) {
5 | let n = arr.length,
6 | nSwap = 0,
7 | nCompare = 0,
8 | nArrAccess = 0;
9 |
10 | for (let i = 0; i < n - 1; ++i) {
11 | ++nArrAccess;
12 | let min = arr[i];
13 | let index = i;
14 | await changeItemColor(i, MIN_ITEM_COLOR);
15 |
16 | // find min
17 | for (let j = i + 1; j < n; ++j) {
18 | if (!isSorting) return { nCompare, nArrAccess, nSwap };
19 | ++nArrAccess;
20 | ++nCompare;
21 | await changeItemColor(j, CURRENT_ITEM_COLOR);
22 | if (arr[j] < min) {
23 | await changeItemColor(j, MIN_ITEM_COLOR);
24 | await changeItemColor(index, ITEM_COLOR);
25 | min = arr[j];
26 | index = j;
27 | } else {
28 | await changeItemColor(j, ITEM_COLOR);
29 | }
30 | }
31 |
32 | // swap if min != initial min
33 | if (index !== i) {
34 | nArrAccess += 2;
35 | ++nSwap;
36 | [arr[i], arr[index]] = [arr[index], arr[i]];
37 | await swapEle(i, index);
38 | }
39 |
40 | await changeItemColor(i, SORTED_ITEM_COLOR);
41 | }
42 |
43 | return { nCompare, nArrAccess, nSwap };
44 | }
45 |
46 | // description
47 | const selectionSortCode = Prism.highlight(
48 | `function selectionSort(arr = []) {
49 | const n = arr.length;
50 |
51 | for (let i = 0; i < n - 1; ++i) {
52 | let min = arr[i];
53 | let index = i;
54 |
55 | // tìm min
56 | for (let j = i + 1; j < n; ++j) {
57 | if (arr[j] < min) {
58 | min = arr[j];
59 | index = j;
60 | }
61 | }
62 |
63 | // Hoán vị nếu min tìm thấy # min ban đầu
64 | if (index !== i) [arr[i], arr[index]] = [arr[index], arr[i]];
65 | }
66 | }`,
67 | Prism.languages.javascript,
68 | 'javascript',
69 | );
70 |
71 | const selectionSortDesc = {
72 | title: '🔎 SELECTION SORT ALGORITHM ✔',
73 | sortNotes: [
74 | {
75 | title: 'P.tử hiện tại',
76 | color: CURRENT_ITEM_COLOR,
77 | },
78 | {
79 | title: 'P.tử nhỏ nhất',
80 | color: SECOND_ITEM_COLOR,
81 | },
82 | {
83 | title: 'P.tử đã chọn',
84 | color: DONE_ITEM_COLOR,
85 | },
86 | ],
87 | htmlContent: `
88 | 1) Độ phức tạp thuật toán (Algorithm Complexity BigO):
89 |
90 |
91 |
92 |
- Độ phức tạp thời gian (Time Complexity):
93 |
94 |
95 | Best case: O(n2 )
96 |
97 | Average case: O(n2 )
98 |
99 | Worst case: O(n2 )
100 |
101 |
102 |
103 |
- Độ phức tạp bộ nhớ (Menory Complexity):
104 |
105 |
Worst case: O(1)
106 |
107 | 2) Ý tưởng thuật toán (Algorithm Idea):
108 |
109 |
110 |
- Đầu vào:
111 |
112 |
113 | - arr : mảng cần sắp xếp.
114 | - n : số lượng phần tử của mảng.
115 | - i : chỉ số index vòng lặp ngoài (0 -> n-1).
116 | - j : chỉ số vòng lặp trong (i+1 -> n)
117 |
118 |
- Ý tưởng:
119 |
120 |
121 | - Sử dụng 2 vòng lặp lồng nhau để chia mảng thành 2 mảng con (1 đã
122 | sắp xếp, 1 không).
123 | - Vòng lặp bên ngoài để duyệt qua mảng.
124 | - Vòng lặp bên trong duyệt qua mảng con chưa sắp xếp để tìm phần
125 | tử min (max).
126 | - Nếu min (max) trong mảng con đó nhỏ (lớn) hơn phần tử cuối mảng
127 | đã sắp xếp thì swap 2 phần từ đó.
128 |
129 |
130 | 3) Triển khai (Implement Algorithm):
131 |
132 |
${selectionSortCode}
133 |
134 | 4) Ghi chú (Note):
135 |
136 | - Không nên dùng cho mảng kích thước lớn.
137 | - Chỉ nên sử dụng khi mảng gần như đã sắp xếp như bảng Highscore
138 | list...
139 |
`,
140 | };
141 |
--------------------------------------------------------------------------------
/scripts/sort-visualizer/stupid-sort.js:
--------------------------------------------------------------------------------
1 | let nStCompare = 0,
2 | nStArrAccess = 0,
3 | nStSwap = 0;
4 |
5 | async function isSortedArr(arr = []) {
6 | let n = arr.length;
7 |
8 | for (let i = 0; i < n - 1; ++i) {
9 | if (!isSorting) return false;
10 | nStCompare += 2;
11 | nStArrAccess += 2;
12 |
13 | await changeItemColor(i, CURRENT_ITEM_COLOR);
14 | await changeItemColor(i, ITEM_COLOR);
15 |
16 | if (arr[i] > arr[i + 1]) {
17 | return false;
18 | }
19 | }
20 |
21 | return true;
22 | }
23 |
24 | function shuffleArray(arr = []) {
25 | return arr.sort(() => {
26 | nStArrAccess += 2;
27 | nStSwap += 2;
28 | return Math.random() - 0.5;
29 | });
30 | }
31 |
32 | async function reRenderArr(arr) {
33 | let xml = '';
34 |
35 | arr.forEach((item, index) => {
36 | xml += ` `;
37 | });
38 |
39 | $('#graph').html(xml);
40 | }
41 |
42 | async function stupidSort(arr = []) {
43 | while (!(await isSortedArr(arr))) {
44 | nStCompare++;
45 |
46 | if (!isSorting)
47 | return { nSwap: nStSwap, nCompare: nStCompare, nArrAccess: nStArrAccess };
48 |
49 | shuffleArray(arr);
50 | await reRenderArr(arr);
51 | }
52 |
53 | return { nSwap: nStSwap, nCompare: nStCompare, nArrAccess: nStArrAccess };
54 | }
55 |
56 | // description
57 | const stSCode = Prism.highlight(
58 | `// Hàm kiểm tra mảng đã sắp xếp chưa?
59 | function isSortedArr(arr = []) {
60 | for (let i = 0; i < arr.length - 1; ++i)
61 | if (arr[i] > arr[i + 1]) return false;
62 |
63 | return true;
64 | }
65 |
66 | // Hàm xáo trộn mảng
67 | function shuffleArray(arr = []) {
68 | return arr.sort(() => Math.random() - 0.5);
69 | }
70 |
71 | // stupid sort
72 | function stupidSort(arr = []) {
73 | while (!isSortedArr(arr)) shuffleArray(arr);
74 | }
75 | `,
76 | Prism.languages.javascript,
77 | 'javascript',
78 | );
79 |
80 | const stupidSortDesc = {
81 | title: '🍀 STUPID SORT ALGORITHM 🐢',
82 | sortNotes: [
83 | {
84 | title: 'P.tử kiểm tra hiện tại',
85 | color: CURRENT_ITEM_COLOR,
86 | },
87 | ],
88 | htmlContent: `
89 | 1) Độ phức tạp thuật toán (Algorithm Complexity BigO):
90 |
91 |
92 | - O(∞) hay O(nhân phẩm)
93 |
94 | 2) Ý tưởng thuật toán (Algorithm Idea):
95 |
96 |
97 | - Xáo trộn mảng, kiểm tra mảng vừa xáo.
98 | - Nếu đã xếp thì dừng, còn không thì lặp lại 🙂.
99 |
100 |
101 | 3) Triển khai (Implement Algorithm):
102 |
103 |
3.2 - Hàm stupid sort:
104 |
${stSCode}
105 |
106 |
107 | 4) Ghi chú (Note):
108 |
109 | - Dùng để kiểm tra nhân phẩm người dùng. 🐭
110 | - Chán quá thì dùng cho vui chứ không có ý nghĩa gì. 😜
111 |
`,
112 | };
113 |
--------------------------------------------------------------------------------
/sort-visualizer.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Dyno - Sorting Visualizer
9 |
10 |
11 |
12 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
42 |
43 |
44 |
66 |
72 |
73 |
74 |
75 |
76 |
98 |
99 |
100 |
101 | Mô tả
102 |
103 |
104 | Tạo mảng ngẫu nhiên
105 |
106 | Lấy lại mảng cũ
107 |
108 | Sắp xếp
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 | Trạng thái:
121 |
122 |
123 | So sánh:
124 |
125 | Hoán vị:
126 |
127 | Truy cập mảng:
128 |
129 |
130 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
--------------------------------------------------------------------------------
/styles/css/atomic.css:
--------------------------------------------------------------------------------
1 | .container,
2 | .container-fluid,
3 | .container-sm,
4 | .container-md,
5 | .container-lg,
6 | .container-xl {
7 | width: 100%;
8 | padding-right: 8px;
9 | padding-left: 8px;
10 | margin-right: auto;
11 | margin-left: auto;
12 | }
13 |
14 | @media (min-width: 576px) {
15 | .container,
16 | .container-sm {
17 | max-width: 540px;
18 | }
19 | }
20 | @media (min-width: 768px) {
21 | .container,
22 | .container-sm,
23 | .container-md {
24 | max-width: 760px;
25 | }
26 | }
27 | @media (min-width: 992px) {
28 | .container,
29 | .container-sm,
30 | .container-md,
31 | .container-lg {
32 | max-width: 960px;
33 | }
34 | }
35 | @media (min-width: 1200px) {
36 | .container,
37 | .container-sm,
38 | .container-md,
39 | .container-lg,
40 | .container-xl {
41 | max-width: 1140px;
42 | }
43 | }
44 | @media (min-width: 1367px) {
45 | .container,
46 | .container-sm,
47 | .container-md,
48 | .container-lg,
49 | .container-xl {
50 | max-width: 1280px;
51 | }
52 | }
53 | .trans-center {
54 | position: absolute;
55 | top: 50%;
56 | left: 50%;
57 | transform: translate(-50%, -50%);
58 | }
59 | .trans-margin {
60 | display: block;
61 | margin: 0 auto;
62 | }
63 |
64 | .pos-rel {
65 | position: relative;
66 | }
67 | .pos-abs {
68 | position: absolute;
69 | }
70 |
71 | .d-none {
72 | display: none;
73 | }
74 | .d-block {
75 | display: block;
76 | }
77 | .d-flex {
78 | display: flex;
79 | }
80 |
81 | .flex-col {
82 | display: flex;
83 | flex-direction: column;
84 | }
85 | .flex-dir-col {
86 | flex-direction: column;
87 | }
88 | .flex-dir-reverse {
89 | flex-direction: row-reverse;
90 | }
91 | .flex-grow-1 {
92 | flex-grow: 1;
93 | }
94 | .flex-wrap {
95 | flex-wrap: wrap;
96 | }
97 | .flex-center {
98 | display: flex;
99 | align-items: center;
100 | justify-content: center;
101 | }
102 | .flex-center--ver {
103 | display: flex;
104 | align-items: center;
105 | }
106 | .flex-center--col {
107 | display: flex;
108 | flex-direction: column;
109 | align-items: center;
110 | justify-content: center;
111 | }
112 | .flex-center-between {
113 | display: flex;
114 | align-items: center;
115 | justify-content: space-between;
116 | }
117 |
118 | .align-i-center {
119 | align-items: center;
120 | }
121 | .align-i-end {
122 | align-items: flex-end;
123 | }
124 |
125 | .justify-content-around {
126 | justify-content: space-around;
127 | }
128 | .justify-content-between {
129 | justify-content: space-between;
130 | }
131 | .justify-content-end {
132 | justify-content: flex-end;
133 | }
134 | .justify-content-center {
135 | justify-content: center;
136 | }
137 |
138 | .w-100 {
139 | width: 100%;
140 | }
141 |
142 | .h-100 {
143 | height: 100%;
144 | }
145 |
146 | .h-100vh {
147 | height: 100vh;
148 | }
149 |
150 | .w-100vw {
151 | width: 100vw;
152 | }
153 |
154 | .m-1 {
155 | margin: 2px;
156 | }
157 | .m-t-1 {
158 | margin-top: 2px;
159 | }
160 | .m-b-1 {
161 | margin-bottom: 2px;
162 | }
163 | .m-l-1 {
164 | margin-left: 2px;
165 | }
166 | .m-r-1 {
167 | margin-right: 2px;
168 | }
169 | .m-lr-1 {
170 | margin-left: 2px;
171 | margin-right: 2px;
172 | }
173 | .m-tb-1 {
174 | margin-top: 2px;
175 | margin-bottom: 2px;
176 | }
177 |
178 | .p-1 {
179 | padding: 2px;
180 | }
181 | .p-t-1 {
182 | padding-top: 2px;
183 | }
184 | .p-b-1 {
185 | padding-bottom: 2px;
186 | }
187 | .p-l-1 {
188 | padding-left: 2px;
189 | }
190 | .p-r-1 {
191 | padding-right: 2px;
192 | }
193 | .p-lr-1 {
194 | padding-left: 2px;
195 | padding-right: 2px;
196 | }
197 | .p-tb-1 {
198 | padding-top: 2px;
199 | padding-bottom: 2px;
200 | }
201 |
202 | .m-2 {
203 | margin: 4px;
204 | }
205 | .m-t-2 {
206 | margin-top: 4px;
207 | }
208 | .m-b-2 {
209 | margin-bottom: 4px;
210 | }
211 | .m-l-2 {
212 | margin-left: 4px;
213 | }
214 | .m-r-2 {
215 | margin-right: 4px;
216 | }
217 | .m-lr-2 {
218 | margin-left: 4px;
219 | margin-right: 4px;
220 | }
221 | .m-tb-2 {
222 | margin-top: 4px;
223 | margin-bottom: 4px;
224 | }
225 |
226 | .p-2 {
227 | padding: 4px;
228 | }
229 | .p-t-2 {
230 | padding-top: 4px;
231 | }
232 | .p-b-2 {
233 | padding-bottom: 4px;
234 | }
235 | .p-l-2 {
236 | padding-left: 4px;
237 | }
238 | .p-r-2 {
239 | padding-right: 4px;
240 | }
241 | .p-lr-2 {
242 | padding-left: 4px;
243 | padding-right: 4px;
244 | }
245 | .p-tb-2 {
246 | padding-top: 4px;
247 | padding-bottom: 4px;
248 | }
249 |
250 | .m-3 {
251 | margin: 6px;
252 | }
253 | .m-t-3 {
254 | margin-top: 6px;
255 | }
256 | .m-b-3 {
257 | margin-bottom: 6px;
258 | }
259 | .m-l-3 {
260 | margin-left: 6px;
261 | }
262 | .m-r-3 {
263 | margin-right: 6px;
264 | }
265 | .m-lr-3 {
266 | margin-left: 6px;
267 | margin-right: 6px;
268 | }
269 | .m-tb-3 {
270 | margin-top: 6px;
271 | margin-bottom: 6px;
272 | }
273 |
274 | .p-3 {
275 | padding: 6px;
276 | }
277 | .p-t-3 {
278 | padding-top: 6px;
279 | }
280 | .p-b-3 {
281 | padding-bottom: 6px;
282 | }
283 | .p-l-3 {
284 | padding-left: 6px;
285 | }
286 | .p-r-3 {
287 | padding-right: 6px;
288 | }
289 | .p-lr-3 {
290 | padding-left: 6px;
291 | padding-right: 6px;
292 | }
293 | .p-tb-3 {
294 | padding-top: 6px;
295 | padding-bottom: 6px;
296 | }
297 |
298 | .m-4 {
299 | margin: 8px;
300 | }
301 | .m-t-4 {
302 | margin-top: 8px;
303 | }
304 | .m-b-4 {
305 | margin-bottom: 8px;
306 | }
307 | .m-l-4 {
308 | margin-left: 8px;
309 | }
310 | .m-r-4 {
311 | margin-right: 8px;
312 | }
313 | .m-lr-4 {
314 | margin-left: 8px;
315 | margin-right: 8px;
316 | }
317 | .m-tb-4 {
318 | margin-top: 8px;
319 | margin-bottom: 8px;
320 | }
321 |
322 | .p-4 {
323 | padding: 8px;
324 | }
325 | .p-t-4 {
326 | padding-top: 8px;
327 | }
328 | .p-b-4 {
329 | padding-bottom: 8px;
330 | }
331 | .p-l-4 {
332 | padding-left: 8px;
333 | }
334 | .p-r-4 {
335 | padding-right: 8px;
336 | }
337 | .p-lr-4 {
338 | padding-left: 8px;
339 | padding-right: 8px;
340 | }
341 | .p-tb-4 {
342 | padding-top: 8px;
343 | padding-bottom: 8px;
344 | }
345 |
346 | .m-5 {
347 | margin: 10px;
348 | }
349 | .m-t-5 {
350 | margin-top: 10px;
351 | }
352 | .m-b-5 {
353 | margin-bottom: 10px;
354 | }
355 | .m-l-5 {
356 | margin-left: 10px;
357 | }
358 | .m-r-5 {
359 | margin-right: 10px;
360 | }
361 | .m-lr-5 {
362 | margin-left: 10px;
363 | margin-right: 10px;
364 | }
365 | .m-tb-5 {
366 | margin-top: 10px;
367 | margin-bottom: 10px;
368 | }
369 |
370 | .p-5 {
371 | padding: 10px;
372 | }
373 | .p-t-5 {
374 | padding-top: 10px;
375 | }
376 | .p-b-5 {
377 | padding-bottom: 10px;
378 | }
379 | .p-l-5 {
380 | padding-left: 10px;
381 | }
382 | .p-r-5 {
383 | padding-right: 10px;
384 | }
385 | .p-lr-5 {
386 | padding-left: 10px;
387 | padding-right: 10px;
388 | }
389 | .p-tb-5 {
390 | padding-top: 10px;
391 | padding-bottom: 10px;
392 | }
393 |
394 | .m-6 {
395 | margin: 12px;
396 | }
397 | .m-t-6 {
398 | margin-top: 12px;
399 | }
400 | .m-b-6 {
401 | margin-bottom: 12px;
402 | }
403 | .m-l-6 {
404 | margin-left: 12px;
405 | }
406 | .m-r-6 {
407 | margin-right: 12px;
408 | }
409 | .m-lr-6 {
410 | margin-left: 12px;
411 | margin-right: 12px;
412 | }
413 | .m-tb-6 {
414 | margin-top: 12px;
415 | margin-bottom: 12px;
416 | }
417 |
418 | .p-6 {
419 | padding: 12px;
420 | }
421 | .p-t-6 {
422 | padding-top: 12px;
423 | }
424 | .p-b-6 {
425 | padding-bottom: 12px;
426 | }
427 | .p-l-6 {
428 | padding-left: 12px;
429 | }
430 | .p-r-6 {
431 | padding-right: 12px;
432 | }
433 | .p-lr-6 {
434 | padding-left: 12px;
435 | padding-right: 12px;
436 | }
437 | .p-tb-6 {
438 | padding-top: 12px;
439 | padding-bottom: 12px;
440 | }
441 |
442 | .m-7 {
443 | margin: 14px;
444 | }
445 | .m-t-7 {
446 | margin-top: 14px;
447 | }
448 | .m-b-7 {
449 | margin-bottom: 14px;
450 | }
451 | .m-l-7 {
452 | margin-left: 14px;
453 | }
454 | .m-r-7 {
455 | margin-right: 14px;
456 | }
457 | .m-lr-7 {
458 | margin-left: 14px;
459 | margin-right: 14px;
460 | }
461 | .m-tb-7 {
462 | margin-top: 14px;
463 | margin-bottom: 14px;
464 | }
465 |
466 | .p-7 {
467 | padding: 14px;
468 | }
469 | .p-t-7 {
470 | padding-top: 14px;
471 | }
472 | .p-b-7 {
473 | padding-bottom: 14px;
474 | }
475 | .p-l-7 {
476 | padding-left: 14px;
477 | }
478 | .p-r-7 {
479 | padding-right: 14px;
480 | }
481 | .p-lr-7 {
482 | padding-left: 14px;
483 | padding-right: 14px;
484 | }
485 | .p-tb-7 {
486 | padding-top: 14px;
487 | padding-bottom: 14px;
488 | }
489 |
490 | .m-8 {
491 | margin: 16px;
492 | }
493 | .m-t-8 {
494 | margin-top: 16px;
495 | }
496 | .m-b-8 {
497 | margin-bottom: 16px;
498 | }
499 | .m-l-8 {
500 | margin-left: 16px;
501 | }
502 | .m-r-8 {
503 | margin-right: 16px;
504 | }
505 | .m-lr-8 {
506 | margin-left: 16px;
507 | margin-right: 16px;
508 | }
509 | .m-tb-8 {
510 | margin-top: 16px;
511 | margin-bottom: 16px;
512 | }
513 |
514 | .p-8 {
515 | padding: 16px;
516 | }
517 | .p-t-8 {
518 | padding-top: 16px;
519 | }
520 | .p-b-8 {
521 | padding-bottom: 16px;
522 | }
523 | .p-l-8 {
524 | padding-left: 16px;
525 | }
526 | .p-r-8 {
527 | padding-right: 16px;
528 | }
529 | .p-lr-8 {
530 | padding-left: 16px;
531 | padding-right: 16px;
532 | }
533 | .p-tb-8 {
534 | padding-top: 16px;
535 | padding-bottom: 16px;
536 | }
537 |
538 | .m-9 {
539 | margin: 18px;
540 | }
541 | .m-t-9 {
542 | margin-top: 18px;
543 | }
544 | .m-b-9 {
545 | margin-bottom: 18px;
546 | }
547 | .m-l-9 {
548 | margin-left: 18px;
549 | }
550 | .m-r-9 {
551 | margin-right: 18px;
552 | }
553 | .m-lr-9 {
554 | margin-left: 18px;
555 | margin-right: 18px;
556 | }
557 | .m-tb-9 {
558 | margin-top: 18px;
559 | margin-bottom: 18px;
560 | }
561 |
562 | .p-9 {
563 | padding: 18px;
564 | }
565 | .p-t-9 {
566 | padding-top: 18px;
567 | }
568 | .p-b-9 {
569 | padding-bottom: 18px;
570 | }
571 | .p-l-9 {
572 | padding-left: 18px;
573 | }
574 | .p-r-9 {
575 | padding-right: 18px;
576 | }
577 | .p-lr-9 {
578 | padding-left: 18px;
579 | padding-right: 18px;
580 | }
581 | .p-tb-9 {
582 | padding-top: 18px;
583 | padding-bottom: 18px;
584 | }
585 |
586 | .m-10 {
587 | margin: 20px;
588 | }
589 | .m-t-10 {
590 | margin-top: 20px;
591 | }
592 | .m-b-10 {
593 | margin-bottom: 20px;
594 | }
595 | .m-l-10 {
596 | margin-left: 20px;
597 | }
598 | .m-r-10 {
599 | margin-right: 20px;
600 | }
601 | .m-lr-10 {
602 | margin-left: 20px;
603 | margin-right: 20px;
604 | }
605 | .m-tb-10 {
606 | margin-top: 20px;
607 | margin-bottom: 20px;
608 | }
609 |
610 | .p-10 {
611 | padding: 20px;
612 | }
613 | .p-t-10 {
614 | padding-top: 20px;
615 | }
616 | .p-b-10 {
617 | padding-bottom: 20px;
618 | }
619 | .p-l-10 {
620 | padding-left: 20px;
621 | }
622 | .p-r-10 {
623 | padding-right: 20px;
624 | }
625 | .p-lr-10 {
626 | padding-left: 20px;
627 | padding-right: 20px;
628 | }
629 | .p-tb-10 {
630 | padding-top: 20px;
631 | padding-bottom: 20px;
632 | }
633 |
634 | .m-lr-auto {
635 | margin-left: auto;
636 | margin-right: auto;
637 | }
638 | .m-0-auto {
639 | margin: 0 auto;
640 | }
641 | .m-auto-0 {
642 | margin: auto 0;
643 | }
644 | .m-auto {
645 | margin: auto;
646 | }
647 | .m-0 {
648 | margin: 0;
649 | }
650 |
651 | .t-center {
652 | text-align: center;
653 | }
654 | .t-left {
655 | text-align: left;
656 | }
657 | .t-right {
658 | text-align: right;
659 | }
660 | .t-end {
661 | text-align: end;
662 | }
663 | .t-justify {
664 | text-align: justify;
665 | }
666 |
667 | .fw-b {
668 | font-weight: bold;
669 | }
670 |
671 | /*# sourceMappingURL=atomic.css.map */
672 |
--------------------------------------------------------------------------------
/styles/css/home.css:
--------------------------------------------------------------------------------
1 | #aboutMe h1 {
2 | min-width: 80vw;
3 | text-align: center;
4 | font-size: 5.4rem;
5 | font-weight: bold;
6 | color: var(--title-color);
7 | transition: all 2.2s ease-in-out;
8 | }
9 | #aboutMe h3 {
10 | text-align: center;
11 | font-size: 2.4rem;
12 | color: var(--secondary-color);
13 | font-weight: 900;
14 | margin-top: 16px;
15 | }
16 |
17 | .contact-info li {
18 | font-size: 2.8rem;
19 | padding: 0.8rem 1.2rem;
20 | color: var(--text-color);
21 | cursor: pointer;
22 | transition: all 0.25s ease-in-out;
23 | }
24 | .contact-info li:hover, .contact-info li:active {
25 | transform: scale(1.2);
26 | opacity: 0.85;
27 | }
28 |
29 | .logo-ani {
30 | -webkit-animation: logoAni 2.6s ease-in-out forwards;
31 | animation: logoAni 2.6s ease-in-out forwards;
32 | }
33 |
34 | @-webkit-keyframes logoAni {
35 | 0% {
36 | transform: translateY(-10px);
37 | }
38 | 10% {
39 | transform: translateY(-5px);
40 | }
41 | 20% {
42 | transform: translateY(-15px);
43 | }
44 | 30% {
45 | transform: translateY(-3px);
46 | }
47 | 40% {
48 | transform: translateY(-25px);
49 | }
50 | 50% {
51 | transform: translateY(0px);
52 | }
53 | 60% {
54 | transform: translateY(-30px);
55 | }
56 | 70% {
57 | transform: translateY(0);
58 | }
59 | 80% {
60 | transform: translateY(-20px);
61 | }
62 | 85% {
63 | transform: translateY(0);
64 | }
65 | 90% {
66 | transform: translateY(-10px);
67 | }
68 | 100% {
69 | transform: translateY(0);
70 | }
71 | }
72 | @keyframes logoAni {
73 | 0% {
74 | transform: translateY(-10px);
75 | }
76 | 10% {
77 | transform: translateY(-5px);
78 | }
79 | 20% {
80 | transform: translateY(-15px);
81 | }
82 | 30% {
83 | transform: translateY(-3px);
84 | }
85 | 40% {
86 | transform: translateY(-25px);
87 | }
88 | 50% {
89 | transform: translateY(0px);
90 | }
91 | 60% {
92 | transform: translateY(-30px);
93 | }
94 | 70% {
95 | transform: translateY(0);
96 | }
97 | 80% {
98 | transform: translateY(-20px);
99 | }
100 | 85% {
101 | transform: translateY(0);
102 | }
103 | 90% {
104 | transform: translateY(-10px);
105 | }
106 | 100% {
107 | transform: translateY(0);
108 | }
109 | }
110 | @media only screen and (max-width: 576px) {
111 | #aboutMe h1 {
112 | font-size: 3.6rem;
113 | }
114 | #aboutMe h3 {
115 | font-size: 1.8rem;
116 | }
117 |
118 | .logo-ani {
119 | max-width: 152px;
120 | max-height: 152px;
121 | }
122 | }
123 |
124 | /*# sourceMappingURL=home.css.map */
125 |
--------------------------------------------------------------------------------
/styles/css/menu.css:
--------------------------------------------------------------------------------
1 | nav {
2 | background-color: var(--header-bg-color);
3 | height: 5.4rem;
4 | box-shadow: 0px 2px 8px 1px rgba(204, 204, 204, 0.35);
5 | position: -webkit-sticky;
6 | position: -webkit-sticky;
7 | position: sticky;
8 | top: 0;
9 | left: 0;
10 | width: 100%;
11 | }
12 |
13 | .menu-item {
14 | color: var(--text-color);
15 | text-transform: uppercase;
16 | font-size: 1.5rem;
17 | font-weight: bold;
18 | letter-spacing: 0.75px;
19 | position: relative;
20 | padding: 0.8rem 1.6rem;
21 | display: flex;
22 | align-items: center;
23 | transition: all 0.25s ease-in-out;
24 | cursor: pointer;
25 | }
26 | .menu-item:hover, .menu-item:active {
27 | background-color: var(--primary-color);
28 | color: var(--contrast-color);
29 | }
30 | .menu-item:hover .sub-menu, .menu-item:active .sub-menu {
31 | visibility: visible;
32 | opacity: 1;
33 | }
34 | .menu-item.active {
35 | background-color: var(--primary-color);
36 | color: var(--contrast-color);
37 | }
38 | .menu-item i {
39 | font-size: var(--header-icon-fs);
40 | }
41 |
42 | .sub-menu {
43 | position: absolute;
44 | top: 5.4rem;
45 | left: 0;
46 | box-shadow: 0px 2px 8px 1px rgba(204, 204, 204, 0.35);
47 | opacity: 0;
48 | visibility: hidden;
49 | transition: all 0.25s ease-in-out;
50 | background-color: var(--header-bg-color);
51 | padding: 8px 0;
52 | }
53 | .sub-menu-item {
54 | padding: 1.2rem 5.2rem;
55 | width: -webkit-max-content;
56 | width: -moz-max-content;
57 | width: max-content;
58 | color: var(--text-color);
59 | transition: all 0.25s ease-in-out;
60 | }
61 | .sub-menu-item:hover, .sub-menu-item:active {
62 | color: var(--secondary-color);
63 | }
64 |
65 | .search-bar-icon i {
66 | font-size: var(--header-icon-fs);
67 | color: var(--text-color);
68 | cursor: pointer;
69 | margin-left: 1.2rem;
70 | transition: all 0.25s ease-in-out;
71 | }
72 | .search-bar-icon i:hover, .search-bar-icon i:active {
73 | transform: scale(1.1);
74 | color: var(--primary-color);
75 | }
76 | .search-bar-control input {
77 | outline: none;
78 | padding: 0.8rem 1.2rem;
79 | height: calc(5.4rem / 1.5);
80 | border: solid 1px var(--light-grey);
81 | }
82 | .search-bar-control #search {
83 | outline: none;
84 | padding: 0.8rem 1.2rem;
85 | height: calc(5.4rem / 1.5);
86 | border: none;
87 | background-color: var(--primary-color);
88 | color: var(--contrast-color);
89 | cursor: pointer;
90 | }
91 |
92 | .mobile-menu {
93 | display: none;
94 | }
95 |
96 | #slideNav {
97 | z-index: 3;
98 | min-height: 100vh;
99 | background-color: var(--header-bg-color);
100 | position: fixed;
101 | top: 0;
102 | left: 0;
103 | transform: translateX(-100%);
104 | }
105 | #slideNav .icon {
106 | font-size: 2.4rem;
107 | color: var(--text-color);
108 | padding: 2.4rem;
109 | text-align: right;
110 | border-bottom: solid 1px var(--light-grey);
111 | }
112 | #slideNav .icon:hover i, #slideNav .icon:active i {
113 | transition: all 1.2s;
114 | transform: rotate(360deg);
115 | }
116 |
117 | .slide-menu-item {
118 | font-weight: bold;
119 | font-size: 1.5rem;
120 | letter-spacing: 0.75px;
121 | text-transform: uppercase;
122 | color: var(--text-color);
123 | padding: 1.6rem 6.4rem;
124 | transition: all 0.25s ease-in-out;
125 | cursor: pointer;
126 | border-bottom: solid 1px var(--light-grey);
127 | }
128 | .slide-menu-item:hover, .slide-menu-item:active {
129 | background-color: var(--primary-color);
130 | color: var(--contrast-color);
131 | }
132 | .slide-menu-item:hover .arrow-down::after, .slide-menu-item:active .arrow-down::after {
133 | border-top-color: var(--contrast-color);
134 | }
135 | .slide-menu-item:hover .arrow-up::after, .slide-menu-item:active .arrow-up::after {
136 | border-bottom-color: var(--contrast-color);
137 | }
138 | .slide-menu-item span {
139 | position: relative;
140 | padding-right: 8px;
141 | }
142 | .slide-menu-item.active {
143 | background-color: var(--primary-color);
144 | color: var(--contrast-color);
145 | }
146 | .slide-menu-item.active .arrow-down::after {
147 | border-top-color: var(--contrast-color);
148 | }
149 | .slide-menu-item.active .arrow-up::after {
150 | border-bottom-color: var(--contrast-color);
151 | }
152 |
153 | .arrow-down::after {
154 | content: "";
155 | width: 0;
156 | height: 0;
157 | border-left: solid 5px transparent;
158 | border-right: solid 5px transparent;
159 | position: absolute;
160 | top: 50%;
161 | right: -1.6rem;
162 | transform: translateY(-50%);
163 | border-top: solid 5px var(--text-color);
164 | }
165 |
166 | .arrow-up::after {
167 | content: "";
168 | width: 0;
169 | height: 0;
170 | border-left: solid 5px transparent;
171 | border-right: solid 5px transparent;
172 | position: absolute;
173 | top: 50%;
174 | right: -1.6rem;
175 | transform: translateY(-50%);
176 | border-bottom: solid 5px var(--text-color);
177 | }
178 |
179 | .sub-slide-menu {
180 | background-color: var(--header-bg-color);
181 | display: none;
182 | }
183 | .sub-slide-menu-item {
184 | font-weight: bold;
185 | font-size: 1.5rem;
186 | letter-spacing: 0.75px;
187 | text-transform: uppercase;
188 | color: var(--text-color);
189 | padding: 1.6rem 6.4rem;
190 | transition: all 0.25s ease-in-out;
191 | cursor: pointer;
192 | }
193 | .sub-slide-menu-item:hover, .sub-slide-menu-item:active {
194 | color: var(--secondary-color);
195 | }
196 | .sub-slide-menu-item span {
197 | display: inline-block;
198 | transform: translateX(2rem);
199 | }
200 | .sub-slide-menu-item.active span {
201 | text-decoration: underline;
202 | }
203 |
204 | #themeBtn {
205 | color: var(--text-color);
206 | font-size: var(--header-icon-fs);
207 | cursor: pointer;
208 | transition: all 0.25s ease-in-out;
209 | }
210 | #themeBtn:hover, #themeBtn:active {
211 | transform: scale(1.1);
212 | color: var(--primary-color);
213 | }
214 |
215 | @media only screen and (max-width: 892px) {
216 | .mobile-menu {
217 | display: flex;
218 | }
219 |
220 | .menu {
221 | display: none;
222 | }
223 | }
224 | @media only screen and (max-width: 480px) {
225 | .search-bar-icon {
226 | padding: 0 2px;
227 | margin-left: 4px;
228 | }
229 |
230 | .search-bar-control {
231 | text-align: right;
232 | }
233 |
234 | .search-bar-control input {
235 | width: 70%;
236 | }
237 |
238 | #slideNav {
239 | width: 100%;
240 | }
241 | }
242 | .show-slide-menu {
243 | -webkit-animation: showSlide 0.25s ease-in forwards;
244 | animation: showSlide 0.25s ease-in forwards;
245 | }
246 |
247 | @-webkit-keyframes showSlide {
248 | from {
249 | transform: translateX(-100%);
250 | }
251 | to {
252 | transform: translateX(0);
253 | }
254 | }
255 | @keyframes showSlide {
256 | from {
257 | transform: translateX(-100%);
258 | }
259 | to {
260 | transform: translateX(0);
261 | }
262 | }
263 | .hide-slide-menu {
264 | -webkit-animation: hideSlide 0.35s ease-out forwards;
265 | animation: hideSlide 0.35s ease-out forwards;
266 | }
267 |
268 | @-webkit-keyframes hideSlide {
269 | from {
270 | transform: translateX(0);
271 | }
272 | to {
273 | transform: translateX(-100%);
274 | }
275 | }
276 | @keyframes hideSlide {
277 | from {
278 | transform: translateX(0);
279 | }
280 | to {
281 | transform: translateX(-100%);
282 | }
283 | }
284 |
285 | /*# sourceMappingURL=menu.css.map */
286 |
--------------------------------------------------------------------------------
/styles/css/reset.css:
--------------------------------------------------------------------------------
1 | :root {
2 | /* color palettes */
3 | --body-bg-color: #fff;
4 | --header-bg-color: #fff;
5 |
6 | --title-color: #189838;
7 | --text-color: #1e1e1e;
8 |
9 | --contrast-color: #fff;
10 | --primary-color: #9977ee;
11 | --secondary-color: #c96eed;
12 |
13 | --light-color: #bcc247;
14 | --dark-color: #c7ac10;
15 |
16 | --label-color: #6e6e6e;
17 |
18 | /* common color */
19 | --light-grey: #e5e5e5;
20 | --grey: #888;
21 | --medium-grey: #5e5e5e;
22 | --dark-grey: #333;
23 |
24 | /* global variable */
25 | --header-icon-fs: 2.2rem;
26 | }
27 |
28 | [data-theme='dark'] {
29 | --body-bg-color: #212223;
30 | --header-bg-color: #3a3b3c;
31 |
32 | --title-color: #50ff77;
33 | --text-color: #fefefe;
34 |
35 | --contrast-color: #fff;
36 | --primary-color: #bb88ff;
37 | --secondary-color: #ff7dff;
38 |
39 | --light-color: #e9ff7d;
40 | --dark-color: #fff37d;
41 |
42 | --label-color: #dedede;
43 | }
44 |
45 | * {
46 | margin: 0;
47 | padding: 0;
48 | font: inherit;
49 | box-sizing: border-box;
50 | }
51 |
52 | html {
53 | font-family: 'Montserrat', 'Roboto', sans-serif;
54 | text-rendering: optimizeLegibility;
55 | font-size: 62.5%;
56 | scroll-behavior: smooth;
57 | }
58 |
59 | html,
60 | body {
61 | width: 100%;
62 | height: 100%;
63 | overflow-x: hidden;
64 | position: relative;
65 | color: var(--text-color);
66 | background: var(--body-bg-color);
67 | }
68 |
69 | ::-webkit-scrollbar {
70 | width: 10px;
71 | }
72 |
73 | ::-webkit-scrollbar-track {
74 | background-color: #fff;
75 | }
76 |
77 | ::-webkit-scrollbar-thumb {
78 | background-color: #aaa;
79 | }
80 |
81 | ::-webkit-scrollbar-thumb:hover {
82 | background-color: #888;
83 | }
84 |
85 | p {
86 | word-wrap: break-word;
87 | }
88 |
89 | ol,
90 | li {
91 | list-style-type: none;
92 | }
93 |
94 | a {
95 | text-decoration: none;
96 | color: var(--main-text-color);
97 | }
98 |
99 | blockquote:before,
100 | blockquote:after,
101 | q:before,
102 | q:after {
103 | content: '';
104 | content: none;
105 | }
106 |
107 | table {
108 | border-collapse: collapse;
109 | border-spacing: 0;
110 | }
111 |
112 | code {
113 | font-size: 1.6rem;
114 | }
115 |
--------------------------------------------------------------------------------
/styles/css/sort-realtime.css:
--------------------------------------------------------------------------------
1 | .wrapper {
2 | padding-top: 3.2rem;
3 | padding-bottom: 3.2rem;
4 | }
5 |
6 | .control-section {
7 | border-bottom: solid 1px var(--secondary-color);
8 | padding: 1.2rem 0;
9 | }
10 | .control-section > * {
11 | margin: 4px 1.6rem;
12 | }
13 |
14 | .sort-input-item label {
15 | display: inline-block;
16 | margin-right: 4px;
17 | margin-bottom: 2px;
18 | font-size: 1.4rem;
19 | font-weight: bold;
20 | color: var(--label-color);
21 | }
22 | .sort-input-item input,
23 | .sort-input-item select {
24 | padding: 6px 8px;
25 | outline: none;
26 | font-size: 1.4rem;
27 | border: solid 1px var(--primary-color);
28 | border-radius: 4px;
29 | color: var(--label-color);
30 | background: transparent;
31 | }
32 | .sort-input-item select option {
33 | background-color: var(--header-bg-color);
34 | opacity: 0.95;
35 | padding: 4px 0;
36 | }
37 | .sort-input-item select:focus > option:checked {
38 | background-color: var(--primary-color);
39 | color: var(--light-grey);
40 | }
41 |
42 | .sort-btn {
43 | flex-wrap: wrap;
44 | }
45 | .sort-btn-item {
46 | margin: 2px 4px;
47 | background: transparent;
48 | border: solid 1px var(--primary-color);
49 | color: var(--text-color);
50 | padding: 1rem 1.4rem;
51 | border-radius: 4px;
52 | font-size: 1.4rem;
53 | transition: all 0.35s;
54 | cursor: pointer;
55 | }
56 | .sort-btn-item:hover, .sort-btn-item:active {
57 | color: var(--secondary-color);
58 | }
59 | .sort-btn-item.disabled {
60 | pointer-events: none;
61 | opacity: 0.5;
62 | }
63 |
64 | .sort-box {
65 | display: grid;
66 | grid-template-columns: 1fr 1fr 1fr;
67 | gap: 2.4rem;
68 | margin: 2.4rem 0;
69 | }
70 |
71 | .sort-box-item {
72 | border: solid 1px var(--label-color);
73 | border-radius: 8px;
74 | padding: 1.6rem;
75 | }
76 |
77 | .sort-name {
78 | font-size: 1.8rem;
79 | color: var(--label-color);
80 | font-weight: 700;
81 | margin-bottom: 1.2rem;
82 | text-transform: capitalize;
83 | }
84 |
85 | .sort-content {
86 | display: flex;
87 | align-items: center;
88 | margin-bottom: 1.2rem;
89 | }
90 | .sort-content .label {
91 | font-size: 1.6rem;
92 | color: var(--priamry-color);
93 | padding-right: 1.2rem;
94 | font-weight: bold;
95 | }
96 | .sort-content .content {
97 | font-size: 1.6rem;
98 | color: var(--text-color);
99 | }
100 |
101 | .sorting {
102 | border: solid 2px var(--title-color);
103 | }
104 | .sorting .sort-name {
105 | color: var(--title-color);
106 | }
107 |
108 | .sorted {
109 | border: solid 2px var(--primary-color);
110 | }
111 | .sorted .sort-name {
112 | color: var(--primary-color);
113 | }
114 |
115 | @media only screen and (max-width: 767px) {
116 | .sort-box {
117 | grid-template-columns: 1fr 1fr;
118 | }
119 | }
120 | @media only screen and (max-width: 480px) {
121 | .sort-box {
122 | grid-template-columns: 1fr;
123 | }
124 | }
125 |
126 | /*# sourceMappingURL=sort-realtime.css.map */
127 |
--------------------------------------------------------------------------------
/styles/css/sort-visualizer.css:
--------------------------------------------------------------------------------
1 | nav {
2 | z-index: 2 !important;
3 | }
4 |
5 | #sortControl {
6 | padding: 1.4rem 1.6rem;
7 | background-color: var(--header-bg-color);
8 | flex-wrap: wrap;
9 | border-bottom: solid 1px var(--secondary-color);
10 | }
11 |
12 | .sort-input {
13 | margin: 6px 0;
14 | flex-wrap: wrap;
15 | }
16 | .sort-input-item {
17 | margin: 2px 8px;
18 | }
19 | .sort-input-item label {
20 | display: inline-block;
21 | margin-right: 4px;
22 | margin-bottom: 2px;
23 | font-size: 1.4rem;
24 | font-weight: bold;
25 | color: var(--label-color);
26 | }
27 | .sort-input-item input,
28 | .sort-input-item select {
29 | padding: 6px 8px;
30 | outline: none;
31 | font-size: 1.4rem;
32 | border: solid 1px var(--primary-color);
33 | border-radius: 4px;
34 | color: var(--label-color);
35 | background: transparent;
36 | }
37 | .sort-input-item select option {
38 | background-color: var(--header-bg-color);
39 | opacity: 0.95;
40 | padding: 4px 0;
41 | }
42 | .sort-input-item select:focus > option:checked {
43 | background-color: var(--primary-color);
44 | color: var(--light-grey);
45 | }
46 |
47 | .sort-btn {
48 | flex-wrap: wrap;
49 | }
50 | .sort-btn-item {
51 | margin: 2px 4px;
52 | background: transparent;
53 | border: solid 1px var(--primary-color);
54 | color: var(--text-color);
55 | padding: 1rem 1.4rem;
56 | border-radius: 4px;
57 | font-size: 1.4rem;
58 | transition: all 0.25s ease-in-out;
59 | cursor: pointer;
60 | }
61 | .sort-btn-item:hover, .sort-btn-item:active {
62 | color: var(--secondary-color);
63 | }
64 | .sort-btn-item.disabled {
65 | pointer-events: none;
66 | opacity: 0.5;
67 | }
68 |
69 | .visual-sort {
70 | width: 100vw;
71 | padding: 8px;
72 | }
73 |
74 | #graph {
75 | border-bottom: solid 8px #111;
76 | align-items: flex-end;
77 | }
78 |
79 | .arr-item {
80 | width: 100%;
81 | background-color: #393a59;
82 | text-align: center;
83 | color: #fff;
84 | }
85 |
86 | #hideControlBtn {
87 | margin: 0.4rem;
88 | top: 18px;
89 | right: 26px;
90 | font-size: 2rem;
91 | color: var(--primary-color);
92 | transition: all 0.25s ease-in-out;
93 | }
94 | #hideControlBtn:hover, #hideControlBtn:active {
95 | transform: scale(1.1);
96 | }
97 |
98 | #sortNote {
99 | padding: 8px;
100 | }
101 |
102 | .sort-note-item {
103 | margin: 2px 16px;
104 | }
105 | .sort-note-item__title {
106 | font-size: 1.8rem;
107 | color: var(--label-color);
108 | letter-spacing: 1px;
109 | }
110 | .sort-note-item__color {
111 | height: 2rem;
112 | width: 4rem;
113 | margin-left: 4px;
114 | }
115 |
116 | #sortAnalysis {
117 | display: none;
118 | }
119 |
120 | .sort-analysis-item {
121 | font-size: 1.8rem;
122 | color: var(--title-color);
123 | margin: 2px 8px;
124 | }
125 | .sort-analysis-item span {
126 | display: inline-block;
127 | font-weight: 500;
128 | color: var(--label-color);
129 | margin-left: 4px;
130 | }
131 |
132 | .overlay {
133 | position: absolute;
134 | top: 0;
135 | left: 0;
136 | width: 100%;
137 | height: 100%;
138 | background-color: #111;
139 | opacity: 0.4;
140 | z-index: 4;
141 | display: none;
142 | }
143 |
144 | .desc-alg-modal {
145 | padding: 3.6rem;
146 | width: 100%;
147 | }
148 | .desc-alg-modal-wrap {
149 | display: none;
150 | background-color: var(--body-bg-color);
151 | z-index: 99;
152 | max-height: 90vh;
153 | min-width: 65vw;
154 | max-width: 95vw;
155 | overflow: auto;
156 | box-shadow: 0px 0px 5px 4px rgba(229, 229, 229, 0.1);
157 | border-radius: 4px;
158 | padding-right: 0;
159 | }
160 | .desc-alg-modal-wrap::-webkit-scrollbar {
161 | width: 5px;
162 | }
163 | .desc-alg-modal .close-icon {
164 | font-size: 2.4rem;
165 | position: -webkit-sticky;
166 | position: sticky;
167 | top: 10px;
168 | }
169 | .desc-alg-modal .close-icon:hover i, .desc-alg-modal .close-icon:active i {
170 | transition: all 1.2s;
171 | transform: rotate(360deg);
172 | }
173 | .desc-alg-modal .title {
174 | color: var(--title-color);
175 | text-transform: uppercase;
176 | font-size: 2.6rem;
177 | letter-spacing: 1px;
178 | font-weight: bold;
179 | margin: 12px 0;
180 | }
181 |
182 | .desc-content .sub-title {
183 | color: var(--dark-color);
184 | font-size: 2rem;
185 | font-weight: 500;
186 | }
187 | .desc-content .sub-content {
188 | font-size: 1.6rem;
189 | padding-left: 18px;
190 | }
191 |
192 | @media only screen and (max-width: 830px) {
193 | .desc-alg-modal-wrap {
194 | min-width: 90vw;
195 | }
196 | }
197 | @media only screen and (max-width: 480px) {
198 | .sort-analysis-item,
199 | .sort-note-item__title {
200 | font-size: 1.4rem;
201 | }
202 |
203 | .desc-alg-modal .title {
204 | font-size: 2rem;
205 | }
206 |
207 | .desc-content .sub-title {
208 | font-size: 1.8rem;
209 | }
210 |
211 | .desc-content .sub-content {
212 | font-size: 1.5rem;
213 | }
214 | }
215 |
216 | /*# sourceMappingURL=sort-visualizer.css.map */
217 |
--------------------------------------------------------------------------------
/styles/css/utils.css:
--------------------------------------------------------------------------------
1 | .cur-pointer {
2 | cursor: pointer;
3 | }
4 |
5 | #overlay {
6 | position: absolute;
7 | top: 0;
8 | left: 0;
9 | width: 100%;
10 | height: 100%;
11 | background-color: var(--grey);
12 | opacity: 0.3;
13 | display: none;
14 | }
15 |
16 | .header-title {
17 | color: var(--title-color);
18 | font-weight: 500;
19 | font-size: 3.6rem;
20 | line-height: 1.25;
21 | text-align: center;
22 | margin: 1.2rem 0;
23 | }
24 |
25 | @media only screen and (max-width: 576px) {
26 | .header-title {
27 | font-weight: 500;
28 | font-size: 2.6rem;
29 | }
30 | }
31 |
32 | /*# sourceMappingURL=utils.css.map */
33 |
--------------------------------------------------------------------------------
/styles/scss/atomic.scss:
--------------------------------------------------------------------------------
1 | // container
2 | .container,
3 | .container-fluid,
4 | .container-sm,
5 | .container-md,
6 | .container-lg,
7 | .container-xl {
8 | width: 100%;
9 | padding-right: 8px;
10 | padding-left: 8px;
11 | margin-right: auto;
12 | margin-left: auto;
13 | }
14 |
15 | @media (min-width: 576px) {
16 | .container,
17 | .container-sm {
18 | max-width: 540px;
19 | }
20 | }
21 |
22 | @media (min-width: 768px) {
23 | .container,
24 | .container-sm,
25 | .container-md {
26 | max-width: 760px;
27 | }
28 | }
29 |
30 | @media (min-width: 992px) {
31 | .container,
32 | .container-sm,
33 | .container-md,
34 | .container-lg {
35 | max-width: 960px;
36 | }
37 | }
38 |
39 | @media (min-width: 1200px) {
40 | .container,
41 | .container-sm,
42 | .container-md,
43 | .container-lg,
44 | .container-xl {
45 | max-width: 1140px;
46 | }
47 | }
48 |
49 | @media (min-width: 1367px) {
50 | .container,
51 | .container-sm,
52 | .container-md,
53 | .container-lg,
54 | .container-xl {
55 | max-width: 1280px;
56 | }
57 | }
58 |
59 | // transform
60 | .trans {
61 | &-center {
62 | position: absolute;
63 | top: 50%;
64 | left: 50%;
65 | transform: translate(-50%, -50%);
66 | }
67 | &-margin {
68 | display: block;
69 | margin: 0 auto;
70 | }
71 | }
72 |
73 | // position
74 | .pos {
75 | &-rel {
76 | position: relative;
77 | }
78 | &-abs {
79 | position: absolute;
80 | }
81 | }
82 |
83 | // display
84 | .d {
85 | &-none {
86 | display: none;
87 | }
88 | &-block {
89 | display: block;
90 | }
91 | &-flex {
92 | display: flex;
93 | }
94 | }
95 |
96 | // display-flex
97 | .flex {
98 | &-col {
99 | display: flex;
100 | flex-direction: column;
101 | }
102 | &-dir-col {
103 | flex-direction: column;
104 | }
105 | &-dir-reverse {
106 | flex-direction: row-reverse;
107 | }
108 | &-grow-1 {
109 | flex-grow: 1;
110 | }
111 | &-wrap {
112 | flex-wrap: wrap;
113 | }
114 | &-center {
115 | display: flex;
116 | align-items: center;
117 | justify-content: center;
118 |
119 | &--ver {
120 | display: flex;
121 | align-items: center;
122 | }
123 |
124 | &--col {
125 | display: flex;
126 | flex-direction: column;
127 | align-items: center;
128 | justify-content: center;
129 | }
130 |
131 | &-between {
132 | display: flex;
133 | align-items: center;
134 | justify-content: space-between;
135 | }
136 | }
137 | }
138 |
139 | .align {
140 | // align-items
141 | &-i {
142 | &-center {
143 | align-items: center;
144 | }
145 | &-end {
146 | align-items: flex-end;
147 | }
148 | }
149 | }
150 |
151 | .justify {
152 | &-content {
153 | &-around {
154 | justify-content: space-around;
155 | }
156 | &-between {
157 | justify-content: space-between;
158 | }
159 | &-end {
160 | justify-content: flex-end;
161 | }
162 | &-center {
163 | justify-content: center;
164 | }
165 | }
166 | }
167 |
168 | //width, height
169 | .w-100 {
170 | width: 100%;
171 | }
172 |
173 | .h-100 {
174 | height: 100%;
175 | }
176 |
177 | .h-100vh {
178 | height: 100vh;
179 | }
180 |
181 | .w-100vw {
182 | width: 100vw;
183 | }
184 |
185 | // spacing 2-20 px (diff 2)*2
186 | @for $i from 1 to 11 {
187 | .m {
188 | &-#{$i} {
189 | margin: #{$i * 2}px;
190 | }
191 | &-t-#{$i} {
192 | margin-top: #{$i * 2}px;
193 | }
194 | &-b-#{$i} {
195 | margin-bottom: #{$i * 2}px;
196 | }
197 | &-l-#{$i} {
198 | margin-left: #{$i * 2}px;
199 | }
200 | &-r-#{$i} {
201 | margin-right: #{$i * 2}px;
202 | }
203 | &-lr-#{$i} {
204 | margin-left: #{$i * 2}px;
205 | margin-right: #{$i * 2}px;
206 | }
207 | &-tb-#{$i} {
208 | margin-top: #{$i * 2}px;
209 | margin-bottom: #{$i * 2}px;
210 | }
211 | }
212 | .p {
213 | &-#{$i} {
214 | padding: #{$i * 2}px;
215 | }
216 | &-t-#{$i} {
217 | padding-top: #{$i * 2}px;
218 | }
219 | &-b-#{$i} {
220 | padding-bottom: #{$i * 2}px;
221 | }
222 | &-l-#{$i} {
223 | padding-left: #{$i * 2}px;
224 | }
225 | &-r-#{$i} {
226 | padding-right: #{$i * 2}px;
227 | }
228 | &-lr-#{$i} {
229 | padding-left: #{$i * 2}px;
230 | padding-right: #{$i * 2}px;
231 | }
232 | &-tb-#{$i} {
233 | padding-top: #{$i * 2}px;
234 | padding-bottom: #{$i * 2}px;
235 | }
236 | }
237 | }
238 |
239 | // margin
240 | .m {
241 | &-lr-auto {
242 | margin-left: auto;
243 | margin-right: auto;
244 | }
245 | &-0-auto {
246 | margin: 0 auto;
247 | }
248 | &-auto-0 {
249 | margin: auto 0;
250 | }
251 | &-auto {
252 | margin: auto;
253 | }
254 | &-0 {
255 | margin: 0;
256 | }
257 | }
258 |
259 | // text
260 | .t {
261 | &-center {
262 | text-align: center;
263 | }
264 | &-left {
265 | text-align: left;
266 | }
267 | &-right {
268 | text-align: right;
269 | }
270 | &-end {
271 | text-align: end;
272 | }
273 | &-justify {
274 | text-align: justify;
275 | }
276 | }
277 |
278 | .fw {
279 | &-b {
280 | font-weight: bold;
281 | }
282 | }
283 |
--------------------------------------------------------------------------------
/styles/scss/home.scss:
--------------------------------------------------------------------------------
1 | @import './mixin.scss';
2 |
3 | #aboutMe {
4 | h1 {
5 | min-width: 80vw;
6 | text-align: center;
7 | font-size: 5.4rem;
8 | font-weight: bold;
9 | color: var(--title-color);
10 | @include transition(all, 2.2s);
11 | }
12 |
13 | h3 {
14 | text-align: center;
15 | font-size: 2.4rem;
16 | color: var(--secondary-color);
17 | font-weight: 900;
18 | margin-top: 16px;
19 | }
20 | }
21 |
22 | .contact-info li {
23 | font-size: 2.8rem;
24 | padding: 0.8rem 1.2rem;
25 | color: var(--text-color);
26 |
27 | cursor: pointer;
28 | @include transition;
29 | &:hover,
30 | &:active {
31 | transform: scale(1.2);
32 | opacity: 0.85;
33 | }
34 | }
35 |
36 | .logo-ani {
37 | -webkit-animation: logoAni 2.6s ease-in-out forwards;
38 | animation: logoAni 2.6s ease-in-out forwards;
39 | }
40 |
41 | @-webkit-keyframes logoAni {
42 | 0% {
43 | transform: translateY(-10px);
44 | }
45 | 10% {
46 | transform: translateY(-5px);
47 | }
48 | 20% {
49 | transform: translateY(-15px);
50 | }
51 | 30% {
52 | transform: translateY(-3px);
53 | }
54 | 40% {
55 | transform: translateY(-25px);
56 | }
57 | 50% {
58 | transform: translateY(0px);
59 | }
60 | 60% {
61 | transform: translateY(-30px);
62 | }
63 | 70% {
64 | transform: translateY(0);
65 | }
66 | 80% {
67 | transform: translateY(-20px);
68 | }
69 | 85% {
70 | transform: translateY(0);
71 | }
72 | 90% {
73 | transform: translateY(-10px);
74 | }
75 | 100% {
76 | transform: translateY(0);
77 | }
78 | }
79 |
80 | @keyframes logoAni {
81 | 0% {
82 | transform: translateY(-10px);
83 | }
84 | 10% {
85 | transform: translateY(-5px);
86 | }
87 | 20% {
88 | transform: translateY(-15px);
89 | }
90 | 30% {
91 | transform: translateY(-3px);
92 | }
93 | 40% {
94 | transform: translateY(-25px);
95 | }
96 | 50% {
97 | transform: translateY(0px);
98 | }
99 | 60% {
100 | transform: translateY(-30px);
101 | }
102 | 70% {
103 | transform: translateY(0);
104 | }
105 | 80% {
106 | transform: translateY(-20px);
107 | }
108 | 85% {
109 | transform: translateY(0);
110 | }
111 | 90% {
112 | transform: translateY(-10px);
113 | }
114 | 100% {
115 | transform: translateY(0);
116 | }
117 | }
118 |
119 | @media only screen and (max-width: 576px) {
120 | #aboutMe {
121 | h1 {
122 | font-size: 3.6rem;
123 | }
124 | h3 {
125 | font-size: 1.8rem;
126 | }
127 | }
128 |
129 | .logo-ani {
130 | max-width: 152px;
131 | max-height: 152px;
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/styles/scss/menu.scss:
--------------------------------------------------------------------------------
1 | @import './mixin.scss';
2 | $nav-height: 5.4rem;
3 | $box-shadow: 0px 2px 8px 1px rgba(#ccc, 0.35);
4 |
5 | @mixin menu-item-active {
6 | background-color: var(--primary-color);
7 | color: var(--contrast-color);
8 | }
9 |
10 | // ======= Navigation =======
11 | nav {
12 | background-color: var(--header-bg-color);
13 | height: $nav-height;
14 | box-shadow: $box-shadow;
15 |
16 | position: -webkit-sticky;
17 | position: -webkit-sticky;
18 | position: sticky;
19 |
20 | top: 0;
21 | left: 0;
22 | width: 100%;
23 | }
24 |
25 | .menu {
26 | &-item {
27 | color: var(--text-color);
28 | text-transform: uppercase;
29 | font-size: 1.5rem;
30 | font-weight: bold;
31 | letter-spacing: 0.75px;
32 |
33 | position: relative;
34 | padding: 0.8rem 1.6rem;
35 | display: flex;
36 | align-items: center;
37 |
38 | @include transition;
39 | cursor: pointer;
40 |
41 | &:hover,
42 | &:active {
43 | @include menu-item-active;
44 |
45 | & .sub-menu {
46 | visibility: visible;
47 | opacity: 1;
48 | }
49 | }
50 |
51 | &.active {
52 | @include menu-item-active;
53 | }
54 |
55 | i {
56 | font-size: var(--header-icon-fs);
57 | }
58 | }
59 | }
60 |
61 | .sub-menu {
62 | position: absolute;
63 | top: $nav-height;
64 | left: 0;
65 | box-shadow: $box-shadow;
66 |
67 | opacity: 0;
68 | visibility: hidden;
69 | @include transition;
70 |
71 | background-color: var(--header-bg-color);
72 | padding: 8px 0;
73 |
74 | &-item {
75 | padding: 1.2rem 5.2rem;
76 |
77 | width: -webkit-max-content;
78 | width: -moz-max-content;
79 | width: max-content;
80 | color: var(--text-color);
81 |
82 | @include transition;
83 |
84 | &:hover,
85 | &:active {
86 | color: var(--secondary-color);
87 | }
88 | }
89 | }
90 |
91 | .search-bar {
92 | &-icon i {
93 | font-size: var(--header-icon-fs);
94 | color: var(--text-color);
95 | cursor: pointer;
96 | margin-left: 1.2rem;
97 | @include transition;
98 |
99 | &:hover,
100 | &:active {
101 | transform: scale(1.1);
102 | color: var(--primary-color);
103 | }
104 | }
105 |
106 | &-control {
107 | @mixin search-input {
108 | outline: none;
109 | padding: 0.8rem 1.2rem;
110 | height: calc(#{$nav-height} / 1.5);
111 | }
112 |
113 | input {
114 | @include search-input;
115 | border: solid 1px var(--light-grey);
116 | }
117 |
118 | #search {
119 | @include search-input;
120 | border: none;
121 |
122 | @include menu-item-active;
123 | cursor: pointer;
124 | }
125 | }
126 | }
127 |
128 | // menu for mobile, device with screen < 892px
129 | .mobile-menu {
130 | display: none;
131 | }
132 |
133 | #slideNav {
134 | z-index: 3;
135 | min-height: 100vh;
136 | background-color: var(--header-bg-color);
137 |
138 | position: fixed;
139 | top: 0;
140 | left: 0;
141 | transform: translateX(-100%);
142 |
143 | .icon {
144 | font-size: 2.4rem;
145 | color: var(--text-color);
146 | padding: 2.4rem;
147 | text-align: right;
148 | border-bottom: solid 1px var(--light-grey);
149 |
150 | &:hover,
151 | &:active {
152 | & i {
153 | transition: all 1.2s;
154 | transform: rotate(360deg);
155 | }
156 | }
157 | }
158 | }
159 |
160 | @mixin menu-item {
161 | font-weight: bold;
162 | font-size: 1.5rem;
163 | letter-spacing: 0.75px;
164 | text-transform: uppercase;
165 |
166 | color: var(--text-color);
167 | padding: 1.6rem 6.4rem;
168 |
169 | @include transition;
170 | cursor: pointer;
171 | }
172 |
173 | .slide-menu {
174 | &-item {
175 | @include menu-item;
176 | border-bottom: solid 1px var(--light-grey);
177 |
178 | &:hover,
179 | &:active {
180 | @include menu-item-active;
181 |
182 | .arrow-down::after {
183 | border-top-color: var(--contrast-color);
184 | }
185 |
186 | .arrow-up::after {
187 | border-bottom-color: var(--contrast-color);
188 | }
189 | }
190 |
191 | span {
192 | position: relative;
193 | padding-right: 8px;
194 | }
195 |
196 | &.active {
197 | @include menu-item-active;
198 | .arrow-down::after {
199 | border-top-color: var(--contrast-color);
200 | }
201 |
202 | .arrow-up::after {
203 | border-bottom-color: var(--contrast-color);
204 | }
205 | }
206 | }
207 | }
208 |
209 | // arrow slide menu
210 | @mixin arrow {
211 | content: '';
212 | width: 0;
213 | height: 0;
214 | border-left: solid 5px transparent;
215 | border-right: solid 5px transparent;
216 |
217 | position: absolute;
218 | top: 50%;
219 | right: -1.6rem;
220 | transform: translateY(-50%);
221 | }
222 |
223 | .arrow-down {
224 | &::after {
225 | @include arrow;
226 | border-top: solid 5px var(--text-color);
227 | }
228 | }
229 |
230 | .arrow-up {
231 | &::after {
232 | @include arrow;
233 | border-bottom: solid 5px var(--text-color);
234 | }
235 | }
236 |
237 | // sub slide menu
238 | .sub-slide-menu {
239 | background-color: var(--header-bg-color);
240 | display: none;
241 |
242 | &-item {
243 | @include menu-item;
244 |
245 | &:hover,
246 | &:active {
247 | color: var(--secondary-color);
248 | }
249 |
250 | span {
251 | display: inline-block;
252 | transform: translateX(2rem);
253 | }
254 |
255 | &.active {
256 | span {
257 | text-decoration: underline;
258 | }
259 | }
260 | }
261 | }
262 |
263 | // theme button
264 | #themeBtn {
265 | color: var(--text-color);
266 | font-size: var(--header-icon-fs);
267 | cursor: pointer;
268 | @include transition;
269 |
270 | &:hover,
271 | &:active {
272 | transform: scale(1.1);
273 | color: var(--primary-color);
274 | }
275 | }
276 |
277 | // responsive
278 | @media only screen and (max-width: 892px) {
279 | .mobile-menu {
280 | display: flex;
281 | }
282 |
283 | .menu {
284 | display: none;
285 | }
286 | }
287 |
288 | @media only screen and (max-width: 480px) {
289 | .search-bar-icon {
290 | padding: 0 2px;
291 | margin-left: 4px;
292 | }
293 | .search-bar-control {
294 | text-align: right;
295 | }
296 | .search-bar-control input {
297 | width: 70%;
298 | }
299 |
300 | #slideNav {
301 | width: 100%;
302 | }
303 | }
304 |
305 | // animation
306 | .show-slide-menu {
307 | -webkit-animation: showSlide 0.25s ease-in forwards;
308 | animation: showSlide 0.25s ease-in forwards;
309 | }
310 |
311 | @-webkit-keyframes showSlide {
312 | from {
313 | transform: translateX(-100%);
314 | }
315 | to {
316 | transform: translateX(0);
317 | }
318 | }
319 |
320 | @keyframes showSlide {
321 | from {
322 | transform: translateX(-100%);
323 | }
324 | to {
325 | transform: translateX(0);
326 | }
327 | }
328 |
329 | .hide-slide-menu {
330 | -webkit-animation: hideSlide 0.35s ease-out forwards;
331 | animation: hideSlide 0.35s ease-out forwards;
332 | }
333 |
334 | @-webkit-keyframes hideSlide {
335 | from {
336 | transform: translateX(0);
337 | }
338 | to {
339 | transform: translateX(-100%);
340 | }
341 | }
342 |
343 | @keyframes hideSlide {
344 | from {
345 | transform: translateX(0);
346 | }
347 | to {
348 | transform: translateX(-100%);
349 | }
350 | }
351 |
--------------------------------------------------------------------------------
/styles/scss/mixin.scss:
--------------------------------------------------------------------------------
1 | @mixin transition($property: all, $duration: 0.25s, $time-fn: ease-in-out) {
2 | transition: $property $duration $time-fn;
3 | }
4 |
--------------------------------------------------------------------------------
/styles/scss/sort-realtime.scss:
--------------------------------------------------------------------------------
1 | .wrapper {
2 | padding-top: 3.2rem;
3 | padding-bottom: 3.2rem;
4 | }
5 |
6 | // ----------- control section -------------
7 | .control-section {
8 | border-bottom: solid 1px var(--secondary-color);
9 | padding: 1.2rem 0;
10 |
11 | & > * {
12 | margin: 4px 1.6rem;
13 | }
14 | }
15 |
16 | .sort-input {
17 | &-item {
18 | label {
19 | display: inline-block;
20 | margin-right: 4px;
21 | margin-bottom: 2px;
22 |
23 | font-size: 1.4rem;
24 | font-weight: bold;
25 | color: var(--label-color);
26 | }
27 |
28 | input,
29 | select {
30 | padding: 6px 8px;
31 | outline: none;
32 | font-size: 1.4rem;
33 |
34 | border: solid 1px var(--primary-color);
35 | border-radius: 4px;
36 |
37 | color: var(--label-color);
38 | background: transparent;
39 | }
40 |
41 | select option {
42 | background-color: var(--header-bg-color);
43 | opacity: 0.95;
44 | padding: 4px 0;
45 | }
46 |
47 | select:focus > option:checked {
48 | background-color: var(--primary-color);
49 | color: var(--light-grey);
50 | }
51 | }
52 | }
53 |
54 | .sort-btn {
55 | flex-wrap: wrap;
56 |
57 | &-item {
58 | margin: 2px 4px;
59 | background: transparent;
60 | border: solid 1px var(--primary-color);
61 | color: var(--text-color);
62 |
63 | padding: 1rem 1.4rem;
64 | border-radius: 4px;
65 | font-size: 1.4rem;
66 |
67 | transition: all 0.35s;
68 | cursor: pointer;
69 |
70 | &:hover,
71 | &:active {
72 | color: var(--secondary-color);
73 | }
74 |
75 | &.disabled {
76 | pointer-events: none;
77 | opacity: 0.5;
78 | }
79 | }
80 | }
81 |
82 | // ----------- sort section -------------
83 |
84 | .sort-box {
85 | display: grid;
86 | grid-template-columns: 1fr 1fr 1fr;
87 | gap: 2.4rem;
88 | margin: 2.4rem 0;
89 | }
90 |
91 | .sort-box-item {
92 | border: solid 1px var(--label-color);
93 | border-radius: 8px;
94 | padding: 1.6rem;
95 | }
96 |
97 | .sort-name {
98 | font-size: 1.8rem;
99 | color: var(--label-color);
100 | font-weight: 700;
101 | margin-bottom: 1.2rem;
102 | text-transform: capitalize;
103 | }
104 |
105 | .sort-content {
106 | display: flex;
107 | align-items: center;
108 | margin-bottom: 1.2rem;
109 |
110 | .label {
111 | font-size: 1.6rem;
112 | color: var(--priamry-color);
113 | padding-right: 1.2rem;
114 | font-weight: bold;
115 | }
116 |
117 | .content {
118 | font-size: 1.6rem;
119 | color: var(--text-color);
120 | }
121 | }
122 |
123 | .sorting {
124 | border: solid 2px var(--title-color);
125 |
126 | .sort-name {
127 | color: var(--title-color);
128 | }
129 | }
130 |
131 | .sorted {
132 | border: solid 2px var(--primary-color);
133 |
134 | .sort-name {
135 | color: var(--primary-color);
136 | }
137 | }
138 |
139 | // ----------- responsive -------------
140 | @media only screen and (max-width: 767px) {
141 | .sort-box {
142 | grid-template-columns: 1fr 1fr;
143 | }
144 | }
145 |
146 | @media only screen and (max-width: 480px) {
147 | .sort-box {
148 | grid-template-columns: 1fr;
149 | }
150 | }
151 |
--------------------------------------------------------------------------------
/styles/scss/sort-visualizer.scss:
--------------------------------------------------------------------------------
1 | @import './mixin.scss';
2 |
3 | nav {
4 | z-index: 2 !important;
5 | }
6 |
7 | #sortControl {
8 | padding: 1.4rem 1.6rem;
9 | background-color: var(--header-bg-color);
10 | flex-wrap: wrap;
11 | border-bottom: solid 1px var(--secondary-color);
12 | }
13 |
14 | .sort-input {
15 | margin: 6px 0;
16 | flex-wrap: wrap;
17 | &-item {
18 | margin: 2px 8px;
19 |
20 | label {
21 | display: inline-block;
22 | margin-right: 4px;
23 | margin-bottom: 2px;
24 |
25 | font-size: 1.4rem;
26 | font-weight: bold;
27 | color: var(--label-color);
28 | }
29 |
30 | input,
31 | select {
32 | padding: 6px 8px;
33 | outline: none;
34 | font-size: 1.4rem;
35 |
36 | border: solid 1px var(--primary-color);
37 | border-radius: 4px;
38 |
39 | color: var(--label-color);
40 | background: transparent;
41 | }
42 |
43 | select option {
44 | background-color: var(--header-bg-color);
45 | opacity: 0.95;
46 | padding: 4px 0;
47 | }
48 |
49 | select:focus > option:checked {
50 | background-color: var(--primary-color);
51 | color: var(--light-grey);
52 | }
53 | }
54 | }
55 |
56 | .sort-btn {
57 | flex-wrap: wrap;
58 |
59 | &-item {
60 | margin: 2px 4px;
61 | background: transparent;
62 | border: solid 1px var(--primary-color);
63 | color: var(--text-color);
64 |
65 | padding: 1rem 1.4rem;
66 | border-radius: 4px;
67 | font-size: 1.4rem;
68 |
69 | @include transition;
70 | cursor: pointer;
71 |
72 | &:hover,
73 | &:active {
74 | color: var(--secondary-color);
75 | }
76 |
77 | &.disabled {
78 | pointer-events: none;
79 | opacity: 0.5;
80 | }
81 | }
82 | }
83 |
84 | .visual-sort {
85 | width: 100vw;
86 | padding: 8px;
87 | }
88 |
89 | #graph {
90 | border-bottom: solid 8px #111;
91 | align-items: flex-end;
92 | }
93 |
94 | .arr-item {
95 | width: 100%;
96 | background-color: #393a59;
97 | text-align: center;
98 | color: #fff;
99 | }
100 |
101 | #hideControlBtn {
102 | margin: 0.4rem;
103 | top: 18px;
104 | right: 26px;
105 | font-size: 2rem;
106 | color: var(--primary-color);
107 | @include transition;
108 |
109 | &:hover,
110 | &:active {
111 | transform: scale(1.1);
112 | }
113 | }
114 |
115 | #sortNote {
116 | padding: 8px;
117 | }
118 |
119 | .sort-note-item {
120 | margin: 2px 16px;
121 | &__title {
122 | font-size: 1.8rem;
123 | color: var(--label-color);
124 | letter-spacing: 1px;
125 | }
126 |
127 | &__color {
128 | height: 2rem;
129 | width: 4rem;
130 | margin-left: 4px;
131 | }
132 | }
133 |
134 | #sortAnalysis {
135 | display: none;
136 | }
137 |
138 | .sort-analysis-item {
139 | font-size: 1.8rem;
140 | color: var(--title-color);
141 | margin: 2px 8px;
142 |
143 | span {
144 | display: inline-block;
145 | font-weight: 500;
146 | color: var(--label-color);
147 | margin-left: 4px;
148 | }
149 | }
150 |
151 | // description algorithm modal
152 | .overlay {
153 | position: absolute;
154 | top: 0;
155 | left: 0;
156 | width: 100%;
157 | height: 100%;
158 | background-color: #111;
159 | opacity: 0.4;
160 | z-index: 4;
161 | display: none;
162 | }
163 |
164 | .desc-alg-modal {
165 | padding: 3.6rem;
166 | width: 100%;
167 | &-wrap {
168 | display: none;
169 | background-color: var(--body-bg-color);
170 | z-index: 99;
171 | max-height: 90vh;
172 | min-width: 65vw;
173 | max-width: 95vw;
174 |
175 | overflow: auto;
176 | box-shadow: 0px 0px 5px 4px rgba(#e5e5e5, 0.1);
177 | border-radius: 4px;
178 | padding-right: 0;
179 |
180 | &::-webkit-scrollbar {
181 | width: 5px;
182 | }
183 | }
184 |
185 | .close-icon {
186 | font-size: 2.4rem;
187 | position: -webkit-sticky;
188 | position: sticky;
189 | top: 10px;
190 | &:hover,
191 | &:active {
192 | & i {
193 | transition: all 1.2s;
194 | transform: rotate(360deg);
195 | }
196 | }
197 | }
198 |
199 | .title {
200 | color: var(--title-color);
201 | text-transform: uppercase;
202 | font-size: 2.6rem;
203 | letter-spacing: 1px;
204 | font-weight: bold;
205 | margin: 12px 0;
206 | }
207 | }
208 |
209 | .desc-content {
210 | .sub-title {
211 | color: var(--dark-color);
212 | font-size: 2rem;
213 | font-weight: 500;
214 | }
215 | .sub-content {
216 | font-size: 1.6rem;
217 | padding-left: 18px;
218 | }
219 | }
220 |
221 | @media only screen and (max-width: 830px) {
222 | .desc-alg-modal-wrap {
223 | min-width: 90vw;
224 | }
225 | }
226 |
227 | @media only screen and (max-width: 480px) {
228 | .sort-analysis-item,
229 | .sort-note-item__title {
230 | font-size: 1.4rem;
231 | }
232 |
233 | .desc-alg-modal .title {
234 | font-size: 2rem;
235 | }
236 |
237 | .desc-content .sub-title {
238 | font-size: 1.8rem;
239 | }
240 |
241 | .desc-content .sub-content {
242 | font-size: 1.5rem;
243 | }
244 | }
245 |
--------------------------------------------------------------------------------
/styles/scss/utils.scss:
--------------------------------------------------------------------------------
1 | // utils
2 | .cur-pointer {
3 | cursor: pointer;
4 | }
5 |
6 | // overlay
7 | #overlay {
8 | position: absolute;
9 | top: 0;
10 | left: 0;
11 | width: 100%;
12 | height: 100%;
13 | background-color: var(--grey);
14 | opacity: 0.3;
15 | display: none;
16 | }
17 |
18 | // title
19 | .header-title {
20 | color: var(--title-color);
21 | font-weight: 500;
22 | font-size: 3.6rem;
23 |
24 | line-height: 1.25;
25 | text-align: center;
26 | margin: 1.2rem 0;
27 | }
28 |
29 | @media only screen and (max-width: 576px) {
30 | .header-title {
31 | font-weight: 500;
32 | font-size: 2.6rem;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/vendors/prism/prism.min.css:
--------------------------------------------------------------------------------
1 | /* PrismJS 1.23.0
2 | https://prismjs.com/download.html#themes=prism-tomorrow&languages=markup+css+clike+javascript */
3 | /**
4 | * prism.js tomorrow night eighties for JavaScript, CoffeeScript, CSS and HTML
5 | * Based on https://github.com/chriskempson/tomorrow-theme
6 | * @author Rose Pritchard
7 | */
8 |
9 | code[class*="language-"],
10 | pre[class*="language-"] {
11 | color: #ccc;
12 | background: none;
13 | font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
14 | font-size: 1em;
15 | text-align: left;
16 | white-space: pre;
17 | word-spacing: normal;
18 | word-break: normal;
19 | word-wrap: normal;
20 | line-height: 1.5;
21 |
22 | -moz-tab-size: 4;
23 | -o-tab-size: 4;
24 | tab-size: 4;
25 |
26 | -webkit-hyphens: none;
27 | -moz-hyphens: none;
28 | -ms-hyphens: none;
29 | hyphens: none;
30 |
31 | }
32 |
33 | /* Code blocks */
34 | pre[class*="language-"] {
35 | padding: 1em;
36 | margin: .5em 0;
37 | overflow: auto;
38 | }
39 |
40 | :not(pre) > code[class*="language-"],
41 | pre[class*="language-"] {
42 | background: #2d2d2d;
43 | }
44 |
45 | /* Inline code */
46 | :not(pre) > code[class*="language-"] {
47 | padding: .1em;
48 | border-radius: .3em;
49 | white-space: normal;
50 | }
51 |
52 | .token.comment,
53 | .token.block-comment,
54 | .token.prolog,
55 | .token.doctype,
56 | .token.cdata {
57 | color: #999;
58 | }
59 |
60 | .token.punctuation {
61 | color: #ccc;
62 | }
63 |
64 | .token.tag,
65 | .token.attr-name,
66 | .token.namespace,
67 | .token.deleted {
68 | color: #e2777a;
69 | }
70 |
71 | .token.function-name {
72 | color: #6196cc;
73 | }
74 |
75 | .token.boolean,
76 | .token.number,
77 | .token.function {
78 | color: #f08d49;
79 | }
80 |
81 | .token.property,
82 | .token.class-name,
83 | .token.constant,
84 | .token.symbol {
85 | color: #f8c555;
86 | }
87 |
88 | .token.selector,
89 | .token.important,
90 | .token.atrule,
91 | .token.keyword,
92 | .token.builtin {
93 | color: #cc99cd;
94 | }
95 |
96 | .token.string,
97 | .token.char,
98 | .token.attr-value,
99 | .token.regex,
100 | .token.variable {
101 | color: #7ec699;
102 | }
103 |
104 | .token.operator,
105 | .token.entity,
106 | .token.url {
107 | color: #67cdcc;
108 | }
109 |
110 | .token.important,
111 | .token.bold {
112 | font-weight: bold;
113 | }
114 | .token.italic {
115 | font-style: italic;
116 | }
117 |
118 | .token.entity {
119 | cursor: help;
120 | }
121 |
122 | .token.inserted {
123 | color: green;
124 | }
125 |
126 |
--------------------------------------------------------------------------------
/vendors/prism/prism.min.js:
--------------------------------------------------------------------------------
1 | /* PrismJS 1.23.0
2 | https://prismjs.com/download.html#themes=prism-tomorrow&languages=markup+css+clike+javascript */
3 | var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(u){var c=/\blang(?:uage)?-([\w-]+)\b/i,n=0,e={},M={manual:u.Prism&&u.Prism.manual,disableWorkerMessageHandler:u.Prism&&u.Prism.disableWorkerMessageHandler,util:{encode:function e(n){return n instanceof W?new W(n.type,e(n.content),n.alias):Array.isArray(n)?n.map(e):n.replace(/&/g,"&").replace(/=l.reach);y+=m.value.length,m=m.next){var b=m.value;if(t.length>n.length)return;if(!(b instanceof W)){var k,x=1;if(h){if(!(k=z(v,y,n,f)))break;var w=k.index,A=k.index+k[0].length,P=y;for(P+=m.value.length;P<=w;)m=m.next,P+=m.value.length;if(P-=m.value.length,y=P,m.value instanceof W)continue;for(var E=m;E!==t.tail&&(Pl.reach&&(l.reach=N);var j=m.prev;O&&(j=I(t,j,O),y+=O.length),q(t,j,x);var C=new W(o,g?M.tokenize(S,g):S,d,S);if(m=I(t,j,C),L&&I(t,m,L),1l.reach&&(l.reach=_.reach)}}}}}}(e,a,n,a.head,0),function(e){var n=[],t=e.head.next;for(;t!==e.tail;)n.push(t.value),t=t.next;return n}(a)},hooks:{all:{},add:function(e,n){var t=M.hooks.all;t[e]=t[e]||[],t[e].push(n)},run:function(e,n){var t=M.hooks.all[e];if(t&&t.length)for(var r,a=0;r=t[a++];)r(n)}},Token:W};function W(e,n,t,r){this.type=e,this.content=n,this.alias=t,this.length=0|(r||"").length}function z(e,n,t,r){e.lastIndex=n;var a=e.exec(t);if(a&&r&&a[1]){var i=a[1].length;a.index+=i,a[0]=a[0].slice(i)}return a}function i(){var e={value:null,prev:null,next:null},n={value:null,prev:e,next:null};e.next=n,this.head=e,this.tail=n,this.length=0}function I(e,n,t){var r=n.next,a={value:t,prev:n,next:r};return n.next=a,r.prev=a,e.length++,a}function q(e,n,t){for(var r=n.next,a=0;a"+a.content+""+a.tag+">"},!u.document)return u.addEventListener&&(M.disableWorkerMessageHandler||u.addEventListener("message",function(e){var n=JSON.parse(e.data),t=n.language,r=n.code,a=n.immediateClose;u.postMessage(M.highlight(r,M.languages[t],t)),a&&u.close()},!1)),M;var t=M.util.currentScript();function r(){M.manual||M.highlightAll()}if(t&&(M.filename=t.src,t.hasAttribute("data-manual")&&(M.manual=!0)),!M.manual){var a=document.readyState;"loading"===a||"interactive"===a&&t&&t.defer?document.addEventListener("DOMContentLoaded",r):window.requestAnimationFrame?window.requestAnimationFrame(r):window.setTimeout(r,16)}return M}(_self);"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism);
4 | Prism.languages.markup={comment://,prolog:/<\?[\s\S]+?\?>/,doctype:{pattern:/"'[\]]|"[^"]*"|'[^']*')+(?:\[(?:[^<"'\]]|"[^"]*"|'[^']*'|<(?!!--)|)*\]\s*)?>/i,greedy:!0,inside:{"internal-subset":{pattern:/(\[)[\s\S]+(?=\]>$)/,lookbehind:!0,greedy:!0,inside:null},string:{pattern:/"[^"]*"|'[^']*'/,greedy:!0},punctuation:/^$|[[\]]/,"doctype-tag":/^DOCTYPE/,name:/[^\s<>'"]+/}},cdata://i,tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"special-attr":[],"attr-value":{pattern:/=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/,inside:{punctuation:[{pattern:/^=/,alias:"attr-equals"},/"|'/]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:[{pattern:/&[\da-z]{1,8};/i,alias:"named-entity"},/?[\da-f]{1,8};/i]},Prism.languages.markup.tag.inside["attr-value"].inside.entity=Prism.languages.markup.entity,Prism.languages.markup.doctype.inside["internal-subset"].inside=Prism.languages.markup,Prism.hooks.add("wrap",function(a){"entity"===a.type&&(a.attributes.title=a.content.replace(/&/,"&"))}),Object.defineProperty(Prism.languages.markup.tag,"addInlined",{value:function(a,e){var s={};s["language-"+e]={pattern:/(^$)/i,lookbehind:!0,inside:Prism.languages[e]},s.cdata=/^$/i;var t={"included-cdata":{pattern://i,inside:s}};t["language-"+e]={pattern:/[\s\S]+/,inside:Prism.languages[e]};var n={};n[a]={pattern:RegExp("(<__[^>]*>)(?:))*\\]\\]>|(?!)".replace(/__/g,function(){return a}),"i"),lookbehind:!0,greedy:!0,inside:t},Prism.languages.insertBefore("markup","cdata",n)}}),Object.defineProperty(Prism.languages.markup.tag,"addAttribute",{value:function(a,e){Prism.languages.markup.tag.inside["special-attr"].push({pattern:RegExp("(^|[\"'\\s])(?:"+a+")\\s*=\\s*(?:\"[^\"]*\"|'[^']*'|[^\\s'\">=]+(?=[\\s>]))","i"),lookbehind:!0,inside:{"attr-name":/^[^\s=]+/,"attr-value":{pattern:/=[\s\S]+/,inside:{value:{pattern:/(=\s*(["']|(?!["'])))\S[\s\S]*(?=\2$)/,lookbehind:!0,alias:[e,"language-"+e],inside:Prism.languages[e]},punctuation:[{pattern:/^=/,alias:"attr-equals"},/"|'/]}}}})}}),Prism.languages.html=Prism.languages.markup,Prism.languages.mathml=Prism.languages.markup,Prism.languages.svg=Prism.languages.markup,Prism.languages.xml=Prism.languages.extend("markup",{}),Prism.languages.ssml=Prism.languages.xml,Prism.languages.atom=Prism.languages.xml,Prism.languages.rss=Prism.languages.xml;
5 | !function(s){var e=/("|')(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/;s.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:/@[\w-](?:[^;{\s]|\s+(?![\s{]))*(?:;|(?=\s*\{))/,inside:{rule:/^@[\w-]+/,"selector-function-argument":{pattern:/(\bselector\s*\(\s*(?![\s)]))(?:[^()\s]|\s+(?![\s)])|\((?:[^()]|\([^()]*\))*\))+(?=\s*\))/,lookbehind:!0,alias:"selector"},keyword:{pattern:/(^|[^\w-])(?:and|not|only|or)(?![\w-])/,lookbehind:!0}}},url:{pattern:RegExp("\\burl\\((?:"+e.source+"|(?:[^\\\\\r\n()\"']|\\\\[^])*)\\)","i"),greedy:!0,inside:{function:/^url/i,punctuation:/^\(|\)$/,string:{pattern:RegExp("^"+e.source+"$"),alias:"url"}}},selector:RegExp("[^{}\\s](?:[^{};\"'\\s]|\\s+(?![\\s{])|"+e.source+")*(?=\\s*\\{)"),string:{pattern:e,greedy:!0},property:/(?!\s)[-_a-z\xA0-\uFFFF](?:(?!\s)[-\w\xA0-\uFFFF])*(?=\s*:)/i,important:/!important\b/i,function:/[-a-z0-9]+(?=\()/i,punctuation:/[(){};:,]/},s.languages.css.atrule.inside.rest=s.languages.css;var t=s.languages.markup;t&&(t.tag.addInlined("style","css"),t.tag.addAttribute("style","css"))}(Prism);
6 | Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0,greedy:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/(\b(?:class|interface|extends|implements|trait|instanceof|new)\s+|\bcatch\s+\()[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,boolean:/\b(?:true|false)\b/,function:/\w+(?=\()/,number:/\b0x[\da-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/[<>]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/,punctuation:/[{}[\];(),.:]/};
7 | Prism.languages.javascript=Prism.languages.extend("clike",{"class-name":[Prism.languages.clike["class-name"],{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$A-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\.(?:prototype|constructor))/,lookbehind:!0}],keyword:[{pattern:/((?:^|})\s*)catch\b/,lookbehind:!0},{pattern:/(^|[^.]|\.\.\.\s*)\b(?:as|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally(?=\s*(?:\{|$))|for|from(?=\s*(?:['"]|$))|function|(?:get|set)(?=\s*(?:[#\[$\w\xA0-\uFFFF]|$))|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,lookbehind:!0}],function:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,number:/\b(?:(?:0[xX](?:[\dA-Fa-f](?:_[\dA-Fa-f])?)+|0[bB](?:[01](?:_[01])?)+|0[oO](?:[0-7](?:_[0-7])?)+)n?|(?:\d(?:_\d)?)+n|NaN|Infinity)\b|(?:\b(?:\d(?:_\d)?)+\.?(?:\d(?:_\d)?)*|\B\.(?:\d(?:_\d)?)+)(?:[Ee][+-]?(?:\d(?:_\d)?)+)?/,operator:/--|\+\+|\*\*=?|=>|&&=?|\|\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?\?=?|\?\.?|[~:]/}),Prism.languages.javascript["class-name"][0].pattern=/(\b(?:class|interface|extends|implements|instanceof|new)\s+)[\w.\\]+/,Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:/((?:^|[^$\w\xA0-\uFFFF."'\])\s]|\b(?:return|yield))\s*)\/(?:\[(?:[^\]\\\r\n]|\\.)*]|\\.|[^/\\\[\r\n])+\/[dgimyus]{0,7}(?=(?:\s|\/\*(?:[^*]|\*(?!\/))*\*\/)*(?:$|[\r\n,.;:})\]]|\/\/))/,lookbehind:!0,greedy:!0,inside:{"regex-source":{pattern:/^(\/)[\s\S]+(?=\/[a-z]*$)/,lookbehind:!0,alias:"language-regex",inside:Prism.languages.regex},"regex-flags":/[a-z]+$/,"regex-delimiter":/^\/|\/$/}},"function-variable":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)\s*=>))/,alias:"function"},parameter:[{pattern:/(function(?:\s+(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)?\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\))/,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*=>)/i,inside:Prism.languages.javascript},{pattern:/(\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*=>)/,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*)\(\s*|\]\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*\{)/,lookbehind:!0,inside:Prism.languages.javascript}],constant:/\b[A-Z](?:[A-Z_]|\dx?)*\b/}),Prism.languages.insertBefore("javascript","string",{hashbang:{pattern:/^#!.*/,greedy:!0,alias:"comment"},"template-string":{pattern:/`(?:\\[\s\S]|\${(?:[^{}]|{(?:[^{}]|{[^}]*})*})+}|(?!\${)[^\\`])*`/,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\${(?:[^{}]|{(?:[^{}]|{[^}]*})*})+}/,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\${|}$/,alias:"punctuation"},rest:Prism.languages.javascript}},string:/[\s\S]+/}}}),Prism.languages.markup&&(Prism.languages.markup.tag.addInlined("script","javascript"),Prism.languages.markup.tag.addAttribute("on(?:abort|blur|change|click|composition(?:end|start|update)|dblclick|error|focus(?:in|out)?|key(?:down|up)|load|mouse(?:down|enter|leave|move|out|over|up)|reset|resize|scroll|select|slotchange|submit|unload|wheel)","javascript")),Prism.languages.js=Prism.languages.javascript;
8 |
--------------------------------------------------------------------------------