我们一起写算法07

快速排序算法与三路快排

快速排序(Quicksort)是对冒泡排序的一种改进。

它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

1.普通快排

快速排序不是一种稳定的排序算法,也就是说,多个相同的值的相对位置也许会在算法结束时产生变动。

一趟快排算法步骤

设要排序的数组arr有N个元素。

1.设置两个变量left、right,排序开始的时候:left=0,right=N-1。

2.以第一个数组元素作为关键数据(基准元素pivot),赋值给pivot,即pivot=A[0];(优化时选择合适的基准元素)。

3.从rightj开始向前搜索,即由后开始向前搜索(right–),找到第一个小于pivot的值A[right],将A[right]和A[left]的值交换。

4.从left开始向后搜索,即由前开始向后搜索(left++),找到第一个大于pivot的A[left],将A[left]和A[right]的值交换。

5.重复第3、4步,直到left=right; (3,4步中,没找到符合条件的值,即3中A[right]不小于pivot,4中A[left]不大于pivot的时候改变right、left的值,使得right=right-1,left=left+1,直至找到为止。找到符合条件的值,进行交换的时候left,right指针位置不变。另外,left==right这一过程一定正好是left++或right–完成的时候,此时令循环结束)。

javascript代码实现如下:

function swap(array,left,right){
	let temp = array[right];
	array[right] = array[left];
	array[left] = temp;
}
// 每次选择最左边的数作为基数
function quickSort(arr){
	//递归出口条件
  if (arr.length<2) { return arr; }
  // 1.定义左右指针
  var left=0;
  var right=arr.length-1;
  // 2.找基准值
  var pivot = arr[0];
  //开启每一轮的排序
  while(left<right){
    // 3.不断递减变量right的值,直到找到右边比arr[0]小的数的下标,找到后二者交换位置
    while(arr[right]>=pivot && left<right){
      right--;
    }
    // 4.不断递增left变量的值,直到找到左边比arr[0]大的数的下标,找到后二者交换位置
    while(arr[left]<=pivot && left<right){
      left++;
    }
    //5.当左边指针与右边指针相遇后,交换arr[0]与当前两个指针所在的元素
    if (right==left) {
    	swap(arr,right,0)
      	break;
    }
    //符合上面条件后 交换两个指针当前位置的元素
    swap(arr,right,left)
  }
  //多次分解数组,直到数组不能再分解为止(只有一个数据),才能得到正确结果
  //递归实现
  var leftarr=quickSort(arr.slice(0,left))//每次抽取指针左侧元素做quickSort,left值在变化
  var rightarr=leftarr.concat(arr.slice(left,right+1))//将指针所指元素连接到左侧数组中
  var newarr = rightarr.concat(quickSort(arr.slice(right+1)))//先把right+1到末尾的数组元素做quickSort,然后连接到上面数组后面
  return newarr
}
//对数组进行排序
console.log(quickSort([5,8,4,9,3,4,7]));//[3, 4, 4, 5, 7, 8, 9]

涉及的API方法介绍:

方法名 用途 语法
slice() 提取字符串的某个部分,并以新的字符串返回被提取的部分 stringObject.slice(start,end)
concat() 用于连接两个或多个数组,该方法不会改变现有的数组,而仅仅会返回被连接数组的一个副本。 arrayObject.concat(arrayX,arrayX,…,arrayX)

参数说明:

slice

concat

2.三路快排

定义:

三路快速排序是快速排序的的一个优化版本, 将数组分成三段, 即小于基准元素、 等于基准元素和大于基准元素这三组, 这样可以比较高效的处理数组中存在相同元素的情况,其它特征与快速排序基本相同。

条件:

当大量数据,且重复数多时,用三路快排。

步骤:

1.新建三个数组变量left、center、right,分别存储小于、等于、大于基准元素的元素。

2.选取基准值pivot (可随机选取)。

3.遍历数组元素,小于基准值的存到left数组,等于基准值的存到center数组,大于基准值的存到right数组。

4.递归调用。反复对left数组和right数组执行步骤1、2、3,直到满足递归出口条件,即left和right数组各自排好序。使用ES6的扩展运算符...展开left、center、right数组,返回排序后的数组。

代码

function quickSort_3(arr1){
  	// 递归出口
  	if (arr1.length == 0) {
  		return [];
  	}
  	//1.新建数组变量left、center、right
  	var left = [];
  	var center = [];
  	var right = [];
  	// 2.选基准值
  	var pivot = arr1[0];//可随机选

  	// 3.遍历数组元素,小于基准值的存到left数组,等于基准值的存到center数组,大于基准值的存到right数组。
  	for (var i=0;i<arr1.length;i++){
  		if (arr1[i]<pivot) {
  			left.push(arr1[i])
  		} else if (arr1[i]==pivot){
  			center.push(arr1[i])
  		} else{
  			right.push(arr1[i])
  		}
  	}
  	// 4.递归调用
  	return [...quickSort_3(left),...center,...quickSort_3(right)]
  }
  
  console.log(quickSort_3([5,8,4,3,7,1,4]))//[1, 3, 4, 4, 5, 7, 8]

3. 快排应用

例如LeetCode第75题,颜色分类。

题目:

/*
给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。

此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。

注意:
不能使用代码库中的排序函数来解决这道题。

示例:

输入: [2,0,2,1,1,0]
输出: [0,0,1,1,2,2]
进阶:

一个直观的解决方案是使用计数排序的两趟扫描算法。
首先,迭代计算出0、1 和 2 元素的个数,然后按照0、1、2的排序,重写当前数组。
你能想出一个仅使用常数空间的一趟扫描算法吗?

*/

此题重复元素较多,就可以使用三路快排的思想。

上述代码,输入数组[2,0,2,1,1,0],输出如下:

结果

回到最初开始的的地方

发布了111 篇原创文章 · 获赞 120 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/w1418899532/article/details/95455736