十大排序算法javascript版自我总结

算法尽显编程之美

ps:以下排序都默认进行升序排列

一,冒泡排序

算法描述每次对两个元素进行比较,每遍历一次都能找到一个最小值,直到没有需要交换的,也就是说排序完成。
描述

  1. 比较相邻的两个元素,如果第一个元素大于第二个元素,则交换他们的位置。
  2. 对每个相邻的元素进行相同的工作,直到结尾的最后一对元素。这样最后的元素是最大的数。
  3. 针对所有元素重复上述操作,除了最后一个(不成对了)。
  4. 持续每次对越来越少的元素重复上述操作,直到没有任何一对数字需要比较。

实现代码

	console.time('冒泡执行时间')
	function bubbleSort(arr){
		const len = arr.length
		for(let i=0; i < len - 1; i++){
			for(let j=0; j < len - i - 1; j++){
				// len - i 每次遍历一次后都会排出一个最大值,因此不需要对后面的有序数据在进行比较
				if(arr[j] > arr[j+1])
					// 解构赋值, 进行快速切换值,不需要额外的创建变量
					[arr[j], arr[j+1]] = [arr[j+1], arr[j]]
			}
		}
		return arr
	}
	let data = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48, 3, 44, 38, 5, 47, 15, 36, 26, 27]
	console.log(bubbleSort(data))
	console.timeEnd('冒泡执行时间')
	// [2, 3, 3, 4, 5, 5, 15, 15, 19, 26, 26, 27, 27, 36, 36, 38, 38, 44, 44, 46, 47, 47, 48, 50]

改进冒泡排序

  • 每次循环,分别从两端同时进行排序(正反冒泡),以减少排序次数。
	function bubbleSort(arr){
		// 初始化两端
		let start = 0, end = arr.length
		while(start < end){ // 如果头 大于 尾,则排序完成
			// 正向冒泡
			for(let i=start; i < end; i++){
				if(arr[i] > arr[i+1]){
					[arr[i], arr[i+1]] = [arr[i+1], arr[i]]
				}
			}
			// 反向冒泡
			for(let i=end; i > start; i--){
				if(arr[i] < arr[i-1]){
					[arr[i], arr[i-1]] = [arr[i-1], arr[i]]
				}
			}
			start++
			end--
		}
		return arr
	}
	let data = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48, 3, 44, 38, 5, 47, 15, 36, 26, 27]
	console.log(bubbleSort(data))
	// [2, 3, 3, 4, 5, 5, 15, 15, 19, 26, 26, 27, 27, 36, 36, 38, 38, 44, 44, 46, 47, 47, 48, 50]
图示

冒泡排序


二,选择排序

算法描述:在未排序序列中找最小元素,放在排序序列起始位置,然后,再从剩余未排序序列中继续寻找最小元素,放在已排序序列末尾,直到未排序序列没有元素,也就是说排序完成。
描述

  1. 首先在未排序序列找到最小元素,放在排序序列的起始位置。
  2. 再从剩余未排序序列中继续找到最小元素,然后放在已排序序列的末尾。
  3. 重复以上操作,直到未排序序列没有元素。

实现代码

	console.time('选择排序')
	function selectSrot(arr){
		// 初始化最小元素的索引
		let minIndex, len = arr.length
		for(let i=0; i < len - 1 ; i++) {
			// 默认最小元素索引
			minIndex = i
			for(let j=i+1; j < len; j++){// i+1 每次遍历,已排序序列都会加一个元素
				if(arr[j] < arr [minIndex]) // 对默认最小元素 与 当前元素进行比较
					// 如果当前元素 小于 默认最小元素 那么把当前元素的索引 设置成最小元素索引 
					minIndex = j 
			}
			// 遍历完成后,找出最小元素的索引,把最小元素放到已排序序列中
			[arr[i], arr[minIndex]] = [arr[minIndex], arr[i]]
		}
	}
	let arr = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48, 3, 44, 38, 5, 47, 15]
	console.log(selectSrot(arr))
	console.timeEnd('选择排序')
图示

选择排序


三, 归并排序

算法描述:采用分治法,对传入待排序序列进行递归拆分,当拆分到每个子序列有序(每个子序列只剩一个元素)时,进行回归,使子序列段间有序,在使两个有序表合成一个有序表。
描述

  1. 把长度为n待排序序列,拆分成两个长度为n/2子序列
  2. 对两个子序列分别采用归并排序。
  3. 将两个排好的子序列合并成一个最终的有序序列。

实现代码

	console.time('归并排序')
	function mergeSort(arr){
		const len = arr.length
		if(len < 2) // 递归终止条件
			return arr
		let [left, right] = [arr.splice(0, Math.floor(len / 2)), arr]
		return merge(mergeSort(left), mergeSort(right)) // 对待排序序列进行拆分
	}
	
	function merge(left, right){
		let result = []
		while(right.length && left.length){
			let item = left[0] >= right[0] ? right.shift() : left.shift()
			result.push(item)
		}
		while(right.length)
			result.push(right.shift())
		while(left.length)
			result.push(left.shift())
		return result
	}
	
	let arr = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48, 3, 44, 38, 5, 47, 15]
	console.log(mergeSort(arr))
	console.timeEnd('归并排序')
图示

归并排序


四, 快速排序

算法描述:本质上来看,快速排序应该算是在冒泡排序基础上的递归分治法,在待排序序列,选一个元素作为基准,所有小于这个基准的都移到基准左边,反之亦然,对基准左右两边的子集,不断重复相同的动作,直到序列有序。

描述

  1. 待排序序列,选一个元素作为基准
  2. 所有小于这个基准的都移到基准左边,反之亦然
  3. 基准左右两边的子集,不断重复1,2步,直到序列有序。
	console.time('快速排序')
	function fastSort(arr, left = 0, right = arr.length - 1) {
		if(left < right){ // 结束递归条件
			console.log('fastSort',left, right, arr)
			let partitionPos = partition(arr, left, right) // 对序列进行拆分
			console.log(partitionPos)
			// 对拆分后的序列,继续进行相同操作
			fastSort(arr, left, partitionPos-1)
			fastSort(arr, partitionPos+1, right)
		}
		return arr
	}
	
	function partition(arr, left, right){
		const pivot = left
		let	index = pivot + 1
		for(let i=index; i <= right; i++){
			if(arr[i] < arr[pivot]){ // 与基准进行比较
				[arr[i], arr[index]] = [arr[index], arr[i]]
				index++ // 基准偏移量(有多少小于基准的元素移动到基准左侧)
			}
		}
		[arr[pivot], arr[index - 1]] = [arr[index - 1], arr[pivot]]
		
		return index - 1
	}
	let arr = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48, 3, 44, 38, 5, 47, 15]
	console.log(fastSort(arr))
	console.timeEnd('快速排序')

改进快速排序

  • 对传入待排序序列进行递归拆分,并对其进行排序,当拆分到每个子序列有序(每个子序列只剩一个元素)时,进行回归。
	console.time('快速排序')
	function fastSort(arr) {
		const len = arr.length
		if(len < 2) return arr // 递归结束条件(当待排序序列只剩一个元素)
		const index = Math.floor(len / 2) // 获取中心点(基准)
		const pivot = arr.splice(index, 1)[0] // 提取出基准
		let left = [], right = []
		console.log(len)
		for(let i=0; i < len - 1; i++){
			console.log(len)
			// 当前元素与基准进行比较
			arr[i] < pivot ? left.push(arr[i]) : right.push(arr[i])
		}
		return fastSort(left).concat([pivot], fastSort(right))
	}
	let arr = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48, 3, 44, 38, 5, 47, 15]
	console.log(fastSort(arr))
	console.timeEnd('快速排序')
图示

快速排序


五, 插入排序

算法描述:通过构建有序序列,对未排序序列,在已排序序列从后向前扫描找到位置并插入。从后向前扫描过程中,需要反复把已排序元素逐步向后挪移,为最新元素提供插入空间。
描述

  1. 从第一个元素开始(该元素可认为已被排序)。
  2. 取出下一个元素,在已排序序列中从后向前扫描。
  3. 如果已排序元素大于新元素,将新元素移到一下位置。
  4. 重复步骤3,直到找到已排序元素小于或等于新元素的位置。
  5. 将新元素插入到该位置。
  6. 重复2~5,直到序列有序。

实现代码

	console.time('插入排序')
	function insertSort(arr){
		const len = arr.length
		for(let i=1; i < len; i++){ // 第一个元素,默认已被排序
			let tmp = arr[i] // 获取新元素
			for(let j=i; j >= 0; j--){ // 从后向前扫描
				if(tmp < arr[j-1]){ // 当前元素 与 新元素进行比较
					// 新元素小于当前元素,把当前元素向后挪移,为新元素提供插入空间
					arr[j] = arr[j-1] 
				}else{
					arr[j] = tmp // 新元素大于当前元素,将新元素移到一下位置,然后插入
					break
				}
			}
		}
		return arr
	}
	let arr = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48, 3, 44, 38, 5, 47, 15]
	console.log(insertSort(arr))
	console.timeEnd('插入排序')

改进插入排序

  • 查找插入位置时使用二分查找的方式。
	console.time('插入排序')
	function insertSort(arr){
		const len = arr.length
		for(let i=1; i < len; i++){
			let tmp = arr[i], left = 0, right = i-1 // 初始化左右两端位置及新元素
			while(left <= right){ // 结束条件,不能再拆分时
				const middle = Math.floor((left+right) / 2)
				tmp < arr[middle] ? right = middle - 1 : left = middle + 1 // 新元素 与 当前元素比较;(小于:0~middle-1,大于:middle+1~right)
			}
			for(let j=i; j >= left; j--){
				arr[j] = arr[j-1] // 新元素小于当前元素,把当前元素向后挪移,为新元素提供插入空间
			}
			arr[left] = tmp
		}
		return arr
	}
	let arr = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48, 3, 44, 38, 5, 47, 15]
	console.log(insertSort(arr))
	console.timeEnd('插入排序')
图示

插入排序


六,希尔排序

算法描述:将待排序序列,分成若干子序列并进行直接插入排序,待整个序列基本有序,在对序列进行直接插入排序
描述

  1. 选择一个增量,按增量序列个数k,进行k躺排序。
  2. 每趟排序对应一个增量,分别对按增量分成的若干子序列进行直接插入排序。
  3. 当增量为1时,对序列进行插入排序,使序列有序。

实现代码

	console.time('希尔排序')
   	function shellSort(arr, spacing = 3){ // spacing 序列间隔 spacing >= 2
   		const len = arr.length
   		let gap = 1 // 增量因子
   		while(gap < len / spacing){ // 动态生成增量
   			gap *= spacing + 1
   		}
   		for(; gap > 0; gap = Math.floor(gap / spacing)){ // gap 增量因子,由大到小
   			console.log(gap)
   			for(let i=0; i < len; i++){
   				let tmp = arr[i], j = i - gap // j:下一个元素的位置
   				for(; j >= 0 && tmp < arr[j]; j -= gap){ // j:为正数,tmp:新元素,arr[j]:当前元素
   					arr[j + gap] = arr[j]
   				}
   				arr[j + gap] = tmp
   			}
   		}
   		return arr
   	}
   	let arr = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48, 3, 44, 38, 5, 47, 15]
   	console.log(shellSort(arr))
   	console.timeEnd('希尔排序')
图示

希尔排序


七,堆排序

算法描述: 堆排序是指利用这种数据结构所设计的一种算法,堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。堆排序可以说是一种利用堆的概念来排序的选择排序
描述

  1. 创建一个堆。
  2. 把堆首与堆尾互换(arr[0] 与 arr[arr.length - 1]的值互换)。
  3. 堆尺寸缩小1。
  4. 重复2步,直到堆尺寸为1。

代码实现

	console.time('堆排序')
	function heapSort(arr){
		let heapLen = arr.length // 获取堆的大小
		// 初始化堆	堆->顶:i,左:2i+1,右:2i+2
		for(let i=Math.floor(heapLen / 2) - 1; i>=0; i--){
			heapify(arr, i, heapLen)
		}
		console.log('初始化堆',JSON.parse(JSON.stringify(arr)))
		// 现在要把最大的放到最后一个,最后一个放到第一个。
		//把堆的大小减少一个,在慢慢的把最大的循环上去。
		for(let j=heapLen - 1; j>=1; j--){
			[arr[0], arr[j]] = [arr[j], arr[0]]
			heapify(arr, 0, --heapLen)
			console.log('每次迭代堆',JSON.parse(JSON.stringify(arr)))
		}
		return arr
	}
	
	function heapify(arr, x, len){
		// 堆->顶:x,左:2x+1,右:2x+2
		let left = 2*x + 1, right = 2*x + 2, largest = x
		// 比较 左 与 父节点(顶)
		if(left < len && arr[left] > arr[largest]){
			// 把左替换成父节点(顶)
			largest = left
		}
		// 比较 右 与 父节点(顶)
		if(right < len && arr[right] > arr[largest]){
			// 把右替换成父节点(顶)
			largest = right
		}
		if(largest !== x){ // 如果父节点(顶)改变,那么堆性质可以发生改变,需要维护堆性质
			// 如果最大者不是父节点 则交换位置
			[arr[x], arr[largest]] = [arr[largest], arr[x]]
			heapify(arr, largest, len)
		}
	}
	let arr = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48, 3, 44, 38, 5, 47, 15]
	console.log(heapSort(arr))
	console.timeEnd('堆排序')
图示

堆排序
堆排序示意图
堆排序


八,计数排序

算法描述待排序序列,有确定的范围,计数排序使用一个额外的数组C,其中第i个元素是待排序序列中等于i的元素的个数。然后根据数组C来将待排序序列中的元素排到正确的位置。它只能对整数进行排序。

描述

  1. 在待排序序列中找出,最小和最大元素。
  2. 统计待排序序列每个值为i元素出现的次数,存入数组C的第i项。
  3. 反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1。

实现代码

	console.time('计数排序')
	function countSort(arr, maxValue){
		// 根据待排序序列的最值,初始化计数数组,并都用0填充
		let newArr = new Array(maxValue + 1).fill(0)
		const len = arr.length
		let sortIndex = 0 // 排序位置
		// 根据待排序序列的元素,向计数数组进行累计
		for(let i=0; i < len; i++){
			newArr[arr[i]]++
		}
		const newLen = newArr.length
		for(let j=0; j < newLen; j++){
			// 进行方向填充
			while(newArr[j] > 0){
				arr[sortIndex++] = j
				newArr[j]--
			}
		}
		return arr
	}
	let arr = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48, 3, 44, 38, 5, 47, 15]
	console.log(countSort(arr, 50))
	console.timeEnd('计数排序')

改进计数排序

  • 找到待排序序列的最大,最小值,根据最大 - 最小的范围创建计数数组。
	console.time('计数排序')
	function countSort(arr){
		const len = arr.length
		let sortIndex = 0 // 排序位置
		let min = max = arr[0] // 初始化最大,最小值
		// 获取待排序序列的最值
		for(let k=0; k < len; k++){
			min = min < arr[k] ? min : arr[k]
			max = max > arr[k] ? max : arr[k]
		}
		// 根据最值创建计数数组,并用0填充
		let newArr = new Array((max - min) + 1).fill(0)
		// 根据待排序序列的元素,向计数数组进行累计
		for(let i=0; i < len; i++){
			newArr[arr[i] - min]++
		}
		const newLen = newArr.length
		for(let j=0; j < newLen; j++){
			// 进行方向填充
			while(newArr[j] > 0){
				arr[sortIndex++] = j + min
				newArr[j]--
			}
		}
		return arr
	}
	let arr = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48, 3, 44, 38, 5, 47, 15]
	console.log(countSort(arr))
	console.timeEnd('计数排序')
图示

计数排序示意图
计数排序


九,桶排序

算法描述:将数据分布到有限数量的桶里,然后对每个桶在分别进行排序,最后把桶的数据拼接起来
描述

  1. 根据数据长度,设置一个桶大小,并计算出需要的桶数量。
  2. 遍历数据,把数据一个一个放进对应的桶里。
  3. 对每个桶大小 > 1 的进行排序。
  4. 最后把桶的数据拼接起来。

实现代码

	console.time('桶排序')
	function bucketSort(arr, bucketSize){
		const len = arr.length
		if(len <= 1)
			return arr
		// 验证输入是否符合要求
		let regex = /^[1-9]+[1-9]*$/ 
		let min = max = arr[0] // 初始化最大,最小值
		let sortIndex = 0 // 排序位置
		// 	// 获取待排序序列的最值
		for(let k=0; k < len; k++){
			min = min < arr[k] ? min : arr[k]
			max = max > arr[k] ? max : arr[k]
		}
		// 设置默认桶大小
		bucketSize = bucketSize > 1 && regex.test(bucketSize) ? bucketSize : 5
		// 动态设置桶数量
		let bucketCount = Math.floor((max - min) / bucketSize) + 1
		// 初始化桶
		let bucketS = new Array(bucketCount).fill(1).map(() => [])
		// 向桶中插入数据
		for(let i=0; i < len; i++){
			bucketS[Math.floor((arr[i] - min) / bucketSize)].push(arr[i])
		}
		// 重置arr
		arr.length = 0
		for (i = 0; i < bucketS.length; i++) {
			let bucketSItem = bucketS[i], bucketSLen = bucketSItem.length
			if(bucketSLen > 0){
				arr = arr.concat(shellSort(bucketSItem)) // 对每个桶进行排序,并进行拼接
			}
		}
		return arr
	}
	let arr = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48, 3, 44, 38, 5, 47, 15]
	console.log(bucketSort(arr))
	console.timeEnd('桶排序')
图示

桶排序


十,基数排序

算法描述:其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较,基数排序时按照从低位到高位依次排序,直到最高位。
描述

  1. 确定最大位数。
  2. 基于桶思想,根据基数0-9,创建十个桶,然后从低位到高位依次循环遍历数据,把数据一个一个放进对应的桶里。
  3. 最后把桶的数据拼接起来。
  4. 重复2,3步,直到最高位

实现代码

	console.time('基数排序')
	function radixSort(arr, maxDigit){ // maxDigit:最大位数
		let mod = 10, dev = 1, len = arr.length // mod:取模 dev:除数
		for(let i=0; i < maxDigit; i++, mod *= 10, dev *= 10){
			// 基于桶思想,根据基数0-9,创建十个桶
			let bucketS = new Array(10).fill(1).map(()=>[])
			for(let j=0; j < len; j++){
				// 向桶中插入数据
				// (arr[j] % mod) / dev
				// 当maxDigit位:1
				// (26 % 10) / 1 => 6 / 1 => 6
				// 当maxDigit位:2
				// (26 % 100) / 10 => 26 / 10 => 2.6
				bucketS[Math.floor((arr[j] % mod) / dev)].push(arr[j])
			}
			console.log(JSON.parse(JSON.stringify(bucketS)))
			let bucketSLen = bucketS.length
			arr.length = 0
			for(let j=0; j < bucketSLen; j++){
				arr = arr.concat(bucketS[j])
			}
		}
		return arr
	}
	let arr = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48, 3, 44, 38, 5, 47, 15]
	console.log(radixSort(arr, 2))
	console.timeEnd('基数排序')
图示

基数排序
基数排序示意图
基数排序

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值