氣泡排序(Bubble Sort)算是前端最簡單的演算法,也是最經典的排序演算法了。網上JavaScript版本的氣泡排序很多,今天用Vue實現一個動態的視覺化氣泡排序。
01、JavaScript氣泡排序
氣泡排序原理也比較簡單,就是相鄰元素兩兩比較排序,把大的元素氣泡排序到後面,遞迴所有相鄰元素組合完成排序。
1.1、原理
有一個無序陣列:let arr = [100, 5, 6, 17, 3, 1]
,長度為len=6
。
- ①、從第一位元素100(0索引)開始,比較相鄰
arr[0]、arr[1]
元素的大小,大的排後面,如果arr[0]>arr[1]
則交換值位置。 - ②、如下圖,依次相鄰元素比較、交換,一輪完成後,最大元素就到了最右邊了。這個過程中,最大的元素(最大的泡泡)就像冒泡一樣到了末尾。
- ③、然後繼續對剩下的前面
len-1=5
個元素重複上述步驟,直到只剩下一個元素。這是一個遞迴的過程,遞迴到第一個元素,就完成了氣泡排序。
氣泡排序的動畫過程如下圖,排序過程很直觀,一目瞭然,下一章節也實現一個跟好的。
1.2、JavaScript實現
經典氣泡排序演算法,用兩個for
迴圈來實現所有元素的兩兩對比排序。統計了一下排序次數,一共比較了15次。氣泡排序的時間複雜度是O(n^2),這是最大值,最小為O(n)。
//經典氣泡排序演算法
//從小到大氣泡排序
let arr = [100, 5, 6, 17, 3, 1];
let count=0; //計數器
function bubbleSort(arr) {
const len = arr.length;
let t;count=0;
for (let i = 0; i < len - 1; i++) {
for (let j = 0; j < len - i - 1; j++) {
count++;
//比較相鄰兩個元素
if (arr[j] > arr[j + 1]) {
//交換兩個元素,大的往後排列
t = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = t;
}
}
}
return arr;
}
console.log(bubbleSort(arr),"比較次數:",count);
//[1, 3, 5, 6, 17, 100] '比較次數:' 15
上面程式碼中交換兩個元素位置的時候,用了一箇中間變數(t),可以改進一下。用一個解構賦值來交換值,就不用額外的一箇中間變數(t)了。
let arr = [100, 5, 6, 17, 3, 1];
function bubbleSort(arr) {
const len = arr.length;
for (let i = 0; i < len - 1; i++) {
for (let j = 0; j < len - i - 1; j++) {
//比較相鄰兩個元素
if (arr[j] > arr[j + 1]) {
//用結構賦值進行交換
[arr[j], arr[j + 1]] = [arr[j+1], arr[j]];
}
}
}
return arr;
}
console.log(bubbleSort(arr));
//[1, 3, 5, 6, 17, 100]
02、Vue實現一個冒泡動畫
用Vue來實現一個視覺化的氣泡排序,用Vue就不用去操作Dom了,只需要要處理好排序過程即可,因此首先要對排序過程進行改造。
2.1、排序過程改造
上一章節的排序是連續執行,瞬間完成的。要實現視覺化的排序效果,每一個排序步驟之間得有間隔,給過渡動畫留時間。就需要把排序的每一個步驟拆開,可以單獨控制執行。
- 定義一個排序物件
SortItem
,包裝待排序元素,用於視覺化展示,屬性包括排序值、泡泡大小、泡泡顏色。 - 用上面的排序物件
SortItem
,生成排序物件集合,正式排序步驟中用該集合。方法的引數為排序元素字串,空格隔開,如“9 100 6 17 3 1”。
//定義一個排序物件,包裝待排序元素
function SortItem(n) {
this.value = n;
this.size = 30 + n + 'px'; //泡泡大小,初試大小30px
this.color = bubbleColor.default;
}
//生成排序物件集合,引數為排序元素字串,如“9 100 6 17 3 1”
function generateSortItems(arrStr) {
let arrItems = [];
let arr = arrStr.trim().split(' ');
for (let i = 0; i < arr.length; i++) {
arrItems[i] = new SortItem(window.parseInt(arr[i]));
}
return arrItems;
}
泡泡列表展示效果如下:
- 然後就是排序過程了,對排序物件集合進行遍歷,把每一次排序操作包裝成一個(閉包)函式,放到一個集合裡,後面就可自定義呼叫執行了。
- 先是用集合實現了一遍,發現這個場景用迭代器
Generator
更優雅,馬上重構,上迭代器!每次迭代yield
返回排序的函式。
//迭代器實現排序步驟的拆分
function* generateSortFunc(items) {
const len = items.length;
for (let i = 0; i < len - 1; i++) {
for (let j = 0; j < len - i - 1; j++) {
//迭代器返回的是一個(閉包)函式,為每一個排序步驟
yield () => {
//執行排序前重置泡泡顏色
resetColor(items);
//正在排序的泡泡元素高亮
items[j].color = bubbleColor.inprocess;
items[j + 1].color = bubbleColor.inprocess;
if (items[j].value > items[j + 1].value) {
[items[j], items[j + 1]] = [items[j + 1], items[j]];
}
}
}
//完成一輪排序,末尾泡泡元素標記為完成態顏色
items[len - i - 1].color = bubbleColor.completed;
}
}
?什麼是Generator?
- 她是一個迭代器,返回一個遍歷器物件,符合可迭代協議和迭代器協議,可用
next()
、for(of)
迭代。 - 她是可控函式:內部程式碼可以自由控制暫停和繼續執行。標準的函式是一次性執行完畢,直到末尾或
return
語句。而生成器的函式可以由yield
暫停執行(交出控制權),next()
恢復執行。 - 她是一個狀態機,封裝了多個內部狀態。
- 她是非同步任務管理容器,提供一種非同步的實現方案。
Generator 使用一個特殊的函式語法function*
(帶星*
號)建立生成器generator
,呼叫生成器函式獲得一個生成器物件,該物件的例項方法:
例項方法 | 描述 |
---|---|
next() | 恢復執行,返回一個由 yield 表示式生成的值:{value: 1, done: false} |
return(value?) | 返回給定的值並結束生成器,可提前中止生成器。 |
throw() | 向生成器丟擲一個錯誤,生成器內部如沒處理則會中止 |
2.2、視覺化排序
接下來就不難了,排序的執行就是呼叫執行器的next()
方法,如果返回物件的done
屬性為true
,則表示迭代完成,否則繼續迭代,執行排序函式。
//單步執行
play() {
let next = this.sortFunc.next();
if (next.done) {
this.sortItems.forEach(item => item.color = bubbleColor.completed);
this.stop();
}
else
next.value();
},
<li>
元素來顯示排序物件。- 移動動畫用的Vue的
<transition-group>
元件來實現。 - “單步執行”就是點選一次只執行一步,“自動執行”則會自動順序執行。
- 修改引數後需”重置“進行初始化。
手動單步執行效果:
自動執行或更多排序引數可以直接檢視線上程式碼示例:掘金-程式碼(視覺化冒泡),完整程式碼也在這裡。
參考資料
©️版權申明:版權所有@安木夕,本文內容僅供學習,歡迎指正、交流,轉載請註明出處!原文編輯地址-語雀