排序算法总结与C++实现(冒泡、简单选择、直接插入、堆、归并、快速)

如有错误,请及时指出!谢谢!

几种排序算法时间复杂度和空间复杂度:

这里写图片描述

1.冒泡排序
如果是从小到大排序,从最后一个元素开始,依次与前一个元素比较,比前一个元素小则交换。这样小的元素就浮到上面了。第二轮再从最后一个元素开始。
C++实现如下:(优化版,引入了flag)

void bubbleSort(vector<int> &A) 
{  
    bool flag=1;
    for (int j = 0; j < A.size() && flag; j++) 
    {
        flag=0;  
        for (int i = A.size() - 1; i > j; i--)
        {
            //如果一直没有数据交换,则说明已经排好了,flag=0,在下一个for不执行直接结束
            //这样就不用再进行后面的for循环了
            if (A[i] < A[i - 1])
            {
                swap(A[i], A[i - 1]);
                flag=1;
            }
        }  
}  

时间复杂度:最好O(n),最坏O(n^2),平均O(n^2)
冒泡排序状态变化举个栗子:
待排:37462
第一轮:37426、37246、32746、23746
第二轮:23746、23476
第三轮:23467,排好了……

2.简单选择排序
第一轮:j=0,先设第一个元素下标为min,将第一个元素(下标为0)与第二个(下标为1)比较,将min设置为较小元素的下标;再讲min与第三个比较,将将min设置为较小元素的下标。一轮比较之后,获得第一个小元素的下标min,将j=0位置元素与min位置元素交换。
第二轮:从第二个元素开始,获得第二小的元素
……
C++实现如下:

void selectionSort(vector<int> &A) 
{  
    for (int j = 0;j < A.size();j++) 
    {  
        int min = j;  
        for (int i = j + 1;i < A.size();i++) 
        {  
            if (A[i] < A[min])  
                min = i;  
        }  
        if(j!=min) swap(A[j],A[min]);  
    }  
}  

时间复杂度:O(n^2)
简单选择排序状态变化举个栗子:
序号:01234
待排:37462
第一轮:初始化最小元素的下标min=0,经过比较min=4,即第4个元素2,交换第0个元素和第4个元素:27463
第二轮:初始化最小元素的下标min=1,经过比较min=4,即第4个元素3,交换第1个元素和第4个元素:23467
第三轮:初始化最小元素的下标min=2,经过比较min=2,不改变……实际上已经排好了……

3.直接插入排序
C++实现如下:

void insertionSort(vector<int> &A) 
{  
    for (int j = 1;j < A.size();j++) 
    {  
        int key = A[j];  
        int i=j-1;  
        while(i>=0&&key < A[i])
        {  
            A[i + 1] = A[i];  //向后移一位
            i--;  
        }  
        A[i + 1] = key;  
    }  
} 

最好O(n),最坏O(n^2),平均O(n^2)
将小的元素不断往前移动,把比它大的元素不断往后移动。
直接插入排序状态变化举个栗子:
序号:01234
待排:53462
第一轮:j=1,key=A[1]=3,i=0 , key=3<A[0]=5 , A[1]=5,i=-1 ,跳出while,A[0]=3→35462
第二轮:j=2 , key=A[2]=4 , i=1 , key=4<A[1]=5, A[2]=5,i=0,跳出while,A[1]=4→34562
………………

4.堆排序
一些基本概念和定义:
大顶堆:完全二叉树,每个结点的值都大于其左右孩子的值;即A[i]≥A[2i],A[i]≥A[2i+1]
小顶堆:完全二叉树,每个结点的值都小于其左右孩子的值;即A[i]≤A[2i],A[i]≤A[2i+1]
完全二叉树,如果用一个数组存储(下标从0开始)。若下标为i,则双亲下标是【((i-1)/2)向下取整】;同理,下标为i的结点,它的左右孩子下标为2i+1和2i+2。

步骤:
(1)构造大顶堆
(2)堆顶移走(其实是与末尾元素交换)
(3)重复(1)

堆排序主函数:

基本步骤:
(1)从最后一个非叶子节点开始,对每个非叶子结点进行堆排序
(2)将堆顶与最后一个元素交换
(3)除去最后一个元素,重新构建大顶堆
void HeapSort(vector<int>& A)
 {  
    int heap_size = A.size() - 1;  
    for (int i = (heap_size-1) / 2;i >= 0;i--)  
    //因为所有叶子结点已经是合法的大顶堆了,所以对所有非叶子结点构造大顶堆
    {  
        maxHeapify(A, heap_size,i);         //从下标为i到heap_size元素构造大顶堆
    }  
    while (heap_size >= 1)
     {  
        swap(A[heap_size], A[0]);    //交换堆顶与末尾元素
        heap_size--;  
        maxHeapify(A, heap_size, 0);  
    }  
}  

下面是构造大顶堆函数maxHeapify()的实现(递归和非递归):

//递归
//从下标为i到heap_size元素构造大顶堆
void maxHeapify(vector<int>& A, int heap_size, int i) 
{  
    int largest = i * 2 + 1;  
    if (largest > heap_size) return;  
    if (largest<heap_size&&A[largest + 1]>A[largest]) 
    {  
        largest++;  //选出较大的孩子用于下一步与父节点交换值
    }  
    if (A[i]<A[largest]) 
    {   //如果自己的孩子比自己大则交换两者的值    
        swap(A[i], A[largest]); 
        maxHeapify(A, heap_size, largest);  
    }  
}  
//非递归
void maxHeapify(vector<int>& A, int heap_size, int i) 
{  
    int largest;  
    while (i * 2 < heap_size)
     {  
        largest = i * 2 + 1;  
        if (largest<heap_size&&A[largest + 1]>A[largest]) 
        {  
            largest++;  
        }  
        if (A[i]<A[largest]) 
        {    //如果自己的孩子比自己大则交换两者的值    
            swap(A[i], A[largest]);  
        }  
        else break; //如果不比自己大则不交换    
        i = largest;  
    }  
}  

由于每次重新恢复堆的时间复杂度为O(logN),共N - 1次重新恢复堆操作,再加上前面建立堆时N / 2次向下调整,每次调整时间复杂度也为O(logN)。

5.两路归并排序
两两组合,排序;归并,排序……

//递归的方法
void MergeSort(vector<int>& A)
{
    MSort(A,0,A.size()-1);
}
void MSort(vector<int>& A,int low,int high)
{
    if(low<high)
    {
        int mid=(low+high)/2;
        MSort(A,low,mid);
        Msort(A,mid+1,high);
        Merge(A,low,mid,high);   //排好序顺便合并
    }
}
void Merge(vector<int>&A, int p, int q, int r)  
{  
    vector<int> L(A.begin() + p, A.begin() + q+1);   //A[p......q]  
    vector<int> R(A.begin() + q+1, A.begin() + r+1); //A[q+1......r]  
    L.push_back(INT_MAX);  
    R.push_back(INT_MAX);  
    for (int i = 0, j = 0, k = p; k <= r; k++)  
    {  
        if (L[i] <= R[j]) 
            A[k] = L[i++];  
        else A[k] = R[j++];    //从小到大排序
    }  
}  
// 非递归
// merge_sort: 非递归实现 --迭代  
// 非递归思想: 将数组中的相邻元素两两配对。用merge函数将他们排序,  
// 构成n/2组长度为2的排序好的子数组段,然后再将他们排序成长度为4的子数组段,  
// 如此继续下去,直至整个数组排好序。    
// merge_sort(): 非递归实现-自底向上  
// 将原数组划分为left[min...max] 和 right[min...max]两部分  
void merge_sort(vector<int> &list)  
{  
    int length = list.size();  
    vector<int> temp(length, 0);  
    int i, left_min, left_max, right_min, right_max, next;  
    for (i = 1; i < length; i *= 2) // i为步长,1,2,4,8……,两两合并
    {  
        for (left_min = 0; left_min < length - i; left_min = right_max)  
        {  
            right_min = left_max = left_min + i;  
            right_max = left_max + i;  
            if (right_max > length)  
                right_max = length;  
            next = 0;  
            while (left_min < left_max && right_min < right_max)  
                temp[next++] = list[left_min] > list[right_min] ? list[right_min++] : list[left_min++];  

            while (left_min < left_max)  
                list[--right_min] = list[--left_max];  

            while (next > 0)  
                list[--right_min] = temp[--next];  
        }  
    }  
}  

6.快速排序
通过一趟排序将待排记录分割成独立的两部分,一部分关键字均比另一部分关键字小;再分别对这两部分进行相同的操作。

void QuickSort(vector<int>& A)
{
    QSort(A,0,A.size()-1);
}
//递归
void QSort(vector<int>& A, int p, int r)  
{  
    if (p<r)  
    {  
        int q = partition(A,p,r);  
        QSort(A, p, q-1);  
        QSort(A, q+1, r);  
    }  
}  

其中partition的实现如下:

int partition(vector<int>& A, int low, int high) 
{  
    int key = A[low];
    while(low<high)
    {
        while(low<high && A[high]>=key)
        {
            high--;
        }
        swap(A[low],A[high]);
        while(low<high && A[low]<=key)
        {
            low++;
        }
        swap(A[low],A[high]);
    }  
    return low;
}  
//非递归的方法
//数据规模很大时,递归的算法很容易导致栈溢出,改为非递归,模拟栈操作
//最大长度为n,每次压栈时先压长度较大的,此时栈深度为logn。  
template<typename Comparable>  
void quicksort2(vector<Comparable> &vec,int low,int high){  
    stack<int> st;  
    if(low<high){  
        int mid=partition(vec,low,high);  
        if(low<mid-1){  
            st.push(low);  
            st.push(mid-1);  
        }  
        if(mid+1<high){  
            st.push(mid+1);  
            st.push(high);  
        }  
        //其实就是用栈保存每一个待排序子串的首尾元素下标,下一次while循环时取出这个范围
        //对这段子序列进行partition操作  
        while(!st.empty()){  
            int q=st.top();  
            st.pop();  
            int p=st.top();  
            st.pop();  
            mid=partition(vec,p,q);  
            if(p<mid-1){  
                st.push(p);  
                st.push(mid-1);  
            }  
            if(mid+1<q){  
                st.push(mid+1);  
                st.push(q);  
            }         
        }  
    }  
}  

快速排序状态变化如下:
序号:012345678
待排:519374862
第一轮:取key=5,219374865,215374869,214375869,214357869,以5为中心分成两半:2143和7869分别再进行快排。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值