排序算法学习经验(三)

来源:互联网 发布:淘宝首页显示不全 编辑:程序博客网 时间:2024/06/10 22:28

上次总结了一下归并排序及其优化的思路,接上次,这次详细分析一下快速排序的具体思路,方便大家理解。
关键点提要:
快速排序、双路快排、三路快排

快速排序:
快速排序相对于归并排序,不需要额外的数组空间,因此空间效率很高。而且,快速排序是许多系统调用库函数的默认排序函数。其思路就是将一组元素,选定一个标定点,然后将整个元素分成两部分,左半部分小于或者等于这个标定点,右半部分大于等于这个标定点;然后分别又对分成的两部分同样进行上面的操作:选标定点过后排序。这样操作下去,整个数组就变成有序的了。
先看举例:
第一步:选定标定点
这里写图片描述
第二步:考虑当前元素的大小,假设大于标定点,则直接合并,若小于标定点,则交换再合并。
大于标定点:
这里写图片描述
这里写图片描述
小于标定点:
这里写图片描述
这里写图片描述
第三步:依次操作直到最后一个元素,然后交换标定点和最后一个小于标定点的值。则完成了标定点j左边的元素全部小于等于v,右边全部大于v的操作
这里写图片描述
这里写图片描述

结合图片理解代码:

#pragma once#include<iostream>using namespace std;// 对arr[l...r]范围的数组进行插入排序template<typename T>void insertionSort(T arr[], int l, int r){    for (int i = l + 1; i <= r; i++)    {        T e = arr[i];        int j;        for (j = i; arr[j - 1] > e && j > l; j--)        {            arr[j] = arr[j - 1];        }        arr[j] = e;    }}template<typename T>int partition(T arr[],int l, int r){    swap(arr[l], arr[rand() % (r - l + 1) + l]);    T p = arr[l];//设置标定点    int i = l + 1;//当前要考虑元素下标    int j = l;//最后一个小于p的元素    while (i <= r)    {        if (arr[i] <= p)        {            swap(arr[i], arr[j + 1]);            j++;        }        i++;    }    swap(arr[j], arr[l]);    return j;}template<typename T>void __quickSort(T arr[], int l, int r){    //递归到底的情况,和前面类似用插入排序    if (r - l <= 9)    {        insertionSort(arr, l, r);        return;    }    int p = partition(arr, l, r);//标定点    //递归到底层后数组就成为有序的数组    __quickSort(arr, l, p - 1);//左层递归调用,继续partition    __quickSort(arr, p + 1, r);//右层递归调用,继续partition}template<typename T>void quickSort(T arr[], int n){    srand(unsigned(time(NULL))); //设置随机种子    __quickSort(arr, 0, n - 1);  //初次调用}

以上就是对基础快速排序的全部实现了。这个算法有一个致命缺陷,就是在partition操作时,分组极其不平衡,导致分组会成为一个极端,左边元素为0或者右边元素为0。例如一个大量重复数组,只包含1,2,3三种元素,进行排序,代码中会将2分到一边,如果3元素个数很少,就会导致12的总数量很大,导致不平衡。为了减少这些情况的出现,下次将会分析一下快速排序的改进,也就是双路快排以及三路快排的原理以及实现。

原创粉丝点击