delete优化_基础排序算法详解与优化

a03be656f11e93bb5efa58f34961b130.png

Linux编程 点击右侧关注,免费入门到精通! dc0ee53ea80e356051b1ff4af799baa0.png

作者丨godbmwhttps://www.jianshu.com/p/a70c3c204857

fca71b343a70c54939843b4dc2517a71.gif1. 谈谈基础排序

常见的基础排序有选择排序、冒泡排序和插入排序。众所周知,他们的时间复杂度是 O(n*n)。

但是,现在要重新认识一下基础排序算法,尤其是“插入排序”:在近乎有序的情况下,插入排序的时间复杂度可以降低到 O(n)的程度。

因此,在处理系统日志的任务中,因为日志记录是按照时间排序,但偶尔会有几条是乱序,此时使用插入排序再好不过。而对于高级排序算法,一个常见的优化就是利用插入排序做局部数据排序优化。

fca71b343a70c54939843b4dc2517a71.gif2. 算法实现

排序算法被封装在了SortBase.h中的SortBase命名空间中,以实现模板化和防止命名冲突。如下图所示:

51a37e6947d7eb34a7a3d7a86c278ebc.png

fca71b343a70c54939843b4dc2517a71.gif2.1 选择排序

假设从小到大排序,那么,刚开始指针指向第一个数据,选择从当前指针所指向数据到最后一个数据间最小的数据,将它放在指针位置。

指针后移一位,重复上述步骤,直到指针移动到最后一个数据。

这种重复保证了每次,指针前面的数据都是从小到大排好顺序的数据。所以,从头到尾扫描一遍,自然排好序了。

代码如下:

template <typename T>
void selectionSort(T arr[], int n) {
  int minIndex = -1;
  for(int i = 0; i     minIndex = i;
    for(int j = i+1; j       if(arr[j]         minIndex = j;
      }
    }
    swap(arr[i], arr[minIndex]);
  }
}

fca71b343a70c54939843b4dc2517a71.gif2.2 冒泡排序

假设排序是从小到大排序。

我一直感觉冒泡排序是和选择排序反过来了(如果说错请指正)。因为选择排序是每次选择最小的数据,放到当前指针位置;而冒泡排序是把不停交换相邻数据,直到把最大的数据“冒泡”到应该到的位置。

优化的地方是:记录每次交换的最后位置,在此之后的元素在下一轮扫描中均不考虑。因为交换的最后位置之后的元素已经是从小到大排序好了的。

在实现过程中,因为需要不停交换相邻两个数据,因此,消耗了很多额外时间。

template <typename T>
void bubbleSort(T arr[], int n) {
  int newn;
  do {
    newn = 0;
    for(int i = 1; i       if(arr[i-1] > arr[i]) {
        swap(arr[i-1], arr[i]);
        // 优化
        newn = i;
      }
    }
    n = newn; // 不再考虑 newn 后的数据
  } while (newn > 0);
}

fca71b343a70c54939843b4dc2517a71.gif2.3 插入排序

插入排序容易和上面两个算法搞混。可以类比打扑克牌时候的对扑克牌进行排序:我们会先排序前 1 张、然后是前 2 张、前 3 张 ... 一直到前 n 张。算法实现显然是双重循环,如下所示:

template <typename T>
void insertionSort(T arr[], int n) {
  for(int i = 1; i     for(int j = i ; j > 0; j--) {
      if(arr[j - 1] > arr[j]) {
        swap(arr[j], arr[j - 1]);
      } else {
        break; // 优化:已经保证之前都是正常排序,直接跳出即可
      }
    }
  }
}

显然,插入排序也能在局部排好序的情况下跳出循环(代码中的优化),以减少算法消耗时间。

然而上述算法其实跑分并比不上选择排序,因为swap(arr[j], arr[j - 1]);这行代码交换了一次,相当于赋值 3 次,在大数据量情况下,比较消耗时间。

优化: 内层循环,每次保存arr[i], 在检测到当前数据大于arr[i]的时候,后移一位当前元素arr[j] = arr[j-1];。当跳出内层循环时,直接将保存的arr[i]赋值给arr[j]即可。

template <typename T>
void insertionSort(T arr[], int n) {
  for(int i = 1; i     T e = arr[i];
    int j = i ;
    for(; j > 0 && arr[j-1] > e; j--) {
      arr[j] = arr[j-1];
    }
    arr[j] = e;
  }
}

fca71b343a70c54939843b4dc2517a71.gif3. 性能测试

首先利用 SortTestHelper::generateRandomArray函数生成大量无序随机数据,然后进行排序和时间测定。代码如下:

#include 
#include "SortHelper.h"
#include "SortBase.h"
#include "SortAdvance.h"

using namespace std;

int main() {
  int n = 50000, left = 0, right = n;

  int *arr = SortTestHelper::generateRandomArray<int>(n, left, right);
  int *brr = SortTestHelper::copyArray<int>(arr, n);
  int *crr = SortTestHelper::copyArray<int>(arr, n);
  SortTestHelper::testSort<int>(arr, n, SortBase::selectionSort<int>, "selection sort");
  SortTestHelper::testSort<int>(brr, n, SortBase::insertionSort<int>, "insertion sort");
  SortTestHelper::testSort<int>(crr, n, SortBase::bubbleSort<int>, "bubble sort");
  delete[] brr;
  delete[] arr;
  delete[] crr;

  return 0;
}

行结果如下图所示:

7e194ee56733c945c4675f56fc402572.png

除了大量无序随机数据,类似于系统日志的数据就是基本有序的大量数据。此时,测试代码如下:

#include 
#include "SortHelper.h"
#include "SortBase.h"
#include "SortAdvance.h"

using namespace std;

int main() {

  int n = 50000, left = 0, right = n;
  int *arr = SortTestHelper::generateNearlyOrderedArray<int>(n, 10);
  int *brr = SortTestHelper::copyArray<int>(arr, n);
  int *crr = SortTestHelper::copyArray<int>(arr, n);
  SortTestHelper::testSort<int>(arr, n, SortBase::selectionSort<int>, "selection sort");
  SortTestHelper::testSort<int>(brr, n, SortBase::insertionSort<int>, "insertion sort");
  SortTestHelper::testSort<int>(crr, n, SortBase::bubbleSort<int>, "bubble sort");
  delete[] brr;
  delete[] arr;
  delete[] crr;

  return 0;
}

如图所示,插入排序的只用了 0.002 秒。在这种数据情况下,插入排序的时间复杂度近似 O(N),绝对快于高级排序的 O(NlogN)。除此之外,还保证了稳定性。

eec4d429814f828627110bd91d0633ec.png

 推荐↓↓↓ 

ca86d2ba4f4ec689a810e63b74f8aa85.png

?16个技术公众号】都在这里!

涵盖:程序员大咖、源码共读、程序员共读、数据结构与算法、黑客技术和网络安全、大数据科技、编程前端、Java、Python、Web编程开发、Android、iOS开发、Linux、数据库研发、幽默程序员等。

7a6abe3e0efccc3d280e45129642c480.png万水千山总是情,点个 “ 好看” 行不行
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值