├── .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 | 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 += ``; 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 |
134 |
${bsCode}
135 |
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 |
234 |
${ebsCode}
235 |
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 | Max heap illustration photo 213 | Max heap illustration photo 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 | Merge sort illustration photo 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 | Radix LSD sort illustration photo 242 |
    243 | Nguồn ảnh: GeeksforGeek 244 |
    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 | 73 | 74 | 75 |
    76 |
    77 | 78 |
    79 | 80 | 81 |
    82 | 83 |
    84 | 85 | 86 |
    87 | 88 |
    89 | 90 | 91 |
    92 | 93 |
    94 | 95 | 96 |
    97 |
    98 | 99 |
    100 | 103 | 105 | 108 | 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 |
      131 |
        132 |
        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+""},!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"},/&#x?[\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 | --------------------------------------------------------------------------------