手撕排序算法(下)

31 篇文章 2 订阅
21 篇文章 0 订阅

排序算法(下)


img

快速排序

快排的思想是这样的:如果要排序数组中下标从 p 到 r 之间的一组数据,我们选择 p 到 r 之间的任意一个数据作为 pivot(分区点)。我们遍历 p 到 r 之间的数据,将小于 pivot 的放到左边,将大于 pivot 的放到右边,将 pivot 放到中间。经过这一步骤之后,数组 p 到 r 之间的数据就被分成了三个部分,前面 p 到 q-1 之间都是小于 pivot 的,中间是 pivot,后面的 q+1 到 r 之间是大于 pivot 的。

1. 找到分界点,两边进行排序
2. 原地排序

参考链接

这时候分析一下快速排序算法

快速排序是原地排序吗

是的,没有借助额外的空间,所以空间复杂度是0(1),是原地排序算法,所以占用空间小,但是缺点就是不稳定

快速排序是稳定的排序算法吗

因为分区的过程涉及交换操作,如果数组中有两个相同的元素,比如序列 6,8,7,6,3,5,9,4,在经过第一次分区操作之后,两个 6 的相对先后顺序就会改变。所以,快速排序并不是一个稳定的排序算法。

快速排序算法的时间复杂度度

  • 最好的情况
原本顺序就是有序的,都要遍历一遍不进行交换0(nlogn)
  • 最坏的情况
原本是倒叙的,那么就要遍历n次里面也要交换n次时间复杂度是0(n*n)

算法实现

#include <iostream>
#include <vector>

int quicksort(int p, int q, std::vector<int>& data) {
  int i = p, j = p;
  while (j != q) {
    if (data[j] < data[q]) {
      std::swap(data[i], data[j]);
      i++;
      j++;
    } else {
      j++;
    }
  }
  std::swap(data[i], data[q]);
  return i;
}

void QuickSort(std::vector<int>& data, int p,
               int q) {  // 这里维持左闭右闭的区间
  if (p >= q) {
    return;
  }
  int index = quicksort(p, q, data);
  QuickSort(data, p, index - 1);
  QuickSort(data, index + 1, q);
}

int main() {
  std::vector<int> data = {2, 5, 36, 14, 38, 7, 5, 1, 2, 14};
  QuickSort(data, 0, data.size() - 1);
  for (auto value : data) {
    std::cout << value << " ";
  }
  std::cout << std::endl;
}

归并排序

归并排序的核心思想还是蛮简单的。如果要排序一个数组,我们先把数组从中间分成前后两部分,然后对前后两部分分别排序,再将排好序的两部分合并在一起,这样整个数组就都有序了。

这时候分析一下归并排序算法

归并排序是原地排序吗

是的,没有借助额外的空间,所以空间复杂度是0(1),是归并排序算法,所以占用空间小,但是缺点就是不稳定

归并排序是稳定的排序算法吗

归并排序并是一个稳定的排序算法。看你Merge()函数怎么写

归并排序算法的时间复杂度度

  • 最好的情况
原本顺序就是有序的,都要遍历一遍不进行交换0(nlogn)
  • 最坏的情况
原本是倒叙的,那么就要遍历n次里面也要交换n次时间复杂度是0(n*n)

算法实现

#include <iostream>
#include <vector>

using namespace std;

void Merge(vector<int>& data, int p, int mid, int q) {
  // 函数的功能是什么呢?
  // 函数的功能在于合并这两个区间内的数值,根据从小到大的规则
  // 开辟一块内存
  //   cout << "start================" << endl;
  //   for (int h = p; h <= mid; h++) {
  //     cout << data[h] << " ";
  //   }
  //   cout << endl;

  //   for (int h = mid + 1; h <= q; h++) {
  //     cout << data[h] << " ";
  //   }
  //   cout << endl;
  vector<int> tmp(q - p + 1);
  int i = p, j = mid + 1, k = 0;
  while (i <= mid && j <= q) {
    // cout << "i: " << i << " j: " << j << " mid: " << mid << endl;
    if (data[i] < data[j]) {
      tmp[k++] = data[i++];
    } else {
      tmp[k++] = data[j++];
    }
  }
  // 看哪一个数组还有残余

  while (j <= q) {
    tmp[k++] = data[j++];
  }

  while (i <= mid) {
    tmp[k++] = data[i++];
  }

  // 开始还原

  for (int num = 0; num < tmp.size(); num++) {
    data[num + p] = tmp[num];
    // cout << tmp[num] << " ";
  }
  //   cout << endl;

  //   cout << "stop================" << endl;
  //   for (int h = p; h <= q; h++) {
  //     cout << data[h] << " ";
  //   }
  //   cout << endl;
  return;
}

void Sort(vector<int>& data, int p, int q) {
  // 递归归并排序,归并排序的难点就在于一分一合
  if (q - p <= 0) {
    return;
  }

  int mid = p + (q - p) / 2;
  // 这里方便起我们一直维持左闭右闭的区间
  Sort(data, p, mid);
  Sort(data, mid + 1, q);
  Merge(data, p, mid, q);
}

int main() {
  vector<int> data = {1, 45, 3, 8, 12, 6, 47, 8, 245, 3, 12, 78};
  Sort(data, 0, data.size() - 1);
  for (auto value : data) {
    cout << value << " ";
  }
  cout << endl;
}

归并排序的算法其实不算很难,难点在于先用递归分离区间,再用合并函数重新合并覆盖区间

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值