js实现:复杂度为O(n^2)的排序算法(冒泡排序、简单选择排序、直接插入排序)

参考博客:
1、各种排序算法总结(全面)
2、[算法总结] 十大排序算法

(假设以下算法的排序方向均是:从小到大。数组元素的个数为:n)
在这里插入图片描述

一、冒泡排序

1、原理:

——待排元素个数为n
(1)先把n个中,最大的元素排到最后,待排元素个数为n-1。【一个元素的位置是确定的】
(2)在把n-1个中,第二大的元素排到待排元素的最后,待排元素个数为n-2。【确定了两个元素的位置】
……
(n-1)重复以上步骤n-1趟,直到待排元素的个数为1。【确定了n-1个元素的位置,剩下那个元素就不用确定了吧?省了一趟循环】

举例:arr=[2,6,4,10,3]
第(1)趟:2比6小,不交换;6比4大,交换;6比10小,不交换;10比3大,交换
此时数组变成:[2,4,6,3,10],10的位置是确定的了!
第(2)趟:2比4小,不交换;4比6小,不交换;6比3大,交换;
此时数组变成:[2,4,3,6,10],6的位置是确定的了!
第(3)趟:2比4小,不交换;4比3大,交换;
此时数组变成:[2,3,4,6,10],4的位置是确定的了!
第(4)趟:2比3小,不交换
此时数组变成:[2,3,4,6,10],3的位置是确定的了!
2的位置自然也确定了!

2、代码:

将待排元素中最大值放到最后,其实就是相邻元素两两进行比较,如果arr[j]>arr[j+1],交换它们的位置。直到j+1==arr.length-1,一趟循环结束

<script>
	let arr = [2,6,4,10,3];
	console.log(bubbleSort(arr));
	
	/**
	 * @param {array} arr
	 */
	function bubbleSort(arr){
    
    
		// 要确定原数组中,哪一个元素的位置
		for(let i=arr.length-1; i>0; i--){
    
    
			// 待排元素的范围:为什么 j 要小于i,为了保证 j+1 不越界
			for(let j=0; j<i; j++){
    
    
				if(arr[j]>arr[j+1]) swap(arr, j, j+1);
			}
		}
		return arr;
	}
	
	/**
	 * @param {array} arr
	 * @param {int} i
	 * @param {int} j
	 */
	function swap(arr, i, j){
    
    
		let temp = arr[i];
		arr[i] = arr[j];
		arr[j] = temp;
	}
</script>

元素之间位置交换,在排序算法中,是很常用的。所以,这里把它单独写成一个swap函数,增加代码的复用性。

3、优化

如果把数组换成一个相对有序的数组,比如:[2,10,3,4,5,6],是不是还要遍历这么多趟?
很显然,这个数组只需要一趟遍历,就已经是有序的了。
【思考】:可不可以增加一个标志位change,当数组已经有序时,结束循环。

<script>
	function bubbleSort(arr){
    
    
		for(let i=arr.length-1; i>0; i--){
    
    
			let change = false; // 标志位
			for(let j=0; j<i; j++){
    
    
				if(arr[j]>arr[j+1]){
    
    
					swap(arr, j, j+1);
					change = true; // 修改标志位
				}
			}
			if(change == false) break; // 标志位。结束循环
		}
		return arr;
	}
</script>

[2,10,3,4,5,6]
第一趟循环结束后,数组已经有序;
第二趟循环结束后,并不会修改标志位;所以会结束循环,
不会执行第三趟。

二、简单选择排序

1、原理

跟冒泡排序其实很相似,也是两两之间比较,但是此时并不会交换,而是记录下来;
当遍历完整个未排序序列后,找到未排序序列中的最值才进行交换。
【可以认为选择排序是冒泡排序的一种改进】

假设:数组的前边是已排序序列,后边是未排序序列。
因为是从小到大排序,所以我们从后边的序列中,选出min值,排到已排序序列的末尾(即:跟已排序序列的后一位元素进行交换)
比如:arr=[4,3,2,5,6]
第(1)趟:已排序序列为空,[],未排序序列为:[4,3,2,5,6],选出min值:2,跟已排序序列的后一位元素,即,跟4交换。[2,3,4,5,6]
第(2)趟:已排序序列为空,[2],未排序序列为:[3,4,5,6],选出min值:3,跟已排序序列的后一位元素,即,跟3交换。[2,3,4,5,6]
第(3)趟:已排序序列为空,[2,3],未排序序列为:[4,5,6],选出min值:4,跟已排序序列的后一位元素,即,跟4交换。[2,3,4,5,6]
第(4)趟:已排序序列为空,[2,3,4],未排序序列为:[5,6],选出min值:5,跟已排序序列的后一位元素,即,跟5交换。[2,3,4,5,6]
第(5)趟:已排序序列为空,[2,3,4,5],未排序序列为:[6],选出min值:6,跟已排序序列的后一位元素,即,跟6交换。[2,3,4,5,6]

即使整个数组已经有序,但是只要未排序序列不为空,就仍然会寻找min值。
如果说比冒泡排序改进了什么的话,应该是减少交换次数?

2、代码

function selectSort(arr){
    
    
	// 已排序序列的右区间+1。
	// i是从0开始,已排序右区间最开始应该理解成-1?
	for(let i in arr){
    
    
		// 假设未排序序列的第一个元素为min
		let min_index = i;
		// 未排序序列的左区间
		for(let j=i; j<arr.length; j++){
    
    
			min_index = arr[j] < arr[min_index] ? j : min_index;
		}
		swap(arr, i, min_index);
	}
	return arr;
}

三、插入排序

1、原理

跟选择排序也很相似,将数组分为已排序序列、未排序序列两部分。不过,这里默认第一个元素是已排序,而前面的选择排序是默认已排序序列为空(因为在选择排序中,已排序序列中的位置,一旦确定就不改变)

1、取出未排序序列的第一个元素key,在已排序序列中,从后往前扫描,一旦遇到比key大的元素arr[j],就往后挪一位(因为key比arr[j]小,肯定在前面,要给key腾出一个位置)。
2、直到找到一个元素**arr[j]**比key小,那key就放在arr[j]的后面。arr[j+1]=key
3、如果遍历完已排序序列都没有找到一个比key小的值,说明key要放在最前面

2、代码

function insertSort(arr){
    
    
	// 未排序序列
	for(let i=1; i<arr.length; i++){
    
    
		// 记录下要排序的元素
		let key = arr[i];
		// 已排序序列
		for(var j=i-1; j>=0 && key < arr[i]; j--){
    
    
			arr[j+1] = arr[j];
		}
		// 跳出循环的条件:j<0 || 遇到比key小的值
		arr[j+1] = key;		
}

猜你喜欢

转载自blog.csdn.net/qq_38432089/article/details/124220384
今日推荐