常见排序算法(二)

 二路归并排序


归并排序是一种基于“归并”思想的排序方法。二路归并的原理是,将序列两两分组,将序列归并,组内单独排序,然后以此类推,直到只剩下一个组为止。

举例:序列{66,12,33,57,64,27,18}进行二路归并排序。
第一趟:两两分组,得到四组:{66,12},{33,57},{64,27},{18},组内单独排序得到{{12,66},{33,57},{27,64},{18}};

第二趟:四个组继续两两分组,得到两组:{12,66,33,57},{27,64,18},组内单独排序得到{{12,33,57,66},{18,27,64}};

第三趟:两个组继续两两分组得到一组:{12,33,57,66,18,27,64},组内单独排序得到{12,18,27,33,57,64,66},排序结束。

c++代码实现:
①递归
思想:反复将当前区间`[left,right]`分成两半,对两个子区间`[left,mid]` 和`[mid+1,right]`分别递归进行归并排序,然后将两个已经有序的子区间合并为有序序列即可。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 100;

// A :[L1,R1]  [L2,R2]
void merge(int A[], int L1, int R1, int L2, int R2) {
  int i = L1, j = L2;
  int temp[maxn], index = 0;
  while (i <= R1 && j <= R2) {
    if (A[i] <= A[j]) {
      temp[index++] = A[i++];
    } else {
      temp[index++] = A[j++];
    }
  }
  while (i <= R1) {
    temp[index++] = A[i++];
  }
  while (j <= R2) {
    temp[index++] = A[j++];
  }
  for (i = 0; i < index; i++) {
    A[L1 + i] = temp[i];
  }
}

void merge_sort(int A[], int left, int right) {
  if (left < right) {
    int mid = (left + mid) / 2;
    merge_sort(A, left, mid);
    merge_sort(A, mid + 1, right);
    merge(A, left, mid, mid + 1, right);
  }
}


②非递归
思想:我们观察到,每次分组时组内元素的个数上限是2的幂次。所以我们可以令步长step的初值为2,然后数组中每step个元素作为一组,将其内部进行排序(即把左`step/2`个元素与右`step/2`个元素合并,而若元素个数不超过`step/2`,则不操作);再令step乘2,重复上述操作,直到`step/2`超过元素个数n。

#include <bits/stdc++.h>
using namespace std;

void merge(int A[], int L1, int R1, int L2, int R2) {
  int i = L1, j = L2;
  int temp[maxn], index = 0;
  while (i <= R1 && j <= R2) {
    if (A[i] <= A[j]) {
      temp[index++] = A[i++];
    } else {
      temp[index++] = A[j++];
    }
  }
  while (i <= R1) {
    temp[index++] = A[i++];
  }
  while (j <= R2) {
    temp[index++] = A[j++];
  }
  for (i = 0; i < index; i++) {
    A[L1 + i] = temp[i];
  }
}

void merge_sort(int A[]) {
  for (int step = 2; step / 2 <= n; step *= 2) {
    for (int i = 1; i <= n; i += step) {
      int mid = i + step / 2 - 1;

      if (mid + 1 <= n) {
        merge(A, i, mid, mid + 1, min(i + step - 1, n));
      }
    }
  }
}

 快速排序
 

对于序列A[1]、A[2]、...、A[n],调整序列中元素的位置,使得A[1]的左侧所有元素都不超过A[1],右侧所有的元素都大于A[1]。时间复杂度为O(nlogn)。

思想

①首先将A[1]存入一个临时变量temp,并令两个下标分别指向序列首尾,即left=1,right=n;

②只要right指向的元素A[right]大于temp,就将right不断左移,直到A[right]≤temp时,将元素A[right]挪到left指向的元素A[left]处;

③只要left指向的元素A[left]不超过temp,就将left不断右移,直到A[left]>temp时,将A[left]挪到right指向的元素A[right ]处;

④重复②③,直到left和right相遇,把temp放到相遇的地方。

举个例子说明:序列A={35,18,16,72,24,65,12,88,46,28,55}

首先取得A[1]=35,使得左边的所有元素不大于35,右边的所有元素都大于35。

①将A[1]=35存入临时变量temp,并令下标指向序列首尾(left=1,right=11)

                      temp  35

                                                        1         2         3         4        5       6        7          8        9        10        11

3518167224651288462855

                                                        left                                                                                                 right

②只要A[right]>35,就把right不断左移,直到right=10时满足A[right]=28<35,right停止左移,之后把A[right]移到A[left]处。

   temp  35

                                                        1         2         3         4        5       6        7          8        9        10        11

281816722465128846   55

                                                        left                                                                                       right

③只要A[left]≤35,就把left不断右移,直到left=4时满足A[left]=72>35,left停止右移,之后把A[left]移到A[right]处。

   temp  35

                                                      1         2         3         4        5       6        7          8        9        10        11

281816  24651288467255

                                                                                       left                                                       right

④只要A[right]>35,就把right不断左移,直到right=7时满足A[right]=12<35,right停止左移,之后把A[right]移到A[left]处。

temp  35

                                                      1         2         3         4        5       6        7          8        9        10        11

281816 12 246588467255

                                                                                       left                          right

⑤只要A[left]≤35,就把left不断右移,直到left=6时满足A[left]=65>35,left停止右移,之后把A[left]移到A[right]处。

temp  35

                                                         1         2         3         4        5       6        7        8        9        10        11

281816 12 24     6588467255

                                                                                                            left     right

⑥只要A[right]>35,就把right不断左移,left与right在A[6]处相遇,之后把temp=35移到A[6]处。

temp  35

                                                         1         2         3         4        5       6        7        8        9        10        11

281816 12 24  35  6588467255

                                                                                                         left  right

一次排序结束,随后对左边右边元素递归。

C++代码:

#include <bits/stdc++.h>
using namespace std;

int Partition(int A[], int left, int right) {
  int temp = A[left];
  while (left < right) {
    while (left < right && A[right] > temp) {
      right--;
    }
    A[left] = A[right];
    while (left < right && A[left] > temp) {
      left++;
    }
    A[right] = A[left];
  }
  A[left] = temp;
  return left;
}

void quick_sort(int A[], int left, int right) {
  if (left < right) {
    int pos = Partition(A, left, right);
    quick_sort(A, left, pos - 1);
    quick_sort(A, pos + 1, right);
  }
}

当然,需要考虑的问题是temp的选择,不能每次都选择第一个元素,这样当元素排列比较随机时效率较高,当元素有序时,会达到最坏时间复杂度O(n^2)。应该随机选择元素作为temp!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

eynoZzzzc

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值