C++实现几种常用的时间复杂度为O(nlogn)的排序方法:归并排序、快速排序、堆排序、希尔排序

(一)归并排序

采用分而治之的策略,即先将数组拆分,在进行排序后重组。

C++代码如下:

Class MergeSort {
    
public:
    int* mergeSort(int* A, int n) {
        int left = 0, right = n-1;
        ArraySort(A, left, right);
        return A;
    }
    void ArraySort(int* A, int left, int right) //数组拆分
    {
        if(left<right)
        {
            int mid = left + (right-left)/2;    // mid = (right+left)/2这种方法不是很推荐,因为可能超内存范围
            ArraySort(A, left, mid);
            ArraySort(A, mid+1, right);
            if(A[mid]>A[mid+1]) ArrayMerge(A, left, mid, right);
        }
    }
    
    void ArrayMerge(int* A, int left, int mid, int right) //数组合并
    {
        int temp[right-left+1];
        int i = left, j = mid+1, k = 0;
        while(i<=mid && j<=right)
        {
            if(A[i]<A[j]) temp[k++] = A[i++];
            else temp[k++] = A[j++];
        }
        while(i<=mid) temp[k++] = A[i++];
        while(j<=right) temp[k++] = A[j++];
        
        for(i=0; i<k; ++i) A[left+i] = temp[i];
        
    }
};

(二)快速排序

随机选取一个数做基准,数组中小于该基准的数放在基准的前面,大于该基准的放在后面。

C++代码如下:

class QuickSort {
public:
    int* quickSort(int* A, int n) {
        qSort(A, 0, n-1);
        return A;        
    }
    void qSort(int* A, int left, int right)
    {
        if(left < right)
        {
            int par = Partition(A, left, right);
            qSort(A, left, par-1);
            qSort(A, par+1, right);
        }   
    }
    int Partition(int* A, int left, int right){
        int j = left-1, m = A[right];
        for(int i=left; i<right; i++)
        {
            if(A[i]<=m) {
                 j++;
                if(i!=j) swap(A[j], A[i]); 
            }
        }
        swap(A[right], A[j+1]);
        return j+1;
      }
};

(三)堆排序

利用二叉树结构的性质,可构造大(小)顶堆。

大(小)顶堆:每个父节点值均大于等于(小于等于)左右孩子节点的值, A[i]>=A[2i+1] && A[i]>=A[2i+2];

常用于取前N个最大(小)值。

大顶堆的基本思想:

1. 将初始待排序列A[0,...n-1]构建大顶堆

2. 将堆顶元素A[0]与A[n-1]交换,得到无序序列A[0,..,n-2]和有序序列A[n-1],调整序列为大顶堆

3. 重复步骤2,直到有序元素个数为n-1

class HeapSort {
public:
    int* heapSort(int* A, int n) {
        // write code here        
        for(int i = n/2-1; i>=0; i--) //建立大顶堆,从最底层的父节点开始
            heapAdjust(A, i, n);
        for(int i = n-1; i>=0; i--)
        {
            swap(A[i], A[0]);
            heapAdjust(A, 0, i);
        }
        return A;
    }
    
    void heapAdjust(int* A, int p, int len)
    {
        int curParent = A[p];
        int child = 2*p + 1;
        while(child < len)
        {
            if(child+1<len && A[child]<A[child+1]) child++;
            if(A[child] > curParent)   {
                A[p] = A[child];   //如果其孩子中有大的,会上移,curParent还要继续下移。
                p = child;
                child = 2*p + 1;
             } 
            else break;
            
        }
        A[p] = curParent;
    }
};

参考:https://segmentfault.com/a/1190000002466215

(四)希尔排序

插入排序的改进版,与插入排序的不同之处为,可逐渐减小步长进行数据对比较,直至步长为1。相较于插入排序,效率更高。

class ShellSort {
public:
    int* shellSort(int* A, int n) {
        for(int gap = n/2; gap>0; gap /= 2)
        {
            for(int i=0; i < gap; i++)
            {
                for(int j=i+gap; j<n; j+=gap)
                if(A[j-gap]>A[j])
                {
                    int temp = A[j];
                    int k = j-gap;
                    while(k>=0 && A[k]>temp){
                        A[k+gap] = A[k];
                        k = k-gap;
                    }
                    A[k+gap] = temp;
                }
            }
        }
        return A;
    }
};


猜你喜欢

转载自blog.csdn.net/qq_24153697/article/details/79410973