快速排序

来源:互联网 发布:提成算法 编辑:程序博客网 时间:2024/06/06 06:44

快速排序算法是一种划分交换的方法,它采用分治法进行排序。
基本思想:取元素序列的某个元素作为基准,将序列划分成左右两个子序列,比这个元素大的或小的放到左边,相对小的或大的放右边,该基准元素则放到中间。然后对此子序列重复上面的方法,直到所有的元素都排在相应的位置。
大体图解
这里写图片描述
这里我们就可以看到这完全是一个子问题,所以我们想到的第一种方法就是递归实现

void QuickSort(int* a,int begin,int end){    assert(a);    if (begin < end)    {        //partsort就是要实现每次某一段区间将怎样划分。        //int div = PartSort1(a, begin, end);        //int div = PartSort2(a, begin, end);        int div = PartSort3(a, begin, end);        QuickSort(a, begin, div - 1);        QuickSort(a, div + 1, end);     }}

实现每一段区间的单趟排序
1.左右指针法
left,right表示一段区间的开始与结束,取a[right]作为基准key,当a[left]小于等于key时,left往后走,left++,当a[right]大于等于key时,right往前走,right–,否则,a[left]找一个比key大的,a[right]找一个比key小的时,进行交换,当left和right相遇时,就把key和这个位置的元素交换。
这里写图片描述
三数取中法

int Getmid(int* a, int left, int right){    int mid = left + (right - left) / 2;    if (a[left] > a[right])    {        if (a[left] > a[mid])        {            if (a[mid] > a[right])            {                return mid;            }            else            {                return right;            }        }        else        {            return left;        }    }    else    {        if (a[mid] < a[right])        {            if (a[mid]>a[left])            {                return mid;            }            else            {                return left;            }        }        else        {            return right;        }    }}
int PartSort1(int* a, int begin, int end){    int left = begin;    int right = end;    int mid = Getmid(a, begin, end);//三数取中法优化,避免取到的基准太大或太小,影响效率。    swap(a[mid], a[end]);//将三数取中法取到的数放到最后    int key = a[right];    while (left < right)//left==right循环结束    {        while(left < right && a[left] <= key)        {            left++;        }        while(left < right && a[right] >= key)        {            right--;        }        if (a[left] > a[right])        {            swap(a[left], a[right]);        }    }    swap(a[left], a[end]);    return left;}

2.挖坑法
其思想和上一种方法大体相同,left,right同样是一段区间的结束和开始,先将a[right]作为第一个坑hole,并将该坑的元素保存,left++,找到一个比这个坑元素大的,放到这个坑里,这样left这个位置就是一个新坑,然后right–,找到一个hole小的,放到这个新坑里,当left和right相遇时,结束,将保存的第一个坑的元素放到这个位置。
这里写图片描述

int PartSort2(int* a, int begin, int end){    int left = begin;    int right = end;    int mid = Getmid(a, begin, end);    int hole = a[end];//定义的一个坑    while (left < right)    {        while (left < right&&a[left] <= hole)        {            left++;        }        if (left < right)//循环可能是第一个条件不符合结束的,所以这里需要判断下        {            swap(a[left], a[right--]);        }        while (left < right&&a[right] >= hole)        {            right--;        }        if (left < right)        {            swap(a[left++], a[right]);        }    }    a[left] = hole;    return left;}

3.前后指针法
定义cur=begin,prev=begin-1,取a[end]为key,cur小于等于key时,prev和cur都往后走,当cur大于key时,只cur++,当cur小于key并且++prev不等于cur时,交换cur,prev,这样小的在前面,大的在后面,当cur等于end时,key放到prev+1的位置。
这里写图片描述

int PartSort3(int* a, int begin, int end){    int cur = begin;    int prev = begin - 1;    int mid = Getmid(a, begin, end);    swap(a[mid], a[end]);    int key = a[end];    while (cur < end)    {        if (a[cur] < key&&++prev != cur)        {            swap(a[cur], a[prev]);        }        cur++;    }    swap(a[++prev], a[end]);    return prev;}

非递归实现
要借助栈来保存每次排序的区间,取出来区间进行排序,然后又把新的区间压进去。

void QuickSortNoR(int* a, int begin, int end){    assert(a);    stack<int> s;    s.push(end);    s.push(begin);    while (!s.empty())    {        int left = s.top();        s.pop();        int right = s.top();        s.pop();        if (left < right)        {            int div = PartSort2(a, left, right);            s.push(div - 1);            s.push(left);            s.push(right);            s.push(div + 1);        }    }}

快速排序是一种不稳定排序,对于排序码相同的元素,排序后可能会颠倒次序。它的时间复杂度是n*lgn。对于它的优化,三数取中法,还可以对小区间的优化,当划分到一定小的区间,不要再继续划分递归,毕竟递归的开销很大,用插入排序对这些小区间排序就行。

原创粉丝点击