【算法】归并排序与快排

归并排序

归并排序是另一种不同的排序方法,因为归并排序使用了递归分治的思想,所以理解起来比较容易。其基本思想是,先递归划分子问题,然后合并结果。把待排序列看成由两个有序的子序列,然后合并两个子序列,然后把子序列看成由两个有序序列。。。。。倒着来看,其实就是先两两合并,然后四四合并。。。最终形成有序序列。空间复杂度为O(n),时间复杂度为O(nlogn)。
举个栗子:

 

#include <iostream>
#include <vector>
#include <algorithm>
#include <limits.h>
#include "Solution.h"

using namespace std;

void MergeArray(int array[], int start, int mid, int end, int temp[]) {
    int i = start;
    int j =  mid + 1;
    int k = 0;
    while (i <= mid && j <= end ) {
        if (array[i] < array[j]) {
            temp[k++] = array[i++];
        }else {
            temp[k++] = array[j++];
        }
    }
    while (i <= mid) {
        temp[k++] = array[i++];
    }
    while (j <= end) {
        temp[k++] = array[j++];
    }
    for (int i = 0; i < k; i ++) {
        array[start + i] = temp[i];
    }

}
// 归并排序,将数组前半部分后半部分分成最小单元,然后在合并
void MergeSort(int array[], int start,  int end, int temp[]) {
    if(start < end) {
        int mid = (start + end)/ 2;
        MergeSort(array, start, mid, temp);
        MergeSort(array, mid + 1, end, temp);
        MergeArray(array, start, mid, end, temp);
    }

}
// 在这里创建临时数组,节省内存开销,因为以后的temp都是在递归李使用的。
void MergeSortmain(int array[], int len) {
    int start = 0;
    int end = len - 1;
    int *temp = new int[len];
    MergeSort(array, start, end, temp);
}

void PrintArray(int array[], int len) {
    for (int i = 0 ; i < len; ++i) {
        cout << array[i] << " " ;

    }
    cout << endl;
}

int main() {

    int array[] = {3,5,3,6,7,3,7,8,1,2};

    MergeSortmain(array, 10);
    PrintArray(array, 10);
    
    return 0;
}

快速排序

快速排序一听名字就觉得很高端,在实际应用当中快速排序确实也是表现最好的排序算法。冒泡排序虽然高端,但其实其思想是来自冒泡排序,冒泡排序是通过相邻元素的比较和交换把最小的冒泡到最顶端,而快速排序是比较和交换小数和大数,这样一来不仅把小数冒泡到上面同时也把大数沉到下面。
举个栗子:对5,3,8,6,4这个无序序列进行快速排序,思路是右指针找比基准数小的,左指针找比基准数大的,交换之。
5,3,8,6,4 用5作为比较的基准,最终会把5小的移动到5的左边,比5大的移动到5的右边。
5,3,8,6,4 首先设置i,j两个指针分别指向两端,j指针先扫描(思考一下为什么?)4比5小停止。然后i扫描,8比5大停止。交换i,j位置。
5,3,4,6,8 然后j指针再扫描,这时j扫描4时两指针相遇。停止。然后交换4和基准数。
4,3,5,6,8 一次划分后达到了左边比5小,右边比5大的目的。之后对左右子序列递归排序,最终得到有序序列。
上面留下来了一个问题为什么一定要j指针先动呢?首先这也不是绝对的,这取决于基准数的位置,因为在最后两个指针相遇的时候,要交换基准数到相遇的位置。一般选取第一个数作为基准数,那么就是在左边,所以最后相遇的数要和基准数交换,那么相遇的数一定要比基准数小。所以j指针先移动才能先找到比基准数小的数。
快速排序是不稳定的,其时间平均时间复杂度是O(nlgn)。
实现代码:
#include <iostream>
#include <vector>
#include <algorithm>
#include <limits.h>
#include "Solution.h"

using namespace std;
int partition(vector<int> &vi, int low, int up)
{
    int pivot = vi[up];//选择最后一个元素作为比较元素
    int i = low-1;//这个慢速移动下标必须设定为比最小下表p小1,否则两个元素的序列比如2,1无法交换
    for (int j = low; j < up; j++)
    {
        if(vi[j] <= pivot)
        {
            i++;
            swap(vi[i], vi[j]);
        }
    }
    swap(vi[i+1], vi[up]);
    return i+1;
}

//C++'s array range should be [low, up], the same as [low, up+1)
void quickSort(vector<int> &vi, int low, int up)
{
    if(low < up)
    {
        int mid = partition(vi, low, up);
        //Watch out! The mid position is on the place, so we don't need to consider it again.
        //That's why below is mid-1, not mid! Otherwise it will occur overflow error!!!
        quickSort(vi, low, mid-1);
        quickSort(vi, mid+1, up);
    }
}

void qSort(vector<int> &vi)
{
    quickSort(vi, 0, vi.size()-1);
}

int main() {

    int a[] = {3,5,7,9,2,3,1,0,7,5,4};
    vector<int> va(a, a+11);

    cout<<"Before quicksort:\n";
    for(auto x:va)
        cout<<x<<" ";
    cout<<endl;

    qSort(va);

    cout<<"After quicksort:\n";
    for(auto x:va)
        cout<<x<<" ";
    cout<<endl;

    return 0;
}

 

posted @ 2019-02-26 17:37  华不摇曳  阅读(1671)  评论(0编辑  收藏  举报