/** * 排序算法演示逻辑 (15种算法) */ document.addEventListener('DOMContentLoaded', () => { const container = document.getElementById('sort-container'); const sizeInput = document.getElementById('array-size'); const algorithmSelect = document.getElementById('algorithm-select'); const speedSelect = document.getElementById('speed-select'); const btnGenerate = document.getElementById('btn-generate'); const btnSort = document.getElementById('btn-sort'); let array = []; let isSorting = false; let cancelSort = false; const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms)); function generateArray() { if (isSorting) return; container.innerHTML = ''; array = []; let size = parseInt(sizeInput.value); if (isNaN(size) || size < 10) size = 10; if (size > 100) size = 100; sizeInput.value = size; for (let i = 0; i < size; i++) { const value = Math.floor(Math.random() * 290) + 10; array.push(value); const bar = document.createElement('div'); bar.classList.add('sort-bar'); bar.style.height = `${value}px`; const barWidth = Math.max(2, Math.floor((container.clientWidth - size * 2) / size)); bar.style.width = `${barWidth}px`; container.appendChild(bar); } } function updateBar(index, height, colorClass) { const bars = container.children; if (bars[index]) { if (height !== null) bars[index].style.height = `${height}px`; bars[index].classList.remove('comparing', 'swapping', 'sorted'); if (colorClass) bars[index].classList.add(colorClass); } } // --- Algorithm Implementations --- async function bubbleSort() { const n = array.length; for (let i = 0; i < n - 1; i++) { for (let j = 0; j < n - i - 1; j++) { if (cancelSort) return; updateBar(j, null, 'comparing'); updateBar(j + 1, null, 'comparing'); await sleep(parseInt(speedSelect.value)); if (array[j] > array[j + 1]) { updateBar(j, null, 'swapping'); updateBar(j + 1, null, 'swapping'); let temp = array[j]; array[j] = array[j + 1]; array[j + 1] = temp; updateBar(j, array[j], null); updateBar(j + 1, array[j + 1], null); } else { updateBar(j, null, null); updateBar(j + 1, null, null); } } updateBar(n - i - 1, null, 'sorted'); } updateBar(0, null, 'sorted'); } async function selectionSort() { const n = array.length; for (let i = 0; i < n - 1; i++) { let minIndex = i; updateBar(minIndex, null, 'swapping'); for (let j = i + 1; j < n; j++) { if (cancelSort) return; updateBar(j, null, 'comparing'); await sleep(parseInt(speedSelect.value)); if (array[j] < array[minIndex]) { if (minIndex !== i) updateBar(minIndex, null, null); minIndex = j; updateBar(minIndex, null, 'swapping'); } else { updateBar(j, null, null); } } if (minIndex !== i) { updateBar(i, null, 'swapping'); await sleep(parseInt(speedSelect.value)); let temp = array[i]; array[i] = array[minIndex]; array[minIndex] = temp; updateBar(i, array[i], null); updateBar(minIndex, array[minIndex], null); } else { updateBar(i, null, null); } updateBar(i, null, 'sorted'); } updateBar(n - 1, null, 'sorted'); } async function insertionSort() { const n = array.length; updateBar(0, null, 'sorted'); for (let i = 1; i < n; i++) { if (cancelSort) return; let key = array[i]; let j = i - 1; updateBar(i, null, 'swapping'); await sleep(parseInt(speedSelect.value)); while (j >= 0 && array[j] > key) { if (cancelSort) return; updateBar(j, null, 'comparing'); await sleep(parseInt(speedSelect.value)); array[j + 1] = array[j]; updateBar(j + 1, array[j + 1], 'sorted'); updateBar(j, null, 'swapping'); j = j - 1; } array[j + 1] = key; updateBar(j + 1, array[j + 1], 'sorted'); for(let k=0; k<=i; k++) updateBar(k, null, 'sorted'); } } async function quickSort(start = 0, end = array.length - 1) { if (cancelSort) return; if (start >= end) { if(start === end && start >= 0 && start < array.length) updateBar(start, null, 'sorted'); return; } let pivotIndex = await partition(start, end); if (cancelSort) return; updateBar(pivotIndex, null, 'sorted'); await Promise.all([ quickSort(start, pivotIndex - 1), quickSort(pivotIndex + 1, end) ]); } async function partition(start, end) { let pivotValue = array[end]; let pivotIndex = start; updateBar(end, null, 'swapping'); for (let i = start; i < end; i++) { if (cancelSort) return start; updateBar(i, null, 'comparing'); updateBar(pivotIndex, null, 'comparing'); await sleep(parseInt(speedSelect.value)); if (array[i] < pivotValue) { let temp = array[i]; array[i] = array[pivotIndex]; array[pivotIndex] = temp; updateBar(i, array[i], null); updateBar(pivotIndex, array[pivotIndex], null); pivotIndex++; } else { updateBar(i, null, null); if (pivotIndex !== i) updateBar(pivotIndex, null, null); } } let temp = array[pivotIndex]; array[pivotIndex] = array[end]; array[end] = temp; updateBar(pivotIndex, array[pivotIndex], null); updateBar(end, array[end], null); return pivotIndex; } async function mergeSort(start = 0, end = array.length - 1) { if (cancelSort) return; if (start >= end) { if(start === end && start >=0) updateBar(start, null, 'sorted'); return; } let mid = Math.floor((start + end) / 2); await mergeSort(start, mid); await mergeSort(mid + 1, end); await merge(start, mid, end); } async function merge(start, mid, end) { let temp = []; let i = start, j = mid + 1; while(i <= mid && j <= end) { if(cancelSort) return; updateBar(i, null, 'comparing'); updateBar(j, null, 'comparing'); await sleep(parseInt(speedSelect.value)); if (array[i] <= array[j]) { temp.push(array[i]); updateBar(i, null, null); i++; } else { temp.push(array[j]); updateBar(j, null, null); j++; } } while(i <= mid) { temp.push(array[i]); i++; } while(j <= end) { temp.push(array[j]); j++; } for(let k = 0; k < temp.length; k++) { if(cancelSort) return; array[start + k] = temp[k]; updateBar(start + k, array[start + k], 'swapping'); await sleep(parseInt(speedSelect.value)); updateBar(start + k, null, 'sorted'); } } async function heapSort() { let n = array.length; for(let i = Math.floor(n/2)-1; i>=0; i--){ await heapify(n, i); } for(let i = n-1; i>0; i--){ if(cancelSort) return; updateBar(0, null, 'swapping'); updateBar(i, null, 'swapping'); await sleep(parseInt(speedSelect.value)); let temp = array[0]; array[0] = array[i]; array[i] = temp; updateBar(0, array[0], null); updateBar(i, array[i], 'sorted'); await heapify(i, 0); } if(!cancelSort) updateBar(0, null, 'sorted'); } async function heapify(n, i) { if(cancelSort) return; let largest = i; let l = 2*i + 1; let r = 2*i + 2; if (l < n) { updateBar(l, null, 'comparing'); updateBar(largest, null, 'comparing'); await sleep(parseInt(speedSelect.value)); if(array[l] > array[largest]) largest = l; updateBar(l, null, null); updateBar(i, null, null); } if (r < n) { updateBar(r, null, 'comparing'); updateBar(largest, null, 'comparing'); await sleep(parseInt(speedSelect.value)); if(array[r] > array[largest]) largest = r; updateBar(r, null, null); if(largest!=r) updateBar(largest, null, null); } if (largest !== i) { updateBar(i, null, 'swapping'); updateBar(largest, null, 'swapping'); await sleep(parseInt(speedSelect.value)); let temp = array[i]; array[i] = array[largest]; array[largest] = temp; updateBar(i, array[i], null); updateBar(largest, array[largest], null); await heapify(n, largest); } } async function shellSort() { let n = array.length; for (let gap = Math.floor(n/2); gap > 0; gap = Math.floor(gap/2)) { for (let i = gap; i < n; i++) { if(cancelSort) return; let temp = array[i]; let j; updateBar(i, null, 'swapping'); await sleep(parseInt(speedSelect.value)); for (j = i; j >= gap && array[j - gap] > temp; j -= gap) { if(cancelSort) return; updateBar(j - gap, null, 'comparing'); await sleep(parseInt(speedSelect.value)); array[j] = array[j - gap]; updateBar(j, array[j], 'swapping'); updateBar(j - gap, null, null); } array[j] = temp; updateBar(j, array[j], null); updateBar(i, null, null); } } } async function cocktailShakerSort() { let is_swapped = true; let start = 0; let end = array.length - 1; while(is_swapped) { is_swapped = false; for(let i=start; i array[i+1]){ updateBar(i, null, 'swapping'); updateBar(i+1, null, 'swapping'); let temp = array[i]; array[i] = array[i+1]; array[i+1] = temp; updateBar(i, array[i], null); updateBar(i+1, array[i+1], null); is_swapped = true; } else { updateBar(i, null, null); updateBar(i+1, null, null); } } if(!is_swapped) break; updateBar(end, null, 'sorted'); end--; is_swapped = false; for(let i=end-1; i>=start; i--){ if(cancelSort) return; updateBar(i, null, 'comparing'); updateBar(i+1, null, 'comparing'); await sleep(parseInt(speedSelect.value)); if(array[i] > array[i+1]){ updateBar(i, null, 'swapping'); updateBar(i+1, null, 'swapping'); let temp = array[i]; array[i] = array[i+1]; array[i+1] = temp; updateBar(i, array[i], null); updateBar(i+1, array[i+1], null); is_swapped = true; } else { updateBar(i, null, null); updateBar(i+1, null, null); } } updateBar(start, null, 'sorted'); start++; } } async function combSort() { let n = array.length; let gap = n; let swapped = true; while(gap !== 1 || swapped) { if(cancelSort) return; gap = Math.floor(gap / 1.3); if(gap < 1) gap = 1; swapped = false; for(let i=0; i array[i+gap]){ updateBar(i, null, 'swapping'); updateBar(i+gap, null, 'swapping'); let temp = array[i]; array[i] = array[i+gap]; array[i+gap] = temp; updateBar(i, array[i], null); updateBar(i+gap, array[i+gap], null); swapped = true; } else { updateBar(i, null, null); updateBar(i+gap, null, null); } } } } async function gnomeSort() { let index = 0; while(index < array.length) { if(cancelSort) return; if(index == 0) index++; updateBar(index, null, 'comparing'); updateBar(index-1, null, 'comparing'); await sleep(parseInt(speedSelect.value)); if(array[index] >= array[index-1]){ updateBar(index, null, null); updateBar(index-1, null, null); index++; } else { updateBar(index, null, 'swapping'); updateBar(index-1, null, 'swapping'); let temp = array[index]; array[index] = array[index-1]; array[index-1] = temp; updateBar(index, array[index], null); updateBar(index-1, array[index-1], null); index--; } } } async function oddEvenSort() { let isSorted = false; while(!isSorted) { isSorted = true; for(let i=1; i<=array.length-2; i=i+2){ if(cancelSort) return; updateBar(i, null, 'comparing'); updateBar(i+1, null, 'comparing'); await sleep(parseInt(speedSelect.value)); if(array[i] > array[i+1]){ updateBar(i, null, 'swapping'); updateBar(i+1, null, 'swapping'); let temp = array[i]; array[i] = array[i+1]; array[i+1] = temp; updateBar(i, array[i], null); updateBar(i+1, array[i+1], null); isSorted = false; } else { updateBar(i, null, null); updateBar(i+1, null, null); } } for(let i=0; i<=array.length-2; i=i+2){ if(cancelSort) return; updateBar(i, null, 'comparing'); updateBar(i+1, null, 'comparing'); await sleep(parseInt(speedSelect.value)); if(array[i] > array[i+1]){ updateBar(i, null, 'swapping'); updateBar(i+1, null, 'swapping'); let temp = array[i]; array[i] = array[i+1]; array[i+1] = temp; updateBar(i, array[i], null); updateBar(i+1, array[i+1], null); isSorted = false; } else { updateBar(i, null, null); updateBar(i+1, null, null); } } } } async function cycleSort() { let n = array.length; for(let cycle_start = 0; cycle_start <= n-2; cycle_start++) { if(cancelSort) return; let item = array[cycle_start]; let pos = cycle_start; for(let i = cycle_start+1; i1; --curr_size){ if(cancelSort) return; let mi = await findMax(curr_size); if(cancelSort) return; if(mi != curr_size-1){ await flip(mi); if(cancelSort) return; await flip(curr_size-1); } updateBar(curr_size-1, null, 'sorted'); } updateBar(0, null, 'sorted'); } async function findMax(n){ let max_idx = 0; for(let i=0; i array[max_idx]){ updateBar(max_idx, null, null); max_idx = i; } else { updateBar(i, null, null); } } updateBar(max_idx, null, null); return max_idx; } async function flip(i){ let start = 0; while(start < i){ updateBar(start, null, 'swapping'); updateBar(i, null, 'swapping'); await sleep(parseInt(speedSelect.value)); let temp = array[start]; array[start] = array[i]; array[i] = temp; updateBar(start, array[start], null); updateBar(i, array[i], null); start++; i--; } } async function radixSort() { let max = Math.max(...array); for(let exp = 1; Math.floor(max/exp) > 0; exp *= 10) { if(cancelSort) return; await countingSortForRadix(exp); } } async function countingSortForRadix(exp) { let output = new Array(array.length).fill(0); let count = new Array(10).fill(0); for(let i=0; i=0; i--){ let idx = Math.floor(array[i]/exp)%10; output[count[idx]-1] = array[i]; count[idx]--; } for(let i=0; i0; i--){ let j = Math.floor(Math.random()*(i+1)); let temp = array[i]; array[i] = array[j]; array[j] = temp; updateBar(i, array[i], 'swapping'); updateBar(j, array[j], 'swapping'); } await sleep(parseInt(speedSelect.value)); for(let i=0; i 200) { alert("猴子排序 (Bogo Sort) 太慢了,为防止卡死,已自动终止!"); return; } } } // ---------------------------------------- // UI Controls // ---------------------------------------- function disableControls(disabled) { sizeInput.disabled = disabled; algorithmSelect.disabled = disabled; btnSort.disabled = disabled; if(disabled) { btnGenerate.innerText = '⏹️ 停止排序'; btnSort.style.opacity = '0.5'; } else { btnGenerate.innerText = '🔄 生成新数据'; btnSort.style.opacity = '1'; } } btnGenerate.addEventListener('click', () => { if (isSorting) { cancelSort = true; isSorting = false; disableControls(false); setTimeout(generateArray, 200); } else { generateArray(); } }); btnSort.addEventListener('click', async () => { if (isSorting) return; const isAlreadySorted = Array.from(container.children).every(el => el.classList.contains('sorted')); if(isAlreadySorted || array.length === 0) { generateArray(); await sleep(200); } isSorting = true; cancelSort = false; disableControls(true); const algo = algorithmSelect.value; try { switch(algo) { case 'bubble': await bubbleSort(); break; case 'selection': await selectionSort(); break; case 'insertion': await insertionSort(); break; case 'quick': await quickSort(); break; case 'merge': await mergeSort(); break; case 'heap': await heapSort(); break; case 'shell': await shellSort(); break; case 'cocktail': await cocktailShakerSort(); break; case 'comb': await combSort(); break; case 'gnome': await gnomeSort(); break; case 'oddEven': await oddEvenSort(); break; case 'cycle': await cycleSort(); break; case 'pancake': await pancakeSort(); break; case 'radix': await radixSort(); break; case 'bogo': await bogoSort(); break; } if(!cancelSort) { for(let i=0; i { if (array.length > 0 && container.clientWidth > 0) { const size = array.length; const barWidth = Math.max(2, Math.floor((container.clientWidth - size * 2) / size)); const bars = container.children; for (let i = 0; i < bars.length; i++) { bars[i].style.width = `${barWidth}px`; } } }); generateArray(); });