(数据结构)七种常用的排序算法分析及代码实现(下)——快速排序及归并排序

快速排序

快速排序算法被列为20世纪十大算法之一。
其相当于冒泡排序的升级,只不过它的实现,增大了记录的比较和移动的距离,将关键字较大的记录从前面直接移动到后面,关键字较小的记录从后面直接移动到前面。从而减少了比较次数和移动交换次数。

思路分析

  • 单趟快排,将区间分成较大和较小两部分。
  • 利用迭代循环,不断细化区间。
  • 对区间进行合并,从而实现整体有序。
    这里写图片描述

代码实现

在这里,将采用三种方法来实现快速排序(递归实现)。
三种方法虽说实现的部分代码不同,但代码的内核是相同的,都是不断细分区间再合并。
同时,也将利用非递归的方法来实现快速排序。

方法一:左右指针法
这里写图片描述

//左右指针法
int PartSort1(int* a, size_t left, size_t right)
{
    int begin = left;
    int end = right;
    int& key = a[end];

    while (begin < end)
    {
        while (begin < end
            && a[begin] <= key)
            ++begin;
        while (begin < end
            && a[end] >= key)
            --end;

        if (begin < end)
        {
            swap(a[begin],a[end]);
        }
    }

    swap(a[begin],key);

    return begin;
}

//快速排序
void QuickSort(int* a, int left, int right)
{
    assert(a);
    if (left >= right)
        return;

    int div = PartSort1(a, left, right);

    QuickSort(a, left, div - 1);
    QuickSort(a, div + 1, right);
}

针对有序数组的优化

//三数取中
int GetMidIndex(int* a, size_t left, size_t right)
{
    int mid = left + ((right - left) >> 1);
    if (a[left] < a[mid])
    {
        if (a[mid] < a[right])
            return mid;
        else if (a[left] > a[right])
            return left;
        else
            return right;
    }

    else//left >= mid
    {
        if (a[mid] > a[right])
            return  mid;
        else if (a[left] < a[right])
            return left;
        else
            return right;
    }
}


//左右指针法
int PartSort1(int* a, size_t left, size_t right)
{
    int begin = left;
    int end = right;

    int mid = GetMidIndex(a, left, right);
    swap(a[mid], a[end]);

    int& key = a[end];

    while (begin < end)
    {
        while (begin < end
            && a[begin] <= key)
            ++begin;
        while (begin < end
            && a[end] >= key)
            --end;

        if (begin < end)
        {
            swap(a[begin], a[end]);
        }
    }

    swap(a[begin], key);

    return begin;
}

//快速排序
void QuickSort(int* a, int left, int right)
{
    assert(a);
    if (left >= right)
        return;

    int div = PartSort1(a, left, right);

    QuickSort(a, left, div - 1);
    QuickSort(a, div + 1, right);
}

方法二:挖坑法
这里写图片描述
这里写图片描述
这里写图片描述

//挖坑法
int PartSort2(int* a, int left, int right)
{
    assert(a);
    int begin = left;
    int end = right;
    int key = a[end];

    while (begin < end)
    {
        while (begin < end
            && a[begin] <= key)
            ++begin;
        a[end] = a[begin];

        while (begin < end
            && a[end] >= key)
            --end;
        a[begin] = a[end];
    }

    a[begin] = key;

    return begin;
}

//快速排序
void QuickSort(int* a, int left, int right)
{
    assert(a);
    if (left >= right)
        return;

    int div = PartSort2(a, left, right);

    QuickSort(a, left, div - 1);
    QuickSort(a, div + 1, right);
}

方法三:前后指针法
这里写图片描述
这里写图片描述

//前后指针法
int PartSort3(int* a, int left, int right)
{
    assert(a);
    int cur = left;
    int prev = left - 1;

    while (cur < right)
    {
        if (a[cur] < a[right]
            && ++prev != cur)
            swap(a[prev], a[cur]);
        cur++;
    }
    swap(a[++prev], a[right]);

    return prev;
}

//快速排序
void QuickSort(int* a, int left, int right)
{
    assert(a);
    if (left >= right)
        return;

    int div = PartSort3(a, left, right);

    QuickSort(a, left, div - 1);
    QuickSort(a, div + 1, right);
}

方法四:非递归实现

//非递归实现快速排序
void QuickSortNonR(int* a, int left, int right)
{
    stack<int> s;
    s.push(right);
    s.push(left);

    while (!s.empty())
    {
        int begin = s.top();
        s.pop();
        int end = s.top();
        s.pop();

        int div = PartSort1(a, begin, end);
        if (begin < div - 1)
        {
            s.push(div - 1);
            s.push(begin);
        }
        if (div + 1 < end)
        {
            s.push(end);
            s.push(div + 1);
        }
    }
}

时间复杂度

  • 因为会对数组进行不对的分裂再合并
  • 所以会有平均时间复杂度为O(N*lgN)
  • 当数组有序时,会出现最坏时间复杂度0(N^2)
  • 但因为三数取中法的引进,即针对有序数列进行了优化,所以我们在这里不看最坏时间复杂度
  • 因此,快速排序的时间复杂度我们认为是0(N*lgN)

归并排序

利用归并的思想实现的排序方法。
假设初始序列含有n个记录,则可以看成是n个有序的子序列,每个子序列的长度为1,然后两两归并,得到[n/2]个长度为2或1的有序子序列;再继续两两归并,重复,直至得到一个长度为n 的有序序列为止。

思路分析

1、根据递归&迭代的思想来实现此排序
2、将区间分成两个小区间,对两个区间进行重新排序,比较排序后的结果放到一个新的数组里面。
3、小区间不断合并,不断合并,最后得到的即为有序序列。
这里写图片描述

代码实现

//归并排序
void _MergeSort(int* a, int left, int right, int* tmp)
{
    assert(a);
    if (left >= right)
        return;

    int mid = left + ((right - left) >>1);

    _MergeSort(a, left, mid, tmp);
    _MergeSort(a, mid + 1, right, tmp);

    int begin1 = left, end1 = mid;
    int begin2 = mid + 1, end2 = right;
    int index = left;

    while (begin1 <= end1 && begin2 <= end2)
    {
        if (a[begin1]<a[begin2])
        {
            tmp[index++] = a[begin1++];
        }
        else
        {
            tmp[index++] = a[begin2++];
        }
    }
    while (begin1 <= end1)
    {
        tmp[index++] = a[begin1++];
    }
    while (begin2 <= end2)
    {
        tmp[index++] = a[begin2++];
    }

    memcpy(a+left, tmp + left, sizeof(int)*sizeof(right - left + 1));

}


void MergeSort(int* a,size_t n)
{
    assert(a);
    int* tmp = new int[n];
    _MergeSort(a, 0, n, tmp);
    //delete[] tmp;
}

时间复杂度

时间复杂度为O(nlgN),是一个稳定的算法

阅读更多 登录后自动展开
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页