常用排序算法之快速排序

32 篇文章 2 订阅
8 篇文章 0 订阅

1.何为“快排”?

     快速排序,即就是很快速,很便捷的一种排序嘛-!O(∩_∩)O~

   但是,快速排序也并非一定是快速的。如果按照最糟糕的情况来看,它的时间复杂度几乎和冒泡排序是相同的0(n2)(是什么情况呢?)。但是,平均时间复杂度是0(NlogN)。

2.基本思想

  快速排序是基于一种“二分”的思想。

   它将在一组无序数中,寻找一个基准数,该数将原数组一分为二,左边的数组的数均小于该基准数,而右边的数组的数均大于该基准数。而该左,右两边的数组再依次递归进行上述操作,直至递归结束。

   快速排序之所以被称为是冒泡排序的进化版,是因为冒泡排序每次比较的都是相邻的两个数,而快速排序只有在最坏的情况下才是这样。一般情况下,快排中的两数交换的距离要大得多!

3.算法模拟

原始数组

 

01234
32451

 

第一轮遍历

  (1) 根据快排的思想,找到一个基准数,我们上面说过它可以是任意数,那么,我们姑且将数组中的第一个数作为基准数(其实不用姑且,因为地球银都这样),即将3作为基准数。

  (2) 我们就开始伟大的征程了!先从右边开始j,寻找错误顺序的那个数(即比基准数小的数),然后,从左开始i,同样,寻找错误顺序的那个数(比基准数大的数)

 

01234
32451

  (3) 根据上表,我们找到了a[2]=4,a[4]=1这两个站错位置的数。接着交换两个数的值(类似于冒泡排序)

 

01234
32154

  (4) 还没有完,我们接下来继续找与基准数站错位置的数。黄天不负有心银,我们找到了5这个数。此时,我们要注意的是,5的左右两边我们之前已经将其他站错位置的数已经“排好序”,这说明,两军已经成功会师i==j!再将5这个数与基准数交换位置即可。

  此时,第一轮遍历完毕!a[3]的顺序已经确定。

 

01234
52134

第二轮遍历

  (1) 第一次遍历,我们将原始数组一分为二,left:a[0-2],right:a[4]

 

012
521

 

 

 

4
4

 

  (2) 因为数据少,比较容易思考(好吧,是寡人太懒),所以right的数组中的值已经排好序了。

 

      因此,我们来看left的数组。同理,将5作为基准数!

 

012
521

 (3)  然后,先从右边开始(想想为什么每次都是从右开始)遍历,我们找到站错位置的a[2]=1,接着,开始从左开始遍历,啊哦~没找到。注意,a[2]=1的左右两边都已经被遍历过了(两军会师i==j)。那么,将1与基准数进行交换。

      此时,第二轮遍历完毕。a[2]位置已经确定!

 

012
125

 

第三轮遍历

 

  (1) 将上一轮的数组,以a[2]进行二分,很遗憾!只剩下左半边。left a[0-1]

 

01
1

  (2) ok,先从右开始遍历,我们找到没有站错位的数,然后,就没有然后了..。因为,基准数的右边全都站位正确。a[0]=1位置已经确定!

 

第四轮遍历与第五轮遍历

    a[1]=2,a[4]=4

   这个不用说了,直接return 返回即可。

 

4.核心代码

(1) 寻找基准数a[1]    a[0]作为临时变量,存储每次遍历后新数组的a[1]值

 

a[0] = a[left];//<span style="font-family: Arial, Helvetica, sans-serif;">3 3 1 2 5 4</span>

(2)从右往左开始遍历j--,找到站错位置的数a[j](比基准数a[0]小) 

 

 

 

 

//从右往左遍历 ,寻找不满足条件的数 3 3 1 2 5 4
		while(i<j){
			if(a[0]>a[j]){
				break;
			}
			j--;
		}

 

(3) 从左往右开始遍历i++,找到站错位置的数a[i](比基准数a[0]大) 

 

//从左往右遍历 
		while(i<j){
			if(a[0]<a[i]){
				break;
			}
			i++;
		}

 

(4) 将会师点的数(i==j)a[i]或a[j]与基准数a[0]进行交换

 

a[left] = a[i];
a[i] = a[0];

(5)传送门调用

 

quickSort(left,i-1,a);//左半边调用
quickSort(i+1,right,a);//右半边调用

 

5.完整代码

 

#include <stdio.h>
#define N 100

void quickSort(int left,int right,int *a){
	if(left>right){//即超过会师点
		return;
	}
	
	int i = left;  //i,j存储当前的左右下标</span>
	int j = right;
	
	a[0] = a[left];
	while(i<j){//每一轮循环
		//从右往左遍历 ,寻找不满足条件的数 3 3 1 2 5 4
		while(i<j){
			if(a[0]>a[j]){
				break;
			}
			j--;
		}
		//从左往右遍历 
		while(i<j){
			if(a[0]<a[i]){
				break;
			}
			i++;
		}
		if(i<j){//类似冒泡排序,交换站错位置的两个数
			int temp = a[i];
			a[i] = a[j];
			a[j] = temp;
		}
	}
	a[left] = a[i];//交换基准点与会师点的数
	a[i] = a[0];
	
	quickSort(left,i-1,a);
	quickSort(i+1,right,a);
	
}

int main(void){
	
	int n=5; 
	//scanf("%d",&n);
	int a[N]={-1,3,1,2,5,4};	//a[0]=-1  相当于一个中转,存放基准数的临时变量
	
	quickSort(1,n,a); 
	
	int i;
	
	for(i=1;i<=n;i++){
		printf("%-3d",a[i]);
	}
	
	return 0;
	
} 

 

6.运算结果

 

 

 

另附JS实现:

 

/**
 * @param {Array} arr
 * @return {Array}
 */
var quickSort = function (arr, left, right) {
  if (left > right) return arr
  // 以左边界作为基准值
  let base = arr[left]
  let _left = left + 1
  let _right = right
  while (_left <= _right) {
    // 处理左边
    while (base > arr[_left]) {
      if (base < arr[_left]) break
      _left++
    }
    // 处理右边
    while (base < arr[_right]) {
      if (base > arr[_right]) break
      _right--
    }
    if (_left < _right) {
      arr[_left] += arr[_right]
      arr[_right] = arr[_left] - arr[_right]
      arr[_left] = arr[_left] - arr[_right]
    }
  }
  arr[left] = arr[_right]
  arr[_right] = base
  arr = quickSort(arr, left, _right - 1)
  arr = quickSort(arr, _right + 1, right)
  return arr
}
let a = [5, 6, 8, 4, 2, 9, 7, 1, 3]
console.log('QS', quickSort(a, 0, a.length - 1))

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值