排序算法-快速排序

快速排序:(时间复杂度O(N^2))

这里写图片描述

方法一:左右指针法(递归,划分为子问题)
注:end的起始位置就是key的位置
这里写图片描述
步骤:
1.从数列中挑出一个元素,key
2.重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
3.递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
有序的情况下或每次选出的key最大or最小,快速排序最坏。

优化:三数取中(取大小中间的那个数,这个方法是为了防止有序的情况下,快排最坏) //减少建立栈帧的开销==
这里写图片描述
保证每次选的key不是最大或最小。

优化:小区间优化(小区间使用插入排序)//减少建立栈帧的开销
小区间使用插入排序,消耗小

方法二:挖坑法
初始状态:
这里写图片描述
移动状态:
这里写图片描述
begin找大,找到大后,值给给end,然后坑变成begin
end找小,找到小后,值填到坑上去,然后坑变成end
这里写图片描述

方法三:(可以使单链表走快排,这个方法代码简洁,但更难理解)
使用两个指针,prev指向区间的前一个位置,cur指向这段区间的开始位置。
初始状态:
这里写图片描述
移动状态:
这里写图片描述
key交换:
这里写图片描述

非递归快速排序: 栈..

代码:
快速排序
Sort.h

#pragma once


//方法三
int PartSort3(int*a, int left, int right)
{
    int prev = left - 1, cur = left;
    while (cur < right)
    {
        if (a[cur] < a[right] && ++prev != cur)
        {
            swap(a[cur], a[prev]);
        }
        ++cur;
    }
    swap(a[right], a[++prev]);

    return prev;

}

//方法二:挖坑法
int PartSort2(int*a, int begin, int end)
{
    int key = a[end];
    while (begin < end)
    {
        while (begin < end && a[begin] <= key)
        {
            ++begin;
        }
        a[end] = a[begin];

        while (begin < end && a[end] >= key)
        {
            --end;
        }
        a[begin] = a[end];
    }
    a[begin] = key;//begin和end相遇

    return begin;

}


//三数取中法
int GetMidIndex(int* a, int begin, int end)
{
    int mid = begin + ((end - begin)) >> 1;
    if (a[begin] < a[mid])
    {
        if (a[mid] < a[end])
        {
            return mid;
        }
        else if (a[begin] > a[end])
        {
            return begin;
        }
        else
        {
            return end;
        }
    }
    else   //mid <= begin
    {
        if (a[end] < a[mid])
        {
            return mid;
        }
        else if (a[begin] < a[end])
        {
            return begin;
        }
        else
        {
            return end;
        }
    }
}



//方法一;左右指针法
int PartSort1(int*a, int begin, int end) //部分排序
{
    int mid = GetMidIndex(a, begin, end);
    swap(a[mid], a[end]);  //将中位数换到最后一个,作为key

    int key = end;//end的起始位置就是key的位置
    while (begin < end)
    {
        while (begin < end && a[begin] <= a[key]) //begin找大,跟key相等 放左边
        {
            ++begin;
        }
        while (begin < end && a[end] >= a[key])   //end找小,跟key相等 放右边
        {
            --end;
        }
        if (begin < end)
        {
            swap(a[begin], a[end]);
        }
    }
    swap(a[begin], a[key]);   //相等时与key交换
    return begin;
}

//[left,right]假设区间为闭区间
void QuickSort(int* a,int left,int right)
{
    if (left >= right)
    {
        return;
    }

    if (right - left < 5)//right - left是区间个数,如果小于5个,使用插入排序(小区间优化)
    {
        InsertSort(a+left, right - left + 1);//区间是从a+left开始 right - left + 1结束
    }

    else //说明这个区间至少有两个及以上的数    (left < right)
    {
        //PartSort1为左右指针法,PartSort2为挖坑法(使用PartSort2时要将小区间优化注释掉),PartSort3为方法三
        int div = PartSort3(a, left, right);//div为划分的分区  
        QuickSort(a, left, div - 1);//左区间  [left, div - 1]
        QuickSort(a, div + 1, right);//右区间  [div + 1, right]
    }

}

void TestQuickSort()
{
    int a[] = { 2, 5, 4, 9, 3, 6, 1, 7, 1, 0 };   //有相等的情况
    //int a[] = { 0, 1 };//注意,如果end的起始位置为key的前一个,则这种情况下会出错
    //int a[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };//有序的情况下,三数取中好
    QuickSort(a, 0,sizeof(a) / sizeof(a[0])-1);//给出区间
    PrintArray(a, sizeof(a) / sizeof(a[0]));
}

Test.cpp

#include<iostream>
#include<assert.h>
using namespace std;

#include"Sort.h"

int main()
{
    TestQuickSort();
    system("pause");
    return 0;
}

非递归 快速排序

Sort.h

#pragma once
#include<stack>

int PartSort1(int*a, int begin, int end) //部分排序
{
    int mid = GetMidIndex(a, begin, end);
    swap(a[mid], a[end]);  //将中位数换到最后一个,作为key

    int key = end;//end的起始位置就是key的位置
    while (begin < end)
    {
        while (begin < end && a[begin] <= a[key]) //begin找大,跟key相等 放左边
        {
            ++begin;
        }
        while (begin < end && a[end] >= a[key])   //end找小,跟key相等 放右边
        {
            --end;
        }
        if (begin < end)
        {
            swap(a[begin], a[end]);
        }
    }
    swap(a[begin], a[key]);   //相等时与key交换
    return begin;
}


//非递归快速排序
void QuickSortNonR(int* a, int left, int right)
{
    stack<int> s;
    s.push(right);
    s.push(left);

    while (!s.empty())
    {
        int begin = s.top();
        s.pop();
        int end = s.top();
        s.pop();

        int div = PartSort1(a, begin, end);//[begin,div-1]  [div+1,end]
        if (begin < div - 1)
        {
            s.push(div - 1);
            s.push(begin);
        }
        if (div + 1 < end)
        {
            s.push(end);
            s.push(div +1);
        }
    }
}

void TestQuickSort()
{
    int a[] = { 2, 5, 4, 9, 3, 6, 1, 7, 1, 0 };
    QuickSortNonR(a, 0, sizeof(a) / sizeof(a[0]) - 1);
    PrintArray(a, sizeof(a) / sizeof(a[0]));
}

Test.cpp

#include<iostream>
#include<assert.h>
using namespace std;

#include"Sort.h"

int main()
{
    TestQuickSort();
    system("pause");
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值