简单选择排序_经典排序算法一、从简单选择排序到堆排序的深度解析

一、简单选择排序

1、从一个简单问题谈起

给定待排序序列A[ 1.....n ],选择出A中最小的记录。下面给出代码如下:

//选择待排序序列a中的最小记录,其下标为indexInt index = 0;for(int i=1;i{    if(a[i]index])          index=i;  }

相信这段代码大家都能看懂,其中A[index]即为要找的最小记录。

2、简单选择排序的过程

问题描述:给定待排序序列A[ 1......n ] ,选择出第i小元素,并和A[i]交换,这就是一次简单选择排序。

//简单选择排序void SimpleSelectionSortOne(int *a,int n){    int i,j,index;    //1.进行n-1趟选择,每次选出第i小记录    for(i=0;i1;i++)    {      index=i;      //2.选择第i小记录为a[index]      for(j=i+1;j        if(a[j]index])          index=j;      //3.与第i个记录交换      if(index!=i)      {          a[i]=a[i]+a[index];          a[index]=a[i]-a[index];          a[i]=a[i]-a[index];      }    }}

图解实例:

假设给定数组A[1......6]={ 2,5,7,9,5,2 },我们来分析一下A数组进行选择排序的过程

第一趟:i=0,index=0, 不用交换。得到序列:{ 2,5,7,9,5,2 }

第二趟:i=1,index=5, a[1] 和 a[5] 进行交换。得到序列:{ 2,2,7,9,5,5 }

第三趟:i=2,index=4, a[2] 和 a[4] 进行交换交换。得到序列:{ 2,2,5,9,7,5 }

第四趟:i=3,index=5, a[3] 和 a[5] 进行交换交换。得到序列:{ 2,2,5,5,7,9 }

第五趟:i=4,index=5, 不用交换。得到序列:{ 2,2,5,5,7,9 }

第六趟:趟选择结束,得到有序序列:{ 1,2,3,5,8,9 }

最佳情况下(待排序序列有序)记录移动次数为0,最坏情况下(待排序序列逆序)记录移动次数n-1。外层循环进行了n-1趟选择,第i趟选择要进行n-i次比较。每一趟的时间:n-i次的比较时间+移动记录的时间(为一常数0或1,可以忽略)。总共进行了n-1趟。忽略移动记录的时间,所以总时间为(n-1)*(n-i)=n^2-(i+1)*n+i。时间复杂度为O(n^2)。不管是最坏还是最佳情况下,比较次数都是一样的,所以简单选择排序平均时间、最坏情况、最佳情况 时间复杂度都为O(n^2)。

递归实现简单选择排序

//递归函数进行简单选择排序void SimpleSelectionSortTwo(int *a,int n){    int index,i;    if(n==0)      return;    //1.选择待排序序列a中的最小记录,其下标为index    for(index=i=0;i    {        if(a[i]index])            index=i;    }    //2.最小记录与待排序序列首元素进行交换    if(index!=0)    {        a[0]=a[0]+a[index];        a[index]=a[0]-a[index];        a[0]=a[0]-a[index];     }      //3.待排序序列元素个数减少,递归对剩下的无序序列排序      simpleSelectionSortTwo(a+1,n-1);}

二、堆排序

从上文我们知道简单选择排序的时间复杂度为O(n^2),熟悉各种排序算法的朋友都知道,这个时间复杂度是很大的,所以怎样减小简单选择排序的时间复杂度呢?从上文分析中我们知道简单选择排序主要操作是进行关键字的比较,所以怎样减少比较次数就是改进的关键。 简单选择排序中第i趟需要进行n-i次比较,如果我们用到前面已排好的序列a[1...i-1]是否可以减少比较次数呢?答案是可以的。举个例子来说吧,A、B、C进行比赛,B战胜了A,C战胜了B,那么显然C可以战胜A,C和A就不用比了。

1、什么是堆

Min-heap: 父节点的值小于或等于子节点的值

Max-heap: 父节点的值大于或等于子节点的值

681e08dca347c455328bdab09353c187.png

2、堆的存储

一般都用数组来表示堆,i结点的父结点下标就为(i–1)/2。它的左右子结点下标分别为2 * i + 1和2 * i + 2。如第0个结点左右子结点下标分别为1和2。

02b37f10d14ba46f51b70f5711861112.png

由于堆存储在下标从0开始计数的数组中,因此,在堆中给定下标为i的结点时:

(1)如果i=0,结点i是根结点,无父结点;否则结点i的父结点为结点(i-1)/2;

(2)如果2i+1>n-1,则结点i无左子女;否则结点i的左子女为结点2i+1;

(3)如果2i+2>n-1,则结点i无右子女;否则结点i的右子女为结点2i+2。

3、大根堆插入元素

插入一个元素:新元素被加入到heap的末尾,然后更新树以恢复堆的次序。

每次插入都是将新数据放在数组最后。可以发现从这个新数据的父结点到根结点必然为一个有序的数列,现在的任务是将这个新数据插入到这个有序数据中——这就类似于直接插入排序中将一个数据并入到有序区间中。需要从下往上,与父节点的关键码进行比较,对调。

图解实例:

eb010864424bab4e530f36e7bdf3b7b2.png

代码实例:

bool MaxHeap::Insert(const int iValue){    if(m_iCurrentHeapSize == m_iHeapMaxSize)    {           cout<<"[info] max heap full"<<endl;        return false;    }       m_pHeap[m_iCurrentHeapSize] = iValue;    ShiftUp(m_iCurrentHeapSize);    m_iCurrentHeapSize++;    return true;}

4、删除根堆堆的最元素

按定义,堆中每次都删除第0个数据。为了便于重建堆,实际的操作是将最后一个数据的值赋给根结点,堆的元素个数-1,然后再从根结点开始进行一次从上向下的调整。调整时先在左右儿子结点中找最小的,如果父结点比这个最小的子结点还小说明不需要调整了,反之将父结点和它交换后再考虑后面的结点。相当于从根结点将一个数据的“下沉”过程。

图解实例:

0389f408dcd75f69acef3beaab70b74e.png

代码实例:

bool MaxHeap::RemoveMaxValue(int &iValue){    if(m_iCurrentHeapSize == 0)    {        cout<<"[info] max heap empty"<<endl;        return false;    }    iValue = m_pHeap[0];    m_pHeap[0] = m_pHeap[m_iCurrentHeapSize-1];    m_iCurrentHeapSize--;    ShiftDowm(0, m_iCurrentHeapSize-1);    return true;}

5、创建最大堆

对于叶子节点,不用调整次序,根据满二叉树的性质,叶子节点比内部节点的个数多1.所以i=n/2 -1 ,不用从n开始。就是从最后一个有叶子结点的结点开始。

代码实例:

MaxHeap::MaxHeap(int *pArry,int iSize){    m_iHeapMaxSize =   iSize > HEAPDEFAULTSIZE ? iSize : HEAPDEFAULTSIZE;    try    {        m_pHeap = new int[m_iHeapMaxSize];    }    catch(std::bad_alloc)    {        exit(-1);    }    m_iCurrentHeapSize = iSize;    for(int i = 0; i < iSize; i++)    {        m_pHeap[i] = pArry[i];    }    //craete max heap    int iCurerentPos = (m_iCurrentHeapSize - 2)/2;    while(iCurerentPos >= 0)    {        ShiftDowm(iCurerentPos, m_iHeapMaxSize-1);        iCurerentPos--;    }}

6、堆排序

过程描述:1、建堆  2、将堆顶记录和堆中最后一个记录交换  3、筛选法调整堆,堆中记录个数减少一个,重复第2步。整个过程中堆是在不断的缩减。

代码实现:

bool MaxHeap::HeapSort(){    if(m_iCurrentHeapSize == 0)    {        cout<<"[info] max heap is empty"<<endl;        return false;    }                   for(int i=m_iCurrentHeapSize-1; i>0; i--)    {           m_pHeap[0] = m_pHeap[0] + m_pHeap[i];        m_pHeap[i] = m_pHeap[0] - m_pHeap[i];        m_pHeap[0] = m_pHeap[0] - m_pHeap[i];        ShiftDowm(0, i - 1);     }       cout<<"[info] max heap sort content is: "<<endl;    PrintHeap();    return true;}

三、编码测试

my_heap.h

/*@breif implement max heap@author wanglu@time 20200627*/#includeconst int HEAPDEFAULTSIZE = 50;class MaxHeap{    public:        /*        @brief build heap        */        MaxHeap(int iSize = HEAPDEFAULTSIZE);        MaxHeap(int *pArry,int iSize);                /*        @brief insert into heap        */        bool Insert(const int iValue);        /*          @brief remvove max value         */        bool RemoveMaxValue(int &iValue);        /*        @brief heap sotr         */        bool HeapSort();            /*        @brief output heap elements        */        void PrintHeap();        /*        @brief get max heap capacity        */        int GetMaxCapacity(){            return m_iHeapMaxSize;           }        /*        @brief get current max heap size        */        int GetCurrentSize(){            return m_iCurrentHeapSize;            }    protected:        /*        @brief Adjust downward heap        */        void ShiftDowm(int start, int end);        /*        @brief Upward adjustment heap         */        void ShiftUp(int start);    private:        int *m_pHeap;//Max Heap object        int m_iCurrentHeapSize;//heap current heap element size        int m_iHeapMaxSize;//heap default max size};

my_heap.cpp

#include#include#includeusing namespace std;MaxHeap::MaxHeap(int iSize){    m_iHeapMaxSize =   iSize > HEAPDEFAULTSIZE ? iSize : HEAPDEFAULTSIZE;    m_iCurrentHeapSize = 0;    try    {        m_pHeap = new int[m_iHeapMaxSize];    }    catch(std::bad_alloc)    {        exit(-1);        }}MaxHeap::MaxHeap(int *pArry,int iSize){    m_iHeapMaxSize =   iSize > HEAPDEFAULTSIZE ? iSize : HEAPDEFAULTSIZE;    try    {        m_pHeap = new int[m_iHeapMaxSize];        }    catch(std::bad_alloc)    {        exit(-1);    }    m_iCurrentHeapSize = iSize;    for(int i = 0; i < iSize; i++)    {        m_pHeap[i] = pArry[i];       }        //craete max heap    int iCurerentPos = (m_iCurrentHeapSize - 2)/2;    while(iCurerentPos >= 0)    {        ShiftDowm(iCurerentPos, m_iHeapMaxSize-1);        iCurerentPos--;    }}void MaxHeap::ShiftDowm(int start, int end){    int iCur = start;    int iMaxChirdPos = 2*iCur + 1;    int iTempValue = m_pHeap[iCur];    while(iMaxChirdPos<= end)    {        if(iMaxChirdPos1])        {            iMaxChirdPos++;            }        if(m_pHeap[iCur] > m_pHeap[iMaxChirdPos])        {            break;        }        else        {            m_pHeap[iCur] = m_pHeap[iMaxChirdPos];            m_pHeap[iMaxChirdPos] = iTempValue;            iCur = iMaxChirdPos;            iMaxChirdPos = 2*iCur + 1;        }    }    m_pHeap[iCur] = iTempValue;}bool MaxHeap::Insert(const int iValue){    if(m_iCurrentHeapSize == m_iHeapMaxSize)    {        cout<<"[info] max heap full"<<endl;        return false;    }    m_pHeap[m_iCurrentHeapSize] = iValue;    ShiftUp(m_iCurrentHeapSize);    m_iCurrentHeapSize++;    return true;}void MaxHeap::ShiftUp(int start){    int iCur = start;    int iParentPos = (iCur -1)/2;    int itempValue = m_pHeap[iCur];        while(iCur > 0)    {        if(itempValue < m_pHeap[iParentPos])        {            break;        }        else        {                m_pHeap[iCur] = m_pHeap[iParentPos];                m_pHeap[iParentPos] = itempValue;                iCur = iParentPos;                iParentPos = (iCur -1)/2;        }    }    m_pHeap[iCur] = itempValue;}bool MaxHeap::RemoveMaxValue(int &iValue){    if(m_iCurrentHeapSize == 0)    {        cout<<"[info] max heap empty"<<endl;        return false;    }    iValue = m_pHeap[0];    m_pHeap[0] = m_pHeap[m_iCurrentHeapSize-1];    m_iCurrentHeapSize--;    ShiftDowm(0, m_iCurrentHeapSize-1);    return true;}void MaxHeap::PrintHeap(){    for(int i=0; i-1; i++)    {        cout<" ";        }    cout<-1]<<}bool MaxHeap::HeapSort(){     if(m_iCurrentHeapSize == 0)    {        cout<<"[info] max heap is empty"<<endl;        return false;        }       for(int i=m_iCurrentHeapSize-1; i>0; i--)    {        m_pHeap[0] = m_pHeap[0] + m_pHeap[i];        m_pHeap[i] = m_pHeap[0] - m_pHeap[i];        m_pHeap[0] = m_pHeap[0] - m_pHeap[i];        ShiftDowm(0, i - 1);    }    cout<<"[info] max heap sort content is: "<<endl;    PrintHeap();    return true;}

堆排序测试HeapSortTest.cpp

#include#includeusing namespace std;int main(){    int iArry[10] = {2,3,1,6,4,9,6,4,0,7};    MaxHeap MyHeap(iArry, 10);    MyHeap.PrintHeap();    MyHeap.HeapSort();    return 0;}

堆排序打印如下:

746f0f131e152570f3e91456541053d0.png

堆插入测试HeapInsertTest.cpp

#include#includeusing namespace std;int main(){    int iArry[10] = {2,3,1,6,4,9,6,4,0,7};    MaxHeap MyHeap(iArry, 10);    MyHeap.PrintHeap();    //MyHeap.HeapSort();    MyHeap.Insert(6);    MyHeap.Insert(8);    MyHeap.PrintHeap();    //int iValue;    //MyHeap.RemoveMaxValue(iValue);    //cout<    //MyHeap.PrintHeap();    return 0;}

堆插入打印如下:

1cfd49f01a18c7e44c48889a10aa1563.png

堆删除测试HeapRemoveTest.cpp

#include#includeusing namespace std;int main(){    int iArry[10] = {2,3,1,6,4,9,6,4,0,7};    MaxHeap MyHeap(iArry, 10);    //MyHeap.PrintHeap();    //MyHeap.HeapSort();    MyHeap.Insert(6);    MyHeap.Insert(8);    MyHeap.PrintHeap();    int iValue;    MyHeap.RemoveMaxValue(iValue);    cout<<"[info] max heap remove max value is "<endl;    MyHeap.PrintHeap();    return 0;}

堆删除最大元素打印如下:

956eba8d83e1f0192d86a93123f400c8.png

创建最大堆测试CreateHeapTest.cpp

#include#includeusing namespace std;int main(){    int iArry[10] = {2,3,1,6,4,9,6,4,0,7};    MaxHeap MyHeap(iArry, 10);    cout<<"[info] create max heap is:"<<endl;    MyHeap.PrintHeap();    cout<<"[info] max heap max capacity is "<endl;    cout<<"[info] max heap current size is "<endl;    return 0;}

创建最大堆打印如下:

943d907ca07e4d6a203c3b7a26796e89.png

四、时间复杂度分析

堆排序是一种选择排序,整体主要由构建初始堆+交换堆顶元素和末尾元素并重建堆两部分组成。其中构建初始堆经推导复杂度为O(n),在交换并重建堆的过程中,需交换n-1次,而重建堆的过程中,根据完全二叉树的性质,[log2(n-1),log2(n-2)...1]逐步递减,近似为nlogn。所以堆排序时间复杂度一般认为就是O(nlogn)级。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值