排序
一、排序简介
什么是排序?顾名思义,排序就是将一些杂乱无章的数字或者事务,按照一定的规律将每一个个体放在与规律相符合的位置上的过程。
二、排序分类
我们可以简单的根据排序的某些相同性将排序分为三类:
三种复杂度为O(n2)的排序
冒泡排序
选择排序
插入排序
两种复杂度为O(nlogn)的排序
归并排序
快速排序
三种不基于比较的排序
桶排序
基数排序
计数排序
三、各个排序的详细介绍以及代码构成
1、复杂度为O(n2)的排序
(1)冒泡排序
冒泡排序,这个名字看上去很奇特,那为什么这种排序叫冒泡呢?
运用毕导的话来说:小学二年级我们学过,当一个泡泡在水中向上运动时,根据公式:P = pgh 我们很容易得到水对泡泡的压强变小,但此时泡泡中空气压强大于水的压强,故泡泡膨胀,体积变大。据此我们得知了:越靠近水面,泡泡的体积越大。从而我们可以将这种现象运用到计算机的排序算法中:将大的泡泡--我们可以理解为大的数依次浮起到相应的位置,下面我们用图来演示如何完成冒泡排序。
首先来看第一遍排序:
从第一个数字开始,5大于3--交换位置,5大于2--交换位置,5大于4--交换位置,5大于1--交换位置。我们可以清楚地看到,通过对该数组的第一次遍历我们将整个数组最大的值5放到了数组的最后面。
其次来看第二遍排序:
从第一个数字开始,3大于2--交换位置,3小于4--不变,4大于1--交换位置。第二遍遍历数组之后,我们将整个数组第二大的数放到了倒数第二个位置。
之后来看第三遍排序:
2小于3--不变,3大于1--交换位置。我们将第三大的数放到了倒数第三个位置上。
最后来看第四遍排序:
2大于1--交换位置。那么数组已经按照从小到大的顺序排列好了。
我们使用冒泡排序排好了一个最简单的数组,现在我们来分析一下这个排序算法:每遍历一次数组,我们只能将一个当前的最大数排到相应的位置,那么这个数组有多少个数我们就需要遍历几次,很容易我们得到该算法的复杂度是O(n2),所以这并不是一个很好的算法,当一个数组的元素很多的时候,这个排序策略并不是我们优先选择的策略。
接下来是冒泡排序的c++代码:
void bubbleSort(vector<int> &array){for (int i = 0 ; i < array.size() ; i++) {for (int j = 0 ; j < array.size() - i - 1; j++) {if (array[j] > array[j + 1]) {int tmp;
tmp = array[j];
array[j] = array[j + 1];
array[j + 1] = tmp;}}}}
(2)选择排序
选择排序当然也十分的好理解:从一个有n个数的乱序数组中选择最大的那个数,将其放在第(n-1)个位置(从0开始);第二个循环从前n-1个数中选择最大的那个数,放在第(n-2)个位置,依次类推进行循环,那么我们就得到了相应从小到大依次排列的有序数组。下面我们用图来演示如何使用选择排序排好一个乱序数组。
第一个循环我们可知:5是最大的数,那么我们将其与当前的最后一位数字1交换位置得到了第二行的数组
第二个循环我们可知:4是最大的数,但是它刚好处于倒数第二个位置,我们不改变其位置
第三个循环我们可知:3是最大的数,将其与当前的倒数第三个位置上的数字2交换位置,得到了第四行的数组
第四个循环我们可知:2是最大的数,但是它处于倒数第四个位置,我们不改变其位置,最后我们得到了一个有序数组
我们来分析一下这个排序的算法复杂度:每次遍历数组找到一个最大的数,遍历一次数组的时间复杂度为O(n),这个数组有多少个数我们就遍历多少遍,所以最终的算法复杂度为O(n2)。
下面是选择排序的c++代码:
void selectionSort(vector<int> &array){int maxIndex = 0 , size = array.size();for (int i = 0 ; i < size - 1 ; i++) {for (int j = 0 ; j < size - i ; j++) {
maxIndex = array[j] > array[maxIndex] ? j : maxIndex;}int tmp = array[maxIndex];
array[maxIndex] = array[size - i - 1];
array[size - i - 1] = tmp;}}
(3)插入排序
对于插入排序,我们也可以从名字来理解这个排序算法。
首先,我们可以将其理解为为了整理扑克牌而将摸到的牌插入到已经排好序的牌中,举个例子,如图,现在我的左手握有已经整理好的牌2、4、5、10,接下来我抽到一张7,那么根据顺序大小,我应该将7插入在5和10之间,保持左手中的牌永远是排好序的,这便是典型的插入算法的原型。
简单介绍完插入排序,我们来模拟一下插入排序的具体步骤:(偷个懒没做图片)
A:右手摸上来一张2,发现其比5小,那么交换位置,交换之后发现左边没有可以比较的数字了,插入结束。
B:右手摸上来一张4,首先与5比较,发现比5小,交换两者位置,之后再与左边的2比较,发现4大于2,那么位置不变,插入结束。
C:右手摸上来一张6,首先与5比较,发现比5大,位置不变,直接将其放在5的右边即可,插入结束。
D:右手摸上来一张1,首先与6比较,发现比6小,交换其位置,依次类推,发现比后面的所有数都小,故放到第一个位置上,插入结束。
E:右手摸上来一张3,同样的首先与6比较,发现比6小,交换其位置,之后发现其比5,4均小,将其放在4左边,再和2比较发现比2大,那么位置不变,插入结束。
F:我们得到了最终排好序的一个有序数组。
下面我们来分析一下这个算法的复杂度:每当我们摸上来一张牌需要将其与之后的牌交换位置,最差情况就是这张牌需要和排好序的所有牌交换位置,那么很简单,这个算法的复杂度即为O(n2)。
下面是插入排序的c++代码:
void insertionSort(vector<int> &array){int size = array.size();for (int i = 1 ; i < size ; i++) {int key = array[i] , j = i - 1;while (j >= 0 && array[j] > key) {
array[j + 1] = array[j];
j--;}
array[j + 1] = key;}}
注:以上为简单排序,用处不大,主要是掌握思想,之后会写排序(二)、(三)。
出品,必属精品。