排序算法第四讲 --- 快速排序(Python、C++、C)

42 篇文章 6 订阅
39 篇文章 142 订阅

排序算法系列目录

  排序算法第一讲 — 冒泡排序(Python、C++、C)
  排序算法第二讲 — 选择排序(Python、C++、C)
  排序算法第三讲 — 插入排序(Python、C++、C)
  排序算法第四讲 — 快速排序(Python、C++、C)
  排序算法第五讲 — 希尔排序(Python、C++、C)
  排序算法第六讲 — 归并排序(Python、C++、C)


题目描述:

给你一个整数数组 nums,请你将该数组采用快速排序方式进行升序排列。

输入示例: [1,8,6,2,5,4,9,3,7]
输出示例: [1,2,3,4,5,6,7,8,9]


解题思路:

快速排序使用分治法来把一个串(list)分为两个子串(sub-lists)。具体算法描述如下:

  • 从数列中挑出一个元素,称为 “基准”(pivot);
  • 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
  • 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。

栗子描述:

来源于:快速排序

  假设我们现在对 "6 1 2 7 9 3 4 5 10 8" 这个10个数进行排序。首先在这个序列中随便找一个数作为基准数。为了方便,就让第一个数6作为基准数吧。接下来,需要将这个序列中所有比基准数大的数放在6的右边,比基准数小的数放在6的左边,类似3 1 2 5 4 6 9 7 10 8
这种排列。那要如何做到呢?

方法:
  分别从初始序列"6 1 2 7 9 3 4 5 10 8"两端开始" 探测 "。先从右往左找一个小于6的数,再从左往右找一个大于6的数,然后交换他们。这里可以用两个变量 i i i j j j,分别指向序列最左边和最右边。刚开始的时候让 i i i指向序列的最左边(即 i = 1 i=1 i=1),指向数字6。让哨兵 j j j指向序列的最右边(即 j = 10 j=10 j=10),指向数字8。

  首先 j j j开始出动。因为此处设置的基准数是最左边的数,所以需要让 j j j先出动,这一点非常重要(请自己想一想为什么)。 j j j一步一步地向左挪动(即 j − − j-- j),直到找到一个小于6的数停下来。接下来 i i i再一步一步向右挪动(即 i + + i++ i++),直到找到一个数大于6的数停下来。最后 j j j停在了数字5面前, i i i 停在了数字7面前。

  现在交换 i i i j j j所指向的元素的值。交换之后的序列为6 1 2 5 9 3 4 7 10 8

  到此,第一次交换结束。接下来开始 j j j继续向左挪动。他发现了4(比基准数6要小,满足要求)之后停了下来。 i i i也继续向右挪动的,他发现了9(比基准数6要大,满足要求)之后停了下来。此时再次进行交换,交换之后的序列为6 1 2 5 4 3 9 7 10 8

  第二次交换结束,“探测"继续。 j j j 继续向左挪动,他发现了3(比基准数6要小,满足要求)之后又停了下来。 i i i 继续向右移动,糟啦!此时 i i i j j j 相遇了, i i i j j j 都走到3面前。说明此时” 探测 "结束。我们将基准数6和3进行交换。交换之后的序列为3 1 2 5 4 6 9 7 10 8

  到此第一轮“探测”真正结束。此时以基准数6为分界点,6左边的数都小于等于6,6右边的数都大于等于6。回顾一下刚才的过程,其实 j j j的使命就是要找小于基准数的数,而 i i i的使命就是要找大于基准数的数,直到 i i i j j j 碰头为止。

  OK,解释完毕。现在基准数6已经归位,它正好处在序列的第6位。此时我们已经将原来的序列,以6为分界点拆分成了两个序列,左边的序列是“3 1 2 5 4”,右边的序列是“9 7 10 8”。接下来还需要分别处理这两个序列。因为6左边和右边的序列目前都还是很混乱的。不过不要紧,我们已经掌握了方法,接下来只要模拟刚才的方法分别处理6左边和右边的序列即可。现在先来处理6左边的序列现吧。

  如果你模拟的没有错,调整完毕之后的序列的顺序应该是2 1 3 5 4

最终得到正确的排序结果:1 2 3 4 5 6 7 8 9 10


代码:

Python写法:

class Solution(object):
    def solution(self,  nums):
        if len(nums) < 2:
            return nums
        else:
            tmp = nums[0]
            less = [num for num in nums[1:] if num <= tmp]
            big = [num for num in nums[1:] if num > tmp]
            return self.solution(less) + [tmp] + self.solution(big)

C++写法:

#include<iostream>
using namespace std;

int a[100],n;  //定义全局变量

void quicksort(int left, int right) {
	int i, j, t, temp;
	if(left > right)
		return;
    temp = a[left];  //temp中存的就是基准数
    i = left;
    j = right;
    while(i != j) {  //顺序很重要,要先从右边开始找
    	while(a[j] >= temp && i < j)
    		j--;
    	while(a[i] <= temp && i < j) //再找右边的
    		i++;       
    	if(i < j)    //交换两个数在数组中的位置
    	{
    		t = a[i];
    		a[i] = a[j];
    		a[j] = t;
    	}
    }
    //最终将基准数归位
    a[left] = a[i];
    a[i] = temp;
    quicksort(left, i-1);  //继续处理左边的,这里是一个递归的过程
    quicksort(i+1, right); //继续处理右边的 ,这里是一个递归的过程
}
int main() {
	int i;
    
	cin >> n;                //读入数据

	for(i = 1; i <= n; i++)
		cin >> a[i];

    quicksort(1, n);         //快速排序调用
    
	
    for(i = 1; i <= n; i++)  //输出排序后的结果
    	cout << a[i] <<" ";

    cout << "\n";
    return 0;
}

C语言:

#include <stdio.h>

int a[100],n;  //定义全局变量

void quicksort(int left, int right) {
	int i, j, t, temp;
	if(left > right)
		return;
    temp = a[left];  //temp中存的就是基准数
    i = left;
    j = right;
    while(i != j) {  //顺序很重要,要先从右边开始找
    	while(a[j] >= temp && i < j)
    		j--;
    	while(a[i] <= temp && i < j) //再找右边的
    		i++;       
    	if(i < j)    //交换两个数在数组中的位置
    	{
    		t = a[i];
    		a[i] = a[j];
    		a[j] = t;
    	}
    }
    //最终将基准数归位
    a[left] = a[i];
    a[i] = temp;
    quicksort(left, i-1);  //继续处理左边的,这里是一个递归的过程
    quicksort(i+1, right); //继续处理右边的 ,这里是一个递归的过程
}
int main() {
	int i;
    //读入数据
	scanf("%d", &n);

	for(i = 1; i <= n; i++)
		scanf("%d", &a[i]);
    quicksort(1, n);   //快速排序调用
    
	//输出排序后的结果
    for(i = 1; i <= n; i++)
    	printf("%d ", a[i]);

    printf("\n");
    return 0;
}


题目来源:

http://lab.csdn.net/#/question/38?tagId=16

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值