栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

排序算法 快排-冒泡-归并排序

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

排序算法 快排-冒泡-归并排序

文章目录
  • 排序算法
    • 交换算法
      • 冒泡排序 - 交换算法 适合本身有序
        • 算法分析
      • 快速排序 - 交换算法 不适合本身有序
        • 基本快排
        • 优化:随机基准值的快排
        • 算法分析
    • 归并排序 与初始状态无关
      • 算法分析

排序算法 交换算法

基本思想:两两比较,如果发生逆序则交换,直到所有记录都排好序位置

冒泡排序 - 交换算法 适合本身有序

基于简单交换思想
基本思想:每趟不断将记录两两比较,并按"前小后大"规则交换。
每一躺确定一个最大的元素,所以n个元素需要n-1躺。
每一趟比较的次数 = n - 趟数。
如果某一趟比较时不出现记录交换,说明已经排好序了,可以提前结束了

优点
每趟结束时,不仅能让最大值找到位置,还能同时部分理顺其他元素。

function bubble(nums){
	let n = nums.length;
	for(let i=1;i //趟数
		let flag = false;//false表示没有发生交换,true表示发生了交换,每一趟开始先置false
		for(let j=0;j//每一趟比较的次数,因为选择nums的元素,所以让j从0开始
			if(nums[j]>nums[j+1]){//发生逆序则交换
				[nums[j],nums[j+1]]=[nums[j+1],nums[j]]; 
				flag = true;
			}
		}
		//一趟结束了
		if(!flag) return nums; //如果某一趟没有元素交换,说明已经排好序了。
	}
	return nums;
}
算法分析

最好情况(正序)
比较次数:n-1
移动次数:0

最坏情况(逆序)
比较次数:1+2+…n-1
移动次数:3(比较次数)

最好时间复杂度:O(n)
最坏时间复杂度:O(n^2)
平均时间复杂度:O(n^2)

适合本身就有序的(正序)

快速排序 - 交换算法 不适合本身有序 基本快排

思路

  1. 选择数组中一个元素(如第一个)为基准值pivotkey
  2. 开始寻找基准值最终的位置,比基准值小的放前面,比基准值大的放后面。最终就是基准值的位置,并且基准值将数组分为左右两个子表。(小 中 大)
  3. 左表和右表分别再执行1,2步。
  4. 直到每一个子表只剩下一个元素。

通过一趟排序,将排序的数组分隔成了两个部分,基准元素也找到最终的位置。
一个n个元素,那么就需要n-1趟。

采用递归的思想实现
1.递归的参数、返回值和停止递归的条件
一般采用左右边界的方法来对数组进行区间划分。
所以我们需要知道数组、左右边界。
我们通过在数组中交换元素进行排序,所以修改了数组本身的值,不需要返回值。
递归的终止条件是子表只有一个元素时,停止。

function qSort(nums,left,right){
	//递归终止条件 为什么不是left=right,考虑基准值在边界的情况,那么下一次的left就会小于right
	if(left>=right) return;
}

2.本层递归逻辑
寻找基准值的位置,根据基准值分为左右两个部分,左右那部分继续排序

function qSort(nums,left,right){
	if(left
		let index =  partiton(nums,left,right);//寻找基准值的位置		
		qSort(nums,left,index-1);//左表排序
		qSort(nums,index+1,right);//右表排序
	}
}
	
function partiton(nums,left,right){
   let pivotkey = nums[left]; //假设数组的第一个元素为基准点
	while(left
		//右边的指针先行,如果找到比基准点小的就可以去覆盖左边指针位置的值,而左边指针位置的值就是我们的基准点,已经被存起来了
		while(left=pivotkey)right--;
		nums[left]=nums[right];
		while(left 
优化:随机基准值的快排 

基本快排的问题:不适合本身有序的数组,如果本身就有序就会退化成冒泡排序

通过随机选取基准值,可以降低初始顺序对快速排序效率的影响。

1.随机选取一个下标
1.1 范围在[left,right]之间,需要生成[left,right]之间的随机整数
2.该下标的值与第一个元素进行交换,就可以开始寻找基准值的位置了。

需要生成[left,right]之间的随机整数
Math.random() 随机 返回[0,1)随机数

Math.random()*(right-left+1)  //[0,5)的随机数
Math.floor(Math.random()*(right-left+1)) //[0,5)的随机整数
left+Math.floor(Math.random()*(right-left+1)) //[2,7)的随机整数

随机基准值选取

let randomIndex = left+Math.floor(Math.random()*(right-left+1)) ;
[nums[randomIndex],nums[left]] = [nums[left],nums[randomIndex]];
let pivotkey = nums[left]; 

完整代码

function sortArray(nums: number[]): number[] {
    let len = nums.length;
    if(len==1)return nums;
    qSort(nums,0,len-1);
    return nums;
};

function qSort(nums,left,right){
	if(left
		let index =  partiton(nums,left,right);//寻找基准值的位置		
		qSort(nums,left,index-1);//左表排序
		qSort(nums,index+1,right);//右表排序
	}
}
	
function partiton(nums,left,right){
    let randomIndex = left+Math.floor((Math.random()*(right-left+1))) ;
[nums[randomIndex],nums[left]] = [nums[left],nums[randomIndex]];
   let pivotkey = nums[left]; 
	while(left
		while(left=pivotkey)right--;
		nums[left]=nums[right];
		while(left 
算法分析 
  • 时间复杂度 O(nlogn)
    • QSort()递归的深度使logn:O(logn)
    • partiton():O(n)
  • 空间复杂度 O(logn)

快速排序不适合对原本有序或基本有序的记录序列进行排序
划分元素的选取是影响时间性能的关键。

归并排序 与初始状态无关

当我们需要排序一个数组时,先将数组分成一半,想办法把左边的数组给排序,右边的数组给排序,之后再将它们归并起来。那现在对数组的排序就变成了对左边数组的排序和右边数组的排序。
当然了当我们对左边的数组和右边的素组进行排序的时候,再分别将左边的数组和右边的数组分成一半,然后对每一个部分归并。
什么时候停止递归,只有一个元素时已经有序了,就不用再排序了,直接归并就好了。


这个思路不就是递归的思路吗!归并的过程就是在递归。

递归三部曲
递归的参数和返回值
直接对原数组nums进行修改
参数需要原数组和两个划分区间的指针left、right

 function merge (let nums,let left,let right){
 }

递归的终止条件
只有一个元素时,自然就是有序的,不需要在进行划分了

if(left==right) return;

本层递归逻辑
本层递归需要把有序的两个部分合并成有序的一个部分。

function merge (nums,left,right){
	if(left==right) return;
 	let mid = (left+right)>>1; //除以2的操作
	merge(nums,left,mid); //左边的有序数组
	merge(nums,mid+1,right);//右边的有序数组
	//优化:如果此时已经有序了,就不用在进行有序合并了
	if(nums[mid]<=nums[mid+1])return;
	//对左边和右边数组进行排序合
	mergeSort(nums,left,mid,right);
	return nums;
}


//两个数组进行排序 
function mergeSort(nums,left,mid,right){
	let l =left;
	let r = mid+1;
	let k = 0;
	let arr = new Array(right-left+1); //记录排好序的结果
	while(l<=mid && r<=right){
		  if(nums[l]<=nums[r]) arr[k++] = nums[l++];
		  else arr[k++] = nums[r++];
	}
	while(l<=mid){
           arr[k++] = nums[l++];
     }
     while(r<=right){
            arr[k++] = nums[r++];
     }
     for(let i=0;i //新数组覆盖老数组
            nums[left+i] = arr[i]; 
     }
}
算法分析
  • 时间复杂度:O(NlogN),这里 N 是数组的长度;
  • 空间复杂度:O(N),辅助数组与输入数组规模相当
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/975488.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号