刚开始的思路是用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>
效果: