iT邦幫忙

2026 iThome 鐵人賽

DAY 9
0

昨天已經成功把隨機產生的 Array 畫成長條圖,今天終於要開始挑戰這個專案最重要的部分:讓 Bubble Sort 動起來。

目前我把畫面分成三個區塊:左側顯示原始資料與目前步驟,中間顯示排序的長條圖,右側則放 Bubble Sort 程式碼,希望之後執行排序時,也可以跟著目前的步驟一起高亮。
https://ithelp.ithome.com.tw/upload/images/20260922/20184088iWo0lTyQY9.png

下面是我 Day 6 已經寫過的 Bubble Sort:

for (let j = arr.length; j > 1; j--) {
  for (let i = 0; i < j - 1; i++) {
    if (arr[i] > arr[i + 1]) {
      const temp = arr[i]
      arr[i] = arr[i + 1]
      arr[i + 1] = temp
    }
  }
}

如果想讓長條圖透過顏色區分目前的排序狀態,應該只要知道「目前比較到哪兩個數字」就可以了吧?

所以我一開始有了以下的想法。


第一個想法:把正在比較的 index 記起來

因為 Bubble Sort 的內層迴圈本來就有 i,所以目前正在比較的位置其實就是:

[i, i + 1]

例如:

arr = [5, 3, 8, 1]

第一次比較的是 index 0、1,下一次則是 1、2。

所以我原本想建立幾個狀態:

const comparing = ref<number[]>([])
const sorted = ref<number[]>([])

每次進入內層迴圈,就記錄目前比較的位置:

comparing.value = [i, i + 1]

這樣 template 就能透過 index 判斷目前哪些長條需要改變樣式:

:class="{
  comparing: comparing.includes(index),
  sorted: sorted.includes(index)
}"

而每一個 Pass 結束後,j - 1 的位置已經確定,因此也可以把它加入 sorted。

到這裡看起來好像都很合理。


但為什麼畫面沒有一步一步動?

真正實作後,我才發現最大的問題不是「狀態要存什麼」,而是:

Bubble Sort 跑得太快了。

我原本想像的執行過程是:

comparing = [0, 1]
↓
畫面更新
↓
交換
↓
comparing = [1, 2]
↓
畫面再次更新

但 for 迴圈是同步執行的,它不會每改一次狀態,就停下來等畫面完成這一步再繼續。

所以實際執行更接近:

[0,1] → [1,2] → [2,3] → Pass 完成 → 下一個 Pass → 排序完成
                                                        ↓
                                                    畫面更新

也就是說,即使我知道每個時間點應該設定什麼狀態,這些狀態還是會在很短的時間內被下一個狀態覆蓋掉。

這時我才發現,我真正要解決的問題不是:

怎麼讓 Bubble Sort 慢慢執行?

而是:

怎麼把 Bubble Sort 的執行過程保存下來,再慢慢播放?


不讓演算法變慢,而是把每一步記下來

最後我的做法是建立 SortStep,把每一個重要時間點需要的資料存成一份快照:

interface SortStep {
  arr: number[]
  barStates: BarState[]
  highlightLines: number[]
  phase: string
  pass: number
  summary: string
  detail: string
}

其中 arr 記錄當下的陣列、barStates 記錄每根長條的狀態、highlightLines 則提供右側程式碼高亮使用。

Bubble Sort 本身還是會很快執行完,不同的是,我會在「比較、交換、Pass 完成」這些重要時間點,把當下的狀態 push 進 steps。

概念上可以先簡化成:

const steps = []

for (...) {
  for (...) {

    // 記錄「比較」的狀態
    steps.push({
      arr: [...arr],
      phase: 'compare'
    })

    if (...) {
      // 執行交換
      // ...

      // 記錄「交換完成」的狀態
      steps.push({
        arr: [...arr],
        phase: 'swap'
      })
    }
  }

  // 記錄這個 Pass 完成
  steps.push({
    arr: [...arr],
    phase: 'pass'
  })
}

這裡很重要的一點是:

steps.push() 不是讓 Bubble Sort 停下來。

Bubble Sort 還是會一次執行完成,只是在執行的過程中,把每個重要時間點「拍照保存」。

因此最後會得到類似:

Step 0 → 初始狀態
Step 1 → 比較 index 0、1
Step 2 → 交換 index 0、1
Step 3 → 比較 index 1、2
Step 4 → 比較 index 2、3
Step 5 → 交換 index 2、3
Step 6 → Pass 完成
...

這樣前面的狀態就不會因為下一次迴圈執行而消失,而是全部保存在 steps 裡。


那要怎麼讓這些 Step 動起來?

有了所有 Step 之後,畫面就不需要直接跟著 Bubble Sort 的 for 迴圈跑了。

我另外使用:

currentStep

記錄「現在播放到第幾步」,再取得目前要顯示的 Step:

const step = steps[currentStep]

播放時只要控制:

Step 0
↓
等待
↓
Step 1
↓
等待
↓
Step 2
↓
等待
↓
Step 3

畫面就有時間根據每個 Step 的 arr、barStates、highlightLines 更新長條圖與右側程式碼。

所以真正被「放慢」的其實不是 Bubble Sort,而是 Step 的播放速度。


原來讓演算法一步一步播放,很像在拍電影

這次實作最困難的地方,是我原本一直以為要讓 Bubble Sort 的 for 迴圈慢慢執行。

實際做完才發現,真正要做的是把「比較、交換、排序完成」等重要時間點記錄下來,再按照順序慢慢播放。

這個過程就像拍電影一樣,先把每個重要畫面記錄成 Step,最後再一張一張播放,就形成了排序動畫。

今天終於從一張靜態的長條圖,走到可以把 Bubble Sort 的排序過程真正「播放」出來。

最後放上今天完成的成果圖~
https://ithelp.ithome.com.tw/upload/images/20260922/201840887wj8IsYJXl.png


上一篇
【Day 8】Vue 實作 — Array 如果看得見會長怎樣?畫出排序資料
下一篇
【Day 10】熟悉又陌生的遞迴 Recursion
系列文
看得到的演算法:用 Vue 3 打造演算法互動視覺化平台 共 12 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言