常用的十种排序

1、直接插入排序

  最好O(n),最坏时间O(n^2),较稳定

  基本思想:在要排序的一组数中,假设前面(n-1) [n>=2] 个数已经是排好顺序的,现在要把第n个数插到前面的有序数中,使得这n个数也是排好顺序的。如此反复循环,直到全部排好顺序。

#include <iostream>
using namespace std;

int main()
{
    int a[]={5,2,6,3,9};
    int len=sizeof(a)/sizeof(a[0]);
    
    for(int i=1;i<len;++i)
    {
        if(a[i]<a[i-1])
        {
            int t=a[i],j;
            for(j=i-1;j>=0&&a[j]>t;--j)
                a[j+1]=a[j];
                
            a[j+1]=t;
        }
    }
    
    for(int i=0;i<len;++i)
        cout<<a[i]<<" ";
    cout<<endl;
    
    return 0;
}

2、希尔排序

  时间复杂度:O(n*log(n)),不稳定

  由于多次插入排序,我们知道一次插入排序是稳定的,不会改变相同元素的相对顺序,但在不同的插入排序过程中,相同的元素可能在各自的插入排序中移动,最后其稳定性就会被打乱,所以shell排序是不稳定的。

  直接插入排序的的步长是为1的,而希尔排序向前比较是以一定步长向前比较。

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main()
{
    vector<int> arr{5,2,6,3,9};
    for(int gap=arr.size()/2;gap>0;gap/=2)//步长
    {
        for(int i=gap;i<arr.size();++i)//和直接插入排序一样,只不过是每次向前移动k步比较而不是移动一步比较
        {
            int tmp=arr[i];
            int j=i-gap;
            while(j>=0&&arr[j]>tmp)
            {
                arr[j+1]=arr[j];
                j-=gap;
            }
            arr[j+gap]=tmp;
        }
    }
    for_each(arr.begin(),arr.end(),[](int i)->void{cout<<i<<" ";});
    cout<<endl;
    return 0;
}

3、简单选择排序

  最好:O(n^2),最坏:O(n^2),不稳定  

  基本思想:在要排序的一组数中,选出最小的一个数与第一个位置的数交换;然后在剩下的数当中再找最小的与第二个位置的数交换,如此循环到倒数第二个数和最后一个数比较为止。

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main()
{
    vector<int> arr{5,2,6,3,9};
    for(int i=0;i<arr.size()-1;++i)
    {
        int minIndex=i;
        for(int j=i+1;j<arr.size();++j)
        {
            if(arr[j]<arr[minIndex])
                minIndex=j;
        }
        if(minIndex!=i)
            swap(arr[minIndex],arr[i]);
    }
    for_each(arr.begin(),arr.end(),[](int i)->void{cout<<i<<" ";});
    cout<<endl;
    return 0;
}

4、堆排序

  最好:nlog(n),最坏:nlog(n),不稳定

  堆排序的基本思想是:将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了

初建堆:

  将一个任意序列看成是对应的完全二叉树,由于叶结点可以视为单元素的堆,因而可以反复利用上述调整的算法,自底向上逐层把所有子树调整为堆,直到整个完全二叉树为堆。最后一个非叶节点位于n/2(向下取整)个位置,n为二叉树结点数目,因此,筛选从n/2(向下取整)个结点开始,逐层向上倒退,知道根节点。

调整堆:

         这是为了保持堆的特性而做的一个操作。对某一个节点为根的子树做堆调整,其实就是将该根节点进行“下沉”操作(具体是通过和子节点交换完成的),一直下沉到合适的位置,使得刚才的子树满足堆的性质。

  1. 在对应的数组元素A[i], 左孩子A[LEFT(i)], 和右孩子A[RIGHT(i)]中找到最大的那一个,将其下标存储在largest中。
  2. 如果A[i]已经就是最大的元素,则程序直接结束。
  3. 否则,i的某个子结点为最大的元素,将A[largest]与A[i]交换。
  4. 再从交换的子节点开始,重复1,2,3步,直至叶子节点,算完成一次堆调整。

       这里需要提一下的是,一般做一次堆调整的时间复杂度为log(n)。

堆排序:

  数组储存成堆的形式之后,第一次将A[0]与A[n - 1]交换,再对A[0…n-2]重新恢复堆。第二次将A[0]与A[n-2]交换,再对A[0…n-3]重新恢复堆,重复这样的操作直到A[0]与A[1]交换。由于每次都是将最小的数据并入到后面的有序区间,故操作完成后整个数组就有序了。

#include <iostream>
#include <vector>
using namespace std;

/*
对于具有n个节点的完全二叉树,如果按照从上(根节点)到下(叶节点)和从左到右的顺序对二叉树中的所有节点从0开始到n-1进行编号,则对于任意的下标为k的节点,有:
如果k=0,则它是根节点,它没有父节点;如果k>0,则它的父节点的下标为[(i-1)/2];
如果2k+1 <= n-1,则下标为k的节点的左子结点的下标为2k+1;否则,下标为k的节点没有左子结点.
如果2k+2 <= n-1,则下标为k的节点的右子节点的下标为2k+2;否则,下标为k的节点没有右子节点
*/
class HeapSort
{
private:
    void adjustHeap(vector<int>& arr,int start,int end)
    {
        //2.调整堆:已知arr[start...end]中除关键字arr[start]之外均满足堆的定义
        //本函数调整arr[start]的关键字,使arr[start,end]成为一个大根堆
        int res=arr[start];
        for(int j=start*2+1;j<=end;j=j*2+1)
        {
            if(j<end&&arr[j]<arr[j+1])//找出左右子树中最大的
                ++j;
            if(res>arr[j])
                break;
            arr[start]=arr[j];
            start=j;//因为j和start交换了,所以要更新start的值;向下调整
        }
        arr[start]=res;
    }
public:
    void heapSort(vector<int>& arr)
    {
        int len=arr.size();
        //1.初建堆
        for(int i=len/2-1;i>=0;--i)//可以把数组中len/2-1后的元素看做单根堆,均满足堆的定义;所以向上调整
            adjustHeap(arr,i,len-1);

        //堆排序
        for(int i=len-1;i>0;--i)
        {
            swap(arr[0],arr[i]);
            adjustHeap(arr,0,i-1);
        }
        return ;
    }
};
int main()
{
    vector<int> arr{54,35,48,36,27,12,44,44,8,14,26,17,28};
    HeapSort hs;
    hs.heapSort(arr);
    for(auto i:arr)
        cout<<i<<" ";
    cout<<endl;
    return 0;
}
常用的十种排序
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

class Heap_sort
{
    public:
        void build_heap(vector<int> &v);
        void adjust_heap(vector<int> &v,vector<int>::iterator i,vector<int>::iterator dis);
        void sort_heap(vector<int> &v);
        vector<int>::iterator left_child(vector<int> &v,vector<int>::iterator i);
        vector<int>::iterator right_child(vector<int> &v,vector<int>::iterator i);
};
//获取左节点下标 
vector<int>::iterator Heap_sort::left_child(vector<int> &v,vector<int>::iterator i)
{
    advance(i,distance(v.begin(),i));
    return i;
}
//获取右节点下标 
vector<int>::iterator Heap_sort::right_child(vector<int> &v,vector<int>::iterator i)
{
    advance(i,distance(v.begin(),i)+1);
    return i;
}
//调整堆 
void Heap_sort::adjust_heap(vector<int> &v,vector<int>::iterator i,vector<int>::iterator dis)
{
    vector<int>::iterator l=left_child(v,i);
    vector<int>::iterator r=right_child(v,i);
    vector<int>::iterator largest=v.begin();
    
    if((*i)<(*l)&&(l<dis))
        largest=l;
    else
        largest=i;
    
    if((*largest)<(*r)&&(r<dis))
        largest=r;
    //建的是大顶锥    
    if(largest!=i)
    {
        swap(*largest,*i);
        adjust_heap(v,largest,dis);
    }
}
//初建堆 
void Heap_sort::build_heap(vector<int> &v)
{
    for(vector<int>::iterator i=v.begin()+v.size()/2-1;i!=--v.begin();--i)
        adjust_heap(v,i,v.end());
}
//堆排序 
void Heap_sort::sort_heap(vector<int> &v)
{
    build_heap(v);//堆排序之前应该先初建堆 
    for(auto i=--v.end();i!=--v.begin();--i)
    {
        swap(*i,*(v.begin()));//交换第一个元素(最大的)与最后一个元素 
        adjust_heap(v,v.begin(),i);//交换完之后第一个元素不是最大的,要调整堆使第一个元素最大 
    }
}

int main()
{
    vector<int> v{70,30,40,10,80,20,90,100,75,60,45};//看做完全二叉树 
    
    Heap_sort hs;//创建堆排序的对象 
    hs.sort_heap(v);
    
    for(auto i:v)
        cout<<i<<" ";
    cout<<endl;
    return 0;
}
View Code

相关文章:

  • 2021-09-28
  • 2022-01-08
  • 2021-10-27
  • 2021-07-25
  • 2022-12-23
  • 2021-06-25
  • 2021-05-18
猜你喜欢
  • 2021-11-19
  • 2021-11-23
  • 2021-12-20
  • 2021-12-30
  • 2021-07-26
  • 2021-08-05
  • 2021-11-02
相关资源
相似解决方案