动画演示排序算法
刚开始的思路是用callback返回每一次交换时的元素下标,然后改变left,和top来实现动画演示效果。但是在for循环中setTimeout是不会阻塞代码的执行的,所以得到的是遍历完了的结果。最后,用缓存每一次交换的两个元素对应的dom的下标的方式保存交换的顺序,再用setTimeout显示出来。为了保存数组对应的dom,用
{ {
data: ,index:, 的方式,而用 first:,second:, 来存放每次交换的两个元素对应的dom的下标。
} }
希望实现堆排序的完全二叉树展示,未成功,待完成。。。
<!doctype html> <html> <head> <style> .bubble { width: 50px; height: 50px; border: 1px solid #fff; border-radius: 50%; position: absolute; transition: all 0.5s linear; text-align: center; line-height: 50px; } .shadow { background: radial-gradient(circle at 50% 50%, rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.3) 40%, rgba(0, 0, 0, 0.4) 10%); -webkit-transform: rotateX(90deg) translateZ(-150px); -moz-transform: rotateX(90deg) translateZ(-150px); -ms-transform: rotateX(90deg) translateZ(-150px); -o-transform: rotateX(90deg) translateZ(-150px); transform: rotateX(90deg) translateZ(-150px); z-index: -1; } .stage { position: relative; } .button-list{ position: absolute; top: 100px; } </style> </head> <body> <div class = "stage"> <div class = "bubble"> <div class = "shadow"></div> </div> <div class = "bubble"> <div class = "shadow"></div> </div> <div class = "bubble"> <div class = "shadow"></div> </div> <div class = "bubble"> <div class = "shadow"></div> </div> <div class = "bubble"> <div class = "shadow"></div> </div> <div class = "bubble"> <div class = "shadow"></div> </div> <div class = "bubble"> <div class = "shadow"></div> </div> <div class = "bubble"> <div class = "shadow"></div> </div> </div> <div class = "button-list"> <button id = "bubbleStart" class = "button">冒泡排序</button> <button id = "insertStart" class = "button">直接插入排序</button> <button id = "shellStart" class = "button">希尔排序</button> <button id = "selectStart" class = "button">选择排序</button> <button id = "heapStart" class = "button">堆排序</button> <button id = "quickStart" class = "button">快速排序</button> <button id = "reset" class = "button">重置</button> </div> <script> /******************排序算法*******************************/ /** 生成record @param arr 待排序数组 */ function createRecord(arr){ let record = [], len = arr.length; for(let i=0; i< len; i++){ record[i] = { "data" : arr[i], "index" : i, }; } return record; } /** 冒泡排序 @param arr 待排序数组 @return states */ const bubbleSort = function(arr){ let exchange = arr.length; let states = [], count=0, record=createRecord(arr); exchange--; while(exchange != 0){ bound = exchange; exchange = 0; for(let j=0; j<bound; j++){ if(record[j].data > record[j+1].data){ let temp = record[j]; record[j] = record[j+1]; record[j+1] = temp; exchange = j; //记录原来位置,用来操作dom states[count] = {"first":record[j].index,"second":record[j+1].index,}; count++; } } } return states; } /** 插入排序 @param arr 待排序数组 @return states */ function insertSort(arr){ let states = [], count = 0, record = createRecord(arr); for(let i = 1,len=record.length; i<len; i++){ let temp = record[i]; //保存待插入的数 let j = 0; //在有序区中的移动,如果temp较小,就往前移动 for(j=i-1; record[j]!=undefined && temp.data<record[j].data; j--){ record[j+1] = record[j] //有序区后移,给temp留插入的空间 //是temp和前面的交换,不能是记录j+1的,因为下一趟J+1就变成之前后移的元素了 states[count] = {"first":record[j].index,"second":temp.index,}; count++; } record[j+1] = temp; //插入temp } return states; } /** 希尔排序 @param arr */ function shellSort(arr){ let states = [], count = 0, record = createRecord(arr); console.log(record); //选择d为len/2 let len = record.length-1; //最后的下标 //增量缩小 直到小于1跳出 for(let d = record.length/2; d>=1; d=parseInt(d/2)){ //划分组数的循环 组数为d for(let i=0; i<d; i++){ //每一个组都是[i+1,len-d+1] --> 对每一个组直接排序 for(let j=i+1; j<=(len-d+1); j++){ let temp = record[j]; let k = 0; for(k = j-1; record[k] != undefined && temp.data < record[k].data; k--){ console.log(1); record[k+1] = record[k]; states[count] = {"first":record[k].index,"second":temp.index,}; count++; } record[k+1]=temp; } } } return states; } /** 选择排序 @param arr */ function selectSort(arr){ let states = [], count = 0, record = createRecord(arr); for(let i=0,len=record.length; i<len; i++){ index = i; for(let j=i+1; j<len; j++){ if(record[j].data<record[index].data) index = j; } //等于就不用交换 if(index != i){ let temp = record[i]; record[i] = record[index]; record[index] = temp; states[count] = {"first":record[i].index,"second":record[index].index,}; count++; } } return states; } /** 堆排序 @param arr */ function heapSort(arr){ let states = [], count = 0, record = createRecord(arr); //筛选法调整堆的算法 /** 从顶向下调整子树 @param i 需要调整的子树的根结点 @param m 子结点的叶子 : 也是无序区的最后一个 */ function sift(i,m){ console.log(record); let left = 2*i+1; //指向左孩子 let right = left + 1; //指向右孩子 let maxChild = left; if(left > m) return; //如果左结点越界 //如果右孩子不越界 if(right <= m && record[left].data<record[right].data){ maxChild = right; } //如果根结点大于左右孩子 if(record[i].data<record[maxChild].data){ let temp = record[i]; record[i] = record[maxChild]; record[maxChild] = temp; states[count] = {"first":record[i].index,"second":temp.index,}; count++; sift(maxChild,m); //递归遍历子树 } } //排序算法 function sort(){ let len = arr.length - 1; let beginIndex = (len-1) / 2; //初始建堆 for(let i = beginIndex; i>=0; i--){ sift(i,len); } //不断移走堆顶和重复建堆 for(let i=len; i>0; i--){ let temp = record[0]; record[0] = record[i]; record[i] = temp; states[count] = {"first":record[0].index,"second":temp.index,}; count++; sift(0,i-1); } } sort(); return states; } /** 快速排序 @param arr */ function quickSort(arr){ let states = [], count = 0, record = createRecord(arr); //一次划分 function partition(first,end){ let i = first, j = end; while(i < j){ while(i<j && record[i].data<=record[j].data) j--; //右侧扫描 if(i<j){ let temp = record[i]; record[i] = record[j]; record[j] = temp; states[count] = {"first":record[i].index,"second":record[j].index,}; i++; count++; } while(i<j && record[i].data<=record[j].data) i++; //左侧扫描 if(i<j){ let temp = record[i]; record[i] = record[j]; record[j] = temp; states[count] = {"first":record[i].index,"second":record[j].index,}; j--; count++; } } return i; } function sort(first,end){ if(first<end){ let pivot = partition(first,end); sort(first,pivot-1); sort(pivot+1,end); } } sort(0,record.length-1); return states; } /***************************************************************/ /** 设置演示用的dom的样式 @param doms dom对象 @param arr 待排序数组 */ function domStyle(doms,arr){ const MAX = 255; const MIN = 0; for(let i = 0,len = doms.length; i < len; i++){ let r = Math.floor(Math.random()*(MAX-MIN+1) + MIN); let g = Math.floor(Math.random()*(MAX-MIN+1) + MIN); let b = Math.floor(Math.random()*(MAX-MIN+1) + MIN); doms[i].innerText = arr[i]; let startColor = "rgba(" + 255 + "," + 255 + "," + 255 + "," + 0 + ")"; let endColor = "rgba(" + r + "," + g + "," + b + "," + 1 + ")"; doms[i].style.background = "radial-gradient(circle at 50% 50%,"+startColor+","+ endColor+")"; reset(doms); } } /** 重置 @param doms */ function reset(doms){ let w = doms[0].offsetWidth; for(let i=0,len=doms.length; i<len; i++){ doms[i].style.left = w * i + "px"; } } /** 生成完全二叉树样式 @param doms 球体数组 */ function createTree(doms){ } /** 演示 @param doms 演示用的dom对象 @param arr states数组对象 @param btn 对应按钮 */ function domOpera(doms,arr,btn){ let i = 0,len = arr.length; btn.setAttribute("disabled","disabled"); console.log(arr); /* for(let i = 0; i<arr.length; i++){ timeout(i); }*/ function timeout(){ if(timer){ clearTimeout(timer); } timer = setTimeout(function(){ let tempLeft = doms[arr[i].first].style.left; //doms[arr[i].first].style.transform = "translateX(" + doms[arr[i].second].style.left + ")"; doms[arr[i].first].style.left = doms[arr[i].second].style.left; doms[arr[i].second].style.left = tempLeft; i++; if(i<len) timeout(); else{ timer = setTimeout(function(){ reset(doms); btn.removeAttribute("disabled"); },2000); } },1000); } timeout(); } let timer = null; window.onload = function(){ let arr = [50,13,12,56,87,58,45,90]; //待排序数组 let doms = document.getElementsByClassName("bubble"); //doms对象 let btns = document.getElementsByClassName("button"); //button domStyle(doms,arr); //显示球体样式 //绑定事件 btns[0].onclick = function(){ domOpera(doms,bubbleSort(arr),this); } btns[1].onclick = function(){ domOpera(doms,insertSort(arr),this); } btns[2].onclick = function(){ domOpera(doms,shellSort(arr),this); } btns[3].onclick = function(){ domOpera(doms,selectSort(arr),this); } btns[4].onclick = function(){ createTree(doms);//生成完成二叉树样式 domOpera(doms,heapSort(arr),this); } btns[5].onclick = function(){ domOpera(doms,quickSort(arr),this); } //重置 btns[6].onclick = function(){ clearTimeout(timer); for(let i=0; i<9; i++){ if(btns[i].hasAttribute("disabled")){ btns[i].removeAttribute("disabled"); break; } } reset(doms); } } </script> </body> </html>
效果: