常见排序算法整理(javascript 实现)

一、 冒泡排序

排序原理:
  • 重复遍历需要排序的列表
  • 比较相邻两个项的值,当 i-1 位置上的数大于 i 位置上的数时(升序排序),交换两个位置上的数
function bubbleSort(arr) {
	const len = arr.length;
	if(!len || len < 2) return arr;
	for(let i = 0; i < len - 1; i ++){
		for(let j = 0; j < len - i - 1; j++){
			if(arr[j] > arr[j+1]){
				[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]
			}
		}
	}
	return arr;
}

二、选择排序

排序原理:
  • 首先在未排序的列表中找到最小(最大)的元素,将最小(大)元素放在第一个位置,保证 0 ~ 1范围内有序
  • 然后再从剩余的列表(1~n 范围内)中找到最小(大) 的元素放在第二个位置,保证 0~2 范围内有序;
  • 以此类推直到 0~n 范围内的所有元素有序
function selectSort(arr) {
	const len = arr.length;
	if(len < 2) return arr;
	for(let i = 0; i < len - 1; i++) {
		let minIndex = i;
		for(let j = i + 1; j < len; j++){
			if(arr[j]  < arr[minIndex]) {
				minIndex = j;
			} 
		}
		[arr[i], arr[minIndex]] = [arr[minIndex], arr[i]];
	}
	return arr;
}

三、插入排序

排序原理
  • 通过构建有序序列,对于未排序的数据,在已排序序列中从后向前扫描,找到对应的位置并插入
  • 需要反复将已排序的元素向后移动,为新元素提供插入空间
  • 假设第一个位置上的数是有序的,从第二个位置上的数开始,和第一个位置上的数进行比较,当第二个位置上的数小于第一个位置上的数时,将第二个位置上的数插入到第一个数位置的前面
function insertSort(arr) {
	const len = arr.length;
	for(let i = 1; i < len; i++) {
		let cur = arr[i]; // 当前需要插入有序序列中的数
		let j = i - 1; // 有序序列的最后一个位置
		while(j >= 0 && arr[j] > cur) {
			// 从后往前的遍历有序序列,只有有序序列中的元素大于当前元素的,就将当前元素插入到有序序列中
			arr[j+1] = arr[j];
			j--;
		}
		arr[j + 1] = cur;
	}
	return arr;
}

四、 快速排序

快速排序原理:
  • 采用分治的思想
  • 通过一个分割元素(等概率从数组中取一个值作为划分区域的标准),将数组分成两个子数组
  • 左边的元素都小于分割元素
  • 右边的元素都大于分割元素
  • 然后再通过递归对子数组进行快排
function quickSort(arr) {
	const len = arr.length;
	if(len < 2) return arr;
	let mid = Math.floor(len / 2);
	let midVal = arr[mid];
	let left = [];
	let right = [];
	for(let i = 0; i < len; i++) {
		if(arr[i] < midVal) {
			left.push(arr[i])
		} else if(arr[i] < midVal) {
			right.push(arr[i])
		}
	}
	return quickSort(left).concat([midVal], quickSort(right));
}

五、归并排序

归并排序原理
  • 采用分治的思想
  • 将一个数组划分为两个小数组,然后递归地对小数组进行排序
  • 最后将已排序的数组合并成一个已排序的数组
function mergeSort(arr) {
	const len = arr.length;
	if(len < 2) return arr;
	// 分割数组
	const mid = Math.floor(len / 2);
	const left = arr.slice(0, mid);
	const right = arr.slice(mid);
	// 对小的数组进行排序
	const sortedLeft = mergeSort(left);
	const sortedRight = mergeSort(right);
	// 合并有序数组
	return merge(sortedLeft, sortedRight);
}

function merge(left, right) {
	const result = [];
	// 左侧数组指针
	let leftIndex = 0;
	// 右侧数组指针
	let rightIndex = 0;
	// 比较两个部分的元素,将较小的一个元素放入result 中
	while(leftIndex < left.length && rightIndex < right.length) {
		if(left[leftIndex] < right[rightIndex]) {
			result.push(left[leftIndex]);
			leftIndex++;
		} else {
			result.push(right[rightIndex]);
			rightIndex++;
		}
	}

	// 将剩余元素添加到result 中
	while(leftIndex < left.length) {
		result.push(left[leftIndex]);
		leftIndex++}
	
	while(rightIndex < right.length) {
		result.push(right[rightIndex]);
		rightIndex++;
	}
	return result;
}

六、堆排序

堆排序原理:
  • 利用堆(堆是一个近似完全二叉树的结构)数据结构设计的排序算法
  • 主要思想是将待排序的序列构造成一个大顶堆或者是小顶堆的结构,然后将堆顶元素与堆底元素交换
  • 将堆的大小建议,重新调整堆
  • 然后反复进行,直到堆的大小为1,排序完成
// 构建大根堆
function heapify(arr, n, i){
	// 初始化最大元素为根节点
	let largest = i;
	// 左子节点
	let left = 2 * i + 1;
	// 右子节点
	let right = 2 * i + 2;

	// 如果左子节点大于根节点,更新最大元素
	if(left < n && arr[left] > arr[lagest]) {
		largest = left;
	}

	// 如果右子节点大于最大元素,更新最大元素
	if(right < n && arr[right] > arr[largest]) {
		largest = right;
	}

	// 如果最大元素不是根节点,交换根节点和最大元素
	if(largest !== i) {
		swap(arr, i, largest);
		// 递归调整被交换的子树
		heapify(arr, n, largest);
	}
}

function swap(arr, i, j) {
	let tmp = arr[i];
	arr[i]= arr[j];
	arr[j] = tmp;
}

function heapSort(arr) {
	const len = arr.length;
	if(len < 2) return arr;
	// 构建大根堆
	for(let i = len/2 - 1; i >= 0; i--) {
		heapify(arr, len, i);
	}

	// 从堆中取出元素,排序完成
	for(let i = len - 1; i >= 0; i--) {
		swap(arr, 0, i);
		heapify(arr, i, 0);
	}
}

七、几种排序算法 时间复杂度、空间复杂度、稳定性对比

算法时间复杂度空间复杂度稳定性
冒泡排序O(n^2)O(1)
选择排序O(n^2)O(1)
插入排序O(n^2)O(1)
快速排序O(n*logn)O(logn)
归并排序O(n*logn)O(n)
堆排序O(n*logn)O(1)
  • 基于比较的排序,时间复杂度在 O(n*logn) 以下是不存在的
  • 基于比较的排序,在时间复杂度在 O(n*logn), 空间复杂度在 O(n) 以下,并且能保持稳定性的算法是不存在
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值