常见排序算法总结

衡量算法优越性的规则

  1. 时间性能。在我的理解里,计算机产生的最大功能和意义就是“以空间换时间”。因此,一个算法的时间开销是衡量其好坏的最终的标志。而高校的排序算法应该是具有尽可能少的关键字比较次数和记录移动次数。
  2. 空间需求:执行一个算法往往需要一些辅助存储空间,好的算法往往需要的辅助存储空间也相对少

排序算法的稳定性

要被排序的元素中,可能会有两个或多个数据相等的情况,如果原数组a[i]=a[j]且i (其实我不理解为什么要分析稳定性,结果不都是一样的嘛?)

排序算法种类:内排序和外排序

内排序是指排序过程中,要被排序的元素全部被存放在内存里。
外排序是由于待排序数据过多,则把数据放在磁盘中,排序数据须通过磁盘和内存的数据传输后,才能进入算法运行。
我的理解里,内排序就是不占用额外内存,外排序就是得占用额外的内存。

算法总结表

常见排序算法总结_第1张图片

一、冒泡排序 Bubble Sort

1)两两比较相邻元素a[i]和a[i+1],如果a[i]>a[i+1],则交换a[i]和a[i+1]的位置
2)对剩下的n-1个元素,再两两进行比较,按同样规则交换它们的位置,经过n-2次边角,将最大值交换到a(n-1)的位置
3)继续上述操作,经过n-1的“冒泡处理”,每次进行n-i次比较
看过动图之后,你会发现,较大的元素会经交换而一层层上浮至顶端,就跟小鱼吐泡泡一点点冒出水面一样。
由于冒泡排序是相邻两个元素进行比较,所以如果两个元素相等,是不会交换的,所以说冒泡排序比较稳定
时间复杂度:O(N^2),适用于待排序数据数量较小的情况,这样算法写起来也简单鸭~

/*time complexity O(N^2)*/
void bubble_Sort(int a[], int n) {
  for (int i = n - 1; i > 0; i--) {
    for (int j = 0; j < i; j++)
      if (a[j] > a[j + 1])
        swap(a[j], a[j + 1]);
  }
}

二、选择排序 Select Sort

我还记得最开始学C语言,讲到冒泡排序的方法,我花了很长的时间才理解它的原理(可能因为我是笨蛋哈哈哈),但是选择排序的原理就看起来很简单。
第一次从所有待排序的数据中找出最大的元素,放在起始位置,之后再继续从剩余元素中寻找最大元素,重复此操作,直至数据全部被排序。

/*select the largest element from the unsorted array,
and then put it at the starting position,then find the
largest element from the rest numbers, repeat is continously*/
/*time complexity O(N^2)*/
void Select_sort(int a[], int n) {
  int min = 0, minindex;
  /*the outer loop ,to traverse*/
  for (int i = n - 1; i >= 1; i--) {
    minindex = i;
    /*the inner loop,to find the largest element to swap*/
    for (int j = 0; j < i; j++)
      if (a[j] <= a[minindex])
        minindex = j;
    swap(a[i], a[minindex]);
  }
}

由于是从整个序列中选最大最小,之后和两端元素交换,所以说,选择排序是一种不稳定的算法。
时间复杂度:O(N^2),同样适用于元素个数较少的序列排序。

三、插入排序 Insertion Sort

将待排序序列分为两部分:有序部分和无序部分,依次拿出一个无需部分中的元素放进有序部分,直至插完所有元素。

/*time complexity O(N^2)*/
void Insertion_Sort(int a[], int n) {
  /*divide the array into two parts,the sorted part and the unsorted part*/
  /*the outer loop,to traverse*/
  for (int i = 1; i < n; i++) {
    for (int j = i - 1; j >= 0 && a[j] > a[j + 1]; j--)
      /*the inner loop,to traverse from the first unsorted one,
      and then find the element that is smaller ,then swap*/
      swap(a[j], a[j + 1]);
  }
}

可以看出插入排序是必须元素不相等时才能移动插入,所以插入排序是稳定的(直接插入排序算法是稳定的,这个算法还有很多改进的版本,其他的稳定性俺就不知道了).
时间复杂度:O(N^2),适用于元素个数不多且元素基本有序

四、希尔排序 Shell Sort

排序方法至此跳出O(n^2)的时间复杂度,逐渐向更高速的方向发展。
希尔排序可以说是插入排序的升级版,它又被称为缩小增量排序
原理:如果待排序序列有n个元素,则先取一个increment(<=n),当作间隔数量将n个元素划分成(n-increment)个序列,分别对这几个序列进行插入排序,之后不断缩小increment,重复组内元素插入排序操作,最终实现increment缩小至1.
那么这个Increment的值该如何选取呢?至今为止没人找到一种防止四海皆准的增量序列,但大量研究表明,请添加图片描述
这个序列效果不错,但必须保证最后的increment=1.
其时间复杂度为 O(N^(3/2)),终于突破了O(N ^2)
由于数据是跳跃式的移动,所以希尔排序也是以一种不稳定的排序算法。
迄今由于不认识increment,又去补了一下英语
常见排序算法总结_第2张图片

/*time complexity O(N^(3/2))*/
void Shell_sort(int a[], int n) {
  int increment = n;
  int temp;
  while (increment > 1) {
    increment = increment / 3 + 1;
    for (int i = increment; i < n; i++) {
      if (a[i - increment] > a[i]) {
        temp = a[i];
        int j = i - increment;
        while (j >= 0 && a[j] > temp) {
          a[j + increment] = a[j];
          j -= increment;
        }
        a[j + increment] = temp;
      }
    }
  }
}

这个过程略微有点复杂,我画了半天图才理解啥意思
常见排序算法总结_第3张图片
常见排序算法总结_第4张图片

五、归并排序 Merging Sort

原理:归并,顾名思义,就是合并的意思。在这个算法之中,假定待排序数据有n个元素,则把这n个元素看作n个有序的子序列,每个子有序序列长度为1,然后双双合并,得到长度为2的有序子序列,重复上述操作,直至得到长度为n的有序序列。
(这玩意儿的代码俺不会写,粘了大佬的哈哈哈)

#include 
#include 
using namespace std;
void merge(int *data, int start, int end, int *result) {
  int left_length = (end - start + 1) / 2 + 1;
  int left_index = start;
  int right_index = start + left_length;
  int result_index = start;
  while (left_index < start + left_length &&
         right_index < end + 1) // store data into new array
  {
    if (data[left_index] <= data[right_index])
      result[result_index++] = data[left_index++];
    else
      result[result_index++] = data[right_index++];
  }
  while (left_index < start + left_length)
    result[result_index++] = data[left_index++];
  while (right_index < end + 1)
    result[result_index++] = data[right_index++];
}

void merge_sort(int *data, int start, int end, int *result) {
  if (1 == end - start) // last only two elements
  {
    if (data[start] > data[end]) {
      int temp = data[start];
      data[start] = data[end];
      data[end] = temp;
    }
    return;
  } else if (end == start)
    return; // last one element then there is no need to sort;
  else {
    // continue to divide the interval
    merge_sort(data, start, (end - start + 1) / 2 + start, result);
    merge_sort(data, (end - start + 1) / 2 + start + 1, end, result);
    // start to merge sorted data
    merge(data, start, end, result);
    for (int i = start; i <= end; ++i) {
      data[i] = result[i];
    }
  }
}
// example
int main() {
  int data[] = {1, 8, 7, 9, 6, 5, 3, 4, 2, 10};
  int length = 10;
  int result[length];
  cout << "before sorted:" << '\n';
  for (int i = 0; i < length; i++)
    cout << data[i] << ' ';
  cout << '\n' << "after sorted:" << '\n';
  merge_sort(data, 0, length - 1, result);
  for (int i = 0; i < length; i++)
    cout << result[i] << ' ';
  return 0;
}

六、堆排序 Heap Sort

堆排序还在整理中~

七、快速排序 Quick Sort

快排篇幅比较大,另开了一篇,专门整理这个
快速排序 QuickSort

swap函数的几种写法

上述算法大多用到了swap这个函数,我之前又在这个函数上犯过很多错误,所以总结一下这个函数的写法。

#include 
#include 
using namespace std;
/*传指针法*/
void swap1(int *a, int *b) {
  int temp = *a;
  *a = *b;
  *b = temp;
}
/*传引用法*/
void swap2(int &a, int &b) {
  int temp = a;
  a = b;
  b = temp;
}
/*位运算法*/
void swap3(int *a, int *b) {
  *a = *a ^ *b;
  *b = *b ^ *a;
  *a = *a ^ *b;
}
int main() {
  int a = 5, b = 6;
  swap3(&a, &b);
  cout << a << " " << b << endl;
}

关于第三种位运算法,详见位运算整理(C/C++)实现

你可能感兴趣的:(简单算法,数据结构,数据结构,算法,排序,排序算法,堆排序)