我用js寫了一個冒泡排序法,怎麼用html和css把排序過程展現出來?
01-06
具體怎麼實現都不說,答什麼題。使用 HTML 實現排序過程,就是把排序的每一步的狀態同步到 DOM 上。如果要實現可視化效果,要求每一步排序是定時順序執行的。核心的難點是如何將排序過程「暫停」。下面是一個正常的冒泡排序結構:
for(i) {
for(j) {
// 輸出當前狀態到 DOM
}
}
你可以把你的排序演算法改成回調加定時器的方式,但是這樣你可能會把你原來的幾層循環嵌套改得面目全非。
JavaScript 中,有 co + generator 或者 async/await 「同步」方案,藉助 babel 可以編譯成 es5。正好適合這種需求。
我簡單寫了個 Demo,地址為:http://meowtec.github.io/demo/sort-v/. 下面是排序可視化的部分源碼,可以看到它的邏輯和結構和原始的排序演算法幾乎沒有變化。
async sort() {
for (var j = 0; j &< this.count; j++) {
let finish = true
for (var i = 0; i &< this.count - j - 1; i ++) {
// 獲取數字
var a = this.getNumber(i)
var b = this.getNumber(i + 1)
this.info(`比較${a}和${b}`)
this.style(i, "active")
this.style(i + 1, "active")
await sleep(1000)
if (a &> b) {
finish = false
this.info(`${a}比${b}要大,需要交換`)
this.style(i, "light")
this.style(i + 1, "light")
await sleep(1000)
// 交換 i 和 i + 1
this.exchange(i, i + 1)
await sleep(1000)
}
else {
this.info(`${a}不比${b}大,跳過`)
await sleep(1000)
}
this.unstyle(i)
this.unstyle(i + 1)
}
if (finish) {
this.info("排序完成!")
break
}
}
}
源碼我沒有上傳,剩下的源碼基本上都是原生的操作 DOM,沒什麼好看的。
效果截圖:
匿名用戶(答案刪除了)提到的把每一步過程信息記錄到一個數組,然後回放這個數組也是可行的方案。
你們這些程序員,不要總是想搞個大新聞。三十行原生能解決的事情,非要引入這庫那庫 ES678,搞個成千上萬行代碼,Excited!我是見得多了,前端哪個類庫我沒用過?Github 上的明哥,比你們高到不知道哪裡去了,我跟他談笑風生!別人造的輪子,你再拿來用一遍,你等於……你也有責任吧?
&
&
&&
&&
&&
&&
&&
&&
&&
&&
&&
&&
&
&
預覽:RunJS
VisuAlgo - Sorting (Bubble, Selection, Insertion, Merge, Quick, Counting, Radix)
維護一個數組,在排序的每一步結束後,將當前狀態壓入數組。當整個排序過程結束後,你就得到了一個存儲了排序過程中所有狀態的數組。然後再按照數組的順序,依次繪製每一個狀態。
http://static.ricterz.me/kp.html
非前端代碼渣勿噴…
---裡面實現了快速排序/選擇排序/冒泡排序的可視化…思路…看代碼最明確了吧…好懷念寫代碼的時光啊233
好多年前, @趙劼 的windjs 庫中就有這樣的demo, 還有相關的ppt的
ppt 在這裡 上周末Jscex項目介紹的幻燈片
使用Jscex實現排序演算法動畫
用了 @Vkki 代碼里的(記錄狀態)思想,d3.js實現了繪圖,添加了點效果(條狀圖上顯示數字、加亮顯示交換元素、加亮顯示排序好的元素):https://github.com/mikemelon/SortVisualization
謝邀。
VisuAlgo 是很牛逼的一個項目,不過很可惜它是不可商用的,我們不會像某些公司那樣不顧別人的聲明直接厚顏無恥地剽竊的 。
我們自己開發了一個。
這是我們數據結構課中使用的圖形化教學模塊,這課演示的是冒泡排序:
至於怎麼實現,我並不能說太多,我只能說我們用了 D3.js。
TL;DR: 在線例子及代碼 - Plunker
我來講一下要解決的幾個問題和 tips。
如何實現「交換」的動畫
我覺得這個是整個應用的難點。
看了 visual.net),他的實現是利用 CSS3 的 transform 函數裡面的 translate。這似乎是不錯的想法,但是我發現要用 JS 獲取 translate 對應的值的話,有點麻煩。
接著,我又看了一個答案的實現。他的方法是利用 CSS 中的 left。而 left 的值是相對於設定了 position: relative 的 parent。
我不是很懂上面的方法。但是我想到了另一個方法:left 是相對於他本身。也就是設置他本身為 position: relative 而不是他的 parent。
.bar {
position: relative;
left: 0; /* must set, or not transition when value in falsy */
/*transition: left 1s;*/
}
我之所以設置 left 為 0,是因為我們要用 CSS3 的 transition 實現漸變的效果。
然後,每次交換的時候,我們只需要把他當前的 left 加/減 柱形圖的寬度。沒錯,這就是這種方法的「缺點」之一,我們需要用 JS 寫死柱形圖的寬度。
swap () {
// ...
const getLeft = item =&>
parseInt(item.style.left.slice(0, -2)) || 0 // rm "px"
item1.style.left = `${this.barWidth + getLeft(item1)}px`
item2.style.left = `${-this.barWidth + getLeft(item2)}px`
}
在交換的時候,我們要先找到對應的 DOM 元素,也就是上面代碼中的 item 和 item2。
怎麼找?(假設我們排序的是一堆數字)
一開始我想到了兩種方法。第一種是,根據數字,找到具有對用數字的 DOM 元素,就像這樣:
swap (value1, value2) {
const item1 = this.el.querySelector(`[data-value="${value1}"]`)
}
但這種方法是行不通的。為什麼?
因為當有多個數字相同的時候,我們找到的 DOM 元素不一定是我們想要交換的。因為 querySelector 返回的總是第一個匹配的元素。那我們在 item1 後面才開始找 item2 行不行?使用 CSS3 的 ~ 選擇器。比如這樣:
swap (value1, value2) {
const item1 = this.el.querySelector(`[data-value="${value1}"]`)
const item2 = this.el.querySelector(`[data-value="${value2}"] ~ data-value="${value2}"]`)
}
這種方法有時可以,有時是不可以的。為什麼?
因為我們的 DOM 結構根本就沒有變化,只是用 CSS 的 left 在視覺上改變了位置而已。
因此,我們的第二種方法當然就是在交換的時候,不僅改變 left 的值,而且交換 DOM 的位置。
function swapDOM(element1, element2) {
element1.parentNode.insertBefore(element2, element1);
}
但是,這種方法還是行不通的。因為 insertBefore 會把 element2 從 DOM 中刪掉。這樣的話,就沒有了「交換」的漸變效果。
果然,我們還是不能 DOM 元素。那麼只能返回第一種方法。
這個時候我突然想到了 React 中循環的時候需要寫上 key 屬性。然後。。。然後只要把 key 和 數字綁定在一起,查找數字對應的 DOM 元素只需要使用 key 就行。
// data = [64, 39, 78, 36]
this.items = data.map((d, i) =&> ({key: `key-${i}`, value: d}))
swap (key1, key2) {
const item1 = this.el.querySelector(`[data-key="${key1}"]`)
}
終於搞定了「交換」動畫了,不知道有沒有更好的實現方法呢?
為什麼我的「交換」動畫一瞬間就跑完了假設我們的排序演算法是這樣的:
sort () {
const items = this.items
for (let i = 0; i &< items.length; i++) {
for (let j = 0; j &< items.length - i - 1; j++) {
const item1 = items[j]
const item2 = items[j + 1]
if (less(item2.value, item1.value)) {
swap(j, j + 1, items)
}
}
}
return this.items
}
注意到,我們的 swap 函數是在 for 循環裡面調用的,這是沒錯的。
然而我們的「交換」動畫就跑完了,根本沒有漸變的效果,我們想要的是等到前一個交換動畫完成後,下一個才發生。為什麼會這樣?因為我們的兩個 for 是瞬間就可以跑完的= =
怎麼解決這個問題呢?setInterval?好像會把排序演算法的邏輯擾亂。
突然,我想到了 jQuery 好像有個動畫隊列的東西。也就是說,我們可以把所有的動畫先放在一個隊列裡面。然後再一個一個地出隊,一個一個地調用。
for () {
for () {
if (less(item2.value, item1.value)) {
this.queue.push(() =&> this.swap(item1.key, item2.key))
}
}
}
play () {
const intervalId = setInterval(() =&> {
if (this.queue.length === 0) {
clearInterval(intervalId)
} else {
const swap = this.queue.shift()
swap()
}
}, 2 * 1000)
}
看了知乎上的答案,這個問題還有其他實現的方法。總的來說,就是如何解決非同步編程問題。
Tips 可以利用 CSS 的 偽元素::before 以及 attr 函數,來實現柱形圖上顯示對應的數字。
.bar::before {
content: attr(data-value);
position: absolute;
top: -16px;
color: #233;
}
Continue我們的代碼還可以繼續優化,比如介面如何設計,使得可以擴展於其他的排序演算法。還有用戶體驗,比如,排序前中後狀態的顏色都設為不同,等等。
--------- 來自我的 blog
實現了一下冒泡排序、插入排序、選擇排序、快速排序、歸併排序、希爾排序的動畫演示。
對於保存每一步的狀態試過3種方法。
1.讓JS暫停。 減慢循環速度。 實現起來會有問題。2.使用閉包+setTimeout()。 代碼量多,不方便修改。難以實現排序速度加快或減慢。3.循環中保存排序狀態。再使用setInterval()。 最後使用的第三種。
另外有些排序如果要做動畫的話可能需要重寫排序的邏輯。
比如我開始寫的快速排序。使用的個簡單的遞歸,不是在同一個數組上進行的操作。就比較難得到數組當前的排序狀態。後來在遞歸上加入了起止位。在原數組上進行操作。就可以獲取到整個數組的狀態了。github:GitHub - liusaint/sortAnimation: JavaScript動畫展示常見排序演算法
Web 前端開發需要使用 MVVM 框架嗎? - 知乎用戶的回答
冒泡排序演算法的可視化
我在兩年前寫過 為了面試。。。當時寫的 回頭看看代碼 感覺好一般 不過為了面試 源碼啥的 都沒有壓縮效果如圖地址為 排序演算法動畫特效右鍵查看源代碼
繪圖用的是Canvas,數字越小高度越低、顏色越深。大概就是這樣~很早之前寫的了,包括冒泡、插入和選擇三種n2的排序方法。http://app.dingdewen.com/algorithm/sort/這幾種排序實質上都是兩重循環,把每一次交換的數對和產生的新序列push到一個數組裡,排序完成之後再用setInterval一步步呈現出來。排列和繪製是完全分開的,方便擴展任意排序演算法。
在線演示地址 可視化排序 by sean熊
我的思路是這樣,不改變原有演算法,收集演算法對數組的改動過程到隊列,然後用定時器展示具體過程下面是核心代碼:&
&
&
&
&
& &
&
&
var arr;
var el = $("#show");
var Queue;
var max;
var type;
$("#begin").click(function(){
if($(this).text() == "開始"){
run();
$(this).text("結束");
}else{
location.reload();
}
})
function init(){
arr = (function(){
var arr = [];
for(var i=0; i&< 50; i++){
arr.push(_.random(1,100));
}
return arr;
})()
max = _.max(arr)*20;
arr.forEach(function(num, index){
var height = num * 20 * 600/max;
el.append("&"+num+"&");
});
}
init();
function run(){
type = $("#type").val();
Queue = [];
if(type == 1){
insertSort(arr);
}else if(type ==2){
bubbleSort(arr);
}
window.interval = setInterval(function(){
if(Queue.length &> 0){
animation(Queue.shift());
}
}, 200)
}
function handle(){
var items;
items = _.sortBy(el.find(".num"), function(obj){
var left = $(obj).css("left");
return parseInt(left);
})
_.each(items, function(item, num){
$(item).attr("id", "num_"+num)
})
}
function animation(arr){
var num = arr[0];
var index = arr[1];
if(type ==1){
console.log(1111)
$("#num_"+(num)).animate({left: index*40 +"px"}, 100, handle);
for(var i=1; i&<= num-index; i++){
$("#num_"+(num-i)).animate({left: (num-i+1)*40 +"px"}, 100, handle);
}
}else if (type ==2 ){
handle();
$("#num_"+(num)).animate({left: index*40 +"px"}, 100);
$("#num_"+(index)).animate({left: num*40 +"px"}, 100);
}
}
function bubbleSort(array) {
var i = 0,
len = array.length,
j, d;
for (; i &< len; i++) {
for (j = 0; j &< len; j++) {
if (array[i] &< array[j]) {
d = array[j];
array[j] = array[i];
array[i] = d;
Queue.push([i, j]);
}
}
}
return array;
}
function insertSort(array) {
var i = 1,j, step, key, len = array.length;
for (; i &< len; i++) {
step = j = i;
key = array[j];
while (--j &> -1) {
if (array[j] &> key) {
array[j + 1] = array[j];
} else {
break;
}
}
array[j + 1] = key;
Queue.push([step, j+1]);
}
return array;
}
&
通過動畫可以看出,插入排序演算法速度要比冒泡快很多
隨便找一個mvvm的庫把你的數組綁定到DOM樹上就可以了~
題主,你是不是參加IFE…ヾ(?ω?ヾ)
我的思路是用維護一個數組,在排序的每一步結束後,把對當前dom的操作放入數組中,排序完成後,再根據數組繪製對dom的每一步操作,這樣就可以把整個過程顯示出來了。排序動畫
告訴我,你是不是在做百度前端學院的題
首先一點,要把計算機時鐘體現為固定的宏觀的時間段比如每隔1秒或者半秒執行一次圖形更新
自己寫的任務五:基礎JavaScript練習(二)
推薦閱讀:
※關於具名的IIFE內部對函數自身再賦值問題?
※官網網站用什麼技術棧比較合理?
※關於angular在指令中無法獲取子元素的問題?
※jQuery 和 YUI (Yahoo User Interface) 各自的優缺點有哪些?具體的使用場景是怎樣的?
TAG:前端開發 | JavaScript | 排序 | HTMLCSS |