动画演示排序算法

刚开始的思路是用callback返回每一次交换时的元素下标,然后改变left,和top来实现动画演示效果。但是在for循环中setTimeout是不会阻塞代码的执行的,所以得到的是遍历完了的结果。最后,用缓存每一次交换的两个元素对应的dom的下标的方式保存交换的顺序,再用setTimeout显示出来。为了保存数组对应的dom,用

{                                                   {

  data: ,index:,   的方式,而用       first:,second:,   来存放每次交换的两个元素对应的dom的下标。

}                                                    }

希望实现堆排序的完全二叉树展示,未成功,待完成。。。

<!doctype html>
<html>
<head>
	<style>
		.bubble {
			 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>

  

				

  效果:

     

原文地址:https://www.cnblogs.com/githubMYL/p/8909700.html