经典排序

最终实现的效果如下:代码在后面贴出,就只有一个html,直接在浏览器运行

 

 代码如下:

<!DOCTYPE html>
<html>
	<head>
		<title>经典排序算法</title>
		<meta charset="utf-8">
		<style>
			.show {
				display: flex;
				flex-direction: row;
				align-items: flex-end;
				justify-content: space-around;
				width: 100%;
				box-sizing: border-box;
				padding: 100px 200px 0;
			}
		</style>
	</head>
	<body>

		排序方法(填入下面列表中词语):<input type="text" class='sortName'> <br />
		<ul>
			<li>bubble</li>
			<li>select</li>
			<li>insert</li>
			<li>quick</li>
		</ul> 
		<button class='btn'>排序</button> 进行排序前F5刷新下- -,主要看个动画效果,将就下

		<div class="show">
			
		</div>

		<script>
			const arr = [2, 33, 17, 25, -1, 0, 2],
				show = document.querySelector('.show'),
				ipt = document.querySelector('.sortName'),
				btn = document.querySelector('.btn')

			btn.addEventListener('click', (e) => {
				const sortName = ipt.value,
					script = document.createElement('script')
				script.innerHTML = `${sortName}Sort(arr)`
				document.body.append(script)
			}, false)
			
			/* 
				由于arr的数据范围差异可能很大 正常操作是用坐标轴单位控制
				这里 我简单得把数据映射到1到n上 保留相对大小关系即可
			*/
			let showArr = arr.slice()
			showArr = showArr.map((item, index) => {	// 保留序号 以便恢复顺序
				return {
					index: index,
					val: item
				}
			})
			showArr.sort((a, b) => a.val - b.val)	// 按值排序
			// 做映射
			let offset = 0
			showArr.reduce((sum, item, index) => {
				const oldVal = item.val
				if (sum === item.val) offset++
				item.val = index - offset + 1
				return oldVal
			})
			showArr[0].val = 1
			showArr.sort((a, b) => a.index - b.index)	// 按序号排序 恢复原来得顺序
			showArr = showArr.map(item => item.val)
			/*
				转换结果:
					[2, 33, 17, 25, -1, 0, 2]
					[3, 6,   4,  5,  1, 2, 3]
			*/
			
			const showItems = [],	// 保留DOM数组方便操作
				frag = document.createDocumentFragment() // 函数片段减少DOM操作
			showArr.map(item => {
				const showItem = document.createElement('div')
				showItem.style.flex = '1'
				showItem.style.margin = '1px'
				showItem.style.height = `${item*50}px`
				showItem.style.background = '#abc'
				showItems.push(showItem)
				frag.appendChild(showItem)
			})
			show.append(frag)

			// 一些工具函数
			
			// 交换showItems中a, b的高度
			function swapDOM(arr, a, b) {
				// 排序速度太快 浏览器把中间过程省略直接渲染了结果 
				// 为了能看清楚过程 对该函数做一个延迟
				
				if (this.n == null) this.n = 1
				else this.n++

				setTimeout(() => {
					if (a !== b) {
						const tmp = arr[a].style.height
						arr[a].style.height = arr[b].style.height
						arr[b].style.height = tmp
					}
				}, this.n * 500)
				
			}
			
			
			// 交换数组a, b的值
			function swap(arr, a, b) {
				if (a !== b) {	// 因为异或的交换方式,如果两数相同,结果会变成0,因此要重新赋值
					arr[a] = arr[a] ^ arr[b]
					arr[b] = arr[a] ^ arr[b]
					arr[a] = arr[a] ^ arr[b]
					swapDOM(showItems, a, b)
				}
			}

			// 查找原数组[a, b]区间子数组最小值在原数组中的索引
			function min(arr, a, b) {
				const minVal = Math.min(...arr.slice(a, b+1))
				return arr.indexOf(minVal, a)
			}

			// 二分查找 不能用于插入排序 二分查找需要数组有序,那我用二分查找去实现插入排序前题是数组有序
			function binarySearch(arr, l, r, target) {
				arr.sort((a, b) => a - b) // 排序
				while (l <= r) {
					console.log(arr.slice(l, r+1))
					const mid = Math.floor((l + r) / 2) // 向下取整
					if (arr[mid] === target) 
						return mid
					else if (arr[mid] < target) 
						l = mid + 1
					else r = mid - 1
				}
				return l	// 不存在,返回target插入后的索引
			}

			/*
				排序一律按从小到大
				主要阐释基本思想,为避免分散注意力,暂不考虑细节优化
				
				不稳定的排序算法:
					选择排序、快速排序、希尔排序、堆排序
				稳定的排序算法:
					冒泡排序、插入排序、归并排序和基数排序

				冒泡排序,选择排序,每一趟都选出一个最值并确定了这个值的位置
				插入排序,是随便选一个元素插入到有序序列中
				注意 上面的三种排序 在过程中都能保证部分有序(感觉还是有用的)

				还有需要注意的是:这些算法 包括快排 都是基于交换的 也就是要依赖上面的swap函数
				数学推理可以证明依赖swap的排序算法性能上限是O(nlogn)
				要超越这个上限,需要考虑基数排序这种不依赖交换的函数

			*/
			
			// 冒泡排序 O(n^2)
			function bubbleSort(arr) {
				const len = arr.length
				for (let i=0; i<len-1; i++) {
					for (let j=1; j<len-i; j++) {
						if (arr[j] < arr[j-1]) swap(arr, j-1, j)
					}
				}
			}
			// bubbleSort(arr)

			// 选择排序 O(n^2)
			function selectSort(arr) {
				const len = arr.length
				for (let i=0; i<len-1; i++) {
					const minIndex = min(arr, i, len-1)	// 选择子数组最小值
					swap(arr, i, minIndex)				// 交换最小值和arr[i]
				}
			}
			// selectSort(arr)

			// 插入排序 O(n^2) 数据量小的时候 非常快
			function insertSort(arr) {
				const len = arr.length
				for (let i=1; i<len; i++) {
					for (let j=i; j>=0; j--) {
						if (arr[j] < arr[j-1]) swap(arr, j-1, j)
					}
				}
			}
			// insertSort(arr)

			// 快速排序 O(nlogn) 性能取决于递归树的深度 中枢选择合理的话就是logn
			// 然后每一层根据中枢调整的时间是n
			function quickSort(arr, start=0, end) {
				if (end == null)	end = arr.length-1
				if (start <= end) {
					// 中枢直接选arr[start]
					const pivot = arr[start]

					// 调整 比中枢小的放左边 比中枢大的放右边
					let l = start,
						r = end
					while(l < r) {
						while(l<r && arr[r]>=pivot) r--	// 从右边找到第一个比中枢小的
						swap(arr, l, r)			// l为中枢 换到r
						while(l<r && arr[l]<=pivot) l++	// 从左边找到第一个比中枢大的
						swap(arr, l, r)			// r为中枢 换到l

					}	// 结束条件一定是l === r

					// 递归
					quickSort(arr, start, l - 1)
					quickSort(arr, l + 1, end)
				}
				
			}
			// quickSort(arr, 0, arr.length-1)
		</script>
	</body>
</html>

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值