前几天一直在纠结算法的问题,可自己研究了下也没啥成效,不知道哪些是重点,眉毛胡子一把抓肯定效果不好的。所以今天把这些比较容易用到的算法总结一下,相当于是让大家有个更好的方式理解,也帮助我自己更好的深入学习。排序应该算是我们工作中用的最普遍的算法之一了。其中冒泡排序,选择排序和插入排序更是基础中的基础,是大家一定要熟练掌握的(能够快速手写)。
首先我们明确几个概念:
时间复杂度:书面意思都说的很官方,我的理解就是一个排序算法经过多少次循环完成排序的,然后用公式算出一个通用的结果,将常数去掉。
空间复杂度:就是你在排序的过程中需要额外消耗的内存。
内排序:排序可以在自己的内存中完成
外排序:排序可能需要借助外部存储空间。一般外排序的空间复杂度较高
稳定:当两个相等的元素经过排序之后,先后顺序保持一致的话那么就是稳定,先后顺序变了那就是不稳定。
下面可以看下所有的算法:
![](https://i-blog.csdnimg.cn/blog_migrate/ee9533460b7fd2b27c27aaf18e33603c.png)
我们今天讲的是我们面试中或者工作中经常会碰到的排序算法。如果需要了解其他算法的实现方式可以参考: https://blog.csdn.net/weixin_41190227/article/details/86600821
这篇文章写的很详细,可以帮你解决其他排序相关的问题。
## 冒泡排序
原理:从头开始遍历,遍历到每个元素时每次都重复走一遍要排序的数组,如果元素顺序错误,就交换位置。这样小的元素就会浮到数组的顶端,大的元素会沉到数组的底部。因此称为冒泡排序。
代码示例:
void bubbleSort(int *data, int length) {
int i, j;
int temp = 0;
for (i = 0; i < length; i++) {
for (j = 0; j < length - i - 1; j++) {
if (data[j + 1] < data[j]) {
temp = data[j + 1];
data[j + 1] = data[j];
data[j] = temp;
}
}
}
}
时间复杂度:O(n2)最好:O(n2)最壞:O(n2)
空间复杂度:O(1)
## 插入排序
原理:同样也是需要经过两次循环完成。外层循环用于便利经过每个元素,内层循环用于比较,每个元素都与前面排好序的元素比较,如果顺序正常就将数组元素前移,当发现第一个小于(或大于)排好序元素的值时就找到元素的位置,这是插入。
代码示例:
void insertSort(int *data, int length) {
int i, j;
int temp = 0;
for (i = 1, j = 0; i < length; i++) {
temp = data[i];
for (j = i - 1; j >= 0 && temp < data[j]; j--) {
data[j + 1] = data[j];
}
data[j + 1] = temp;
}
}
时间复杂度:O(n2)最好:O(n)最坏:O(n2)
##选择排序
原理:选择排序应该是表现最稳定的排序算法之一了,因为他的复杂度一直是O(n2)。所以比较适合小规模数组的排序,并且不会额外消耗空间。算法是每次都在无序的数组中选择最小的放在最前面,这样每次都从剩下的无序数组中挑出最小的,这样就完成排序。
代码示例:
void selectSort(int *data, length) {
int i, j;
int temp;
for (i = 0; i < length; i++) {
for (j = i; j < length; j++) {
if (a[i] > a[j]) {
temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
}
}
时间复杂度:O(n2)最好:O(n2)最坏:O(n2)
## 希尔排序
原理:希尔排序是插入排序排序的一种演进,通过改变每次排序时的步长来提高算法效率。主要是加入了步长的概念,第一次从步长n/2开始,然后一次步长除以2,在每一次步长的元素排序中,使用插入排序,这样就能完成最终的排序。
代码示例:
void shellSort(int *data, int length) {
int i, j;
int gap, temp;
for (gap = length / 2; gap >= 1; gap /= 2) {
for (i = gap; i < length; i += gap) {
temp = data[i];
for (j = i - gap; j >= 0 && temp < data[j]; j-=gap {
data[j + gap] = data[j];
}
data[j + gap] = temp;
}
}
}
时间复杂度:O(n3/2)最好:O(nlogn)最坏:O(nlogn)
## 归并排序
原理:归并排序和希尔排序的原理实际相同,都是分而治之的原理。对部分元素先进行排序,然后整合在一起就是一个完整的排好序的数组。首先我们对元素拆分为两大组,然后再拆分,直到只有两个元素为一组。然后两组数据进行比较,将元素从小到大存放到一个临时数组中,结束之后再将四组一个的元素(此时两组元素都是经过两个元素排序的,所以都是有序的)进行排序,同样放到临时数组中,一直反复执行以上操作直到排序完成。从以上思路可以看出,归并排序实际上是可以用递归的思想,解决,拆分之后,再集合。正好和函数的入栈和弹栈相关联。
代码示例:
void sort(int *data, int *temp, int start, int middle, int end {
int i, j, k;
i = start;
j = middle + 1;
k = 0;
while (i <= start && j <= middle) {
while (data[i] < data[j]) {
temp[k++] = data[i++];
}
while (data[i] > data[j]) {
temp[k++] = data[j++];
}
}
while (i <= middle) {
temp[k++] = data[i++];
}
while (j <= end) {
temp[k++] = data[j++];
}
for (i = start; i <= end; i++) {
data[i] = temp[i];
}
}
void merge(int *data, int *temp, int start, int end) {
if (start < end) {
int middle = start + (end - start) / 2;
merge(data, temp, start, middle);
merge(data, temp, middle + 1, end);
sort(data, temp, start, middle, end);
}
}
void mergeSort(int *data, int length) {
int *temp = new int[length];
if (temp == NULL) {
return;
}
merge(data, temp, 0, length);
}
时间复杂度:O(nlogn)最好:O(nlogn)最坏:O(n)
## 快速排序
原理:快速排序是很有意思的一种排序方式,名称就叫快速排序,想必肯定效率很高了。快速排序主要是使用了哨兵的概念,首先找一个关键字,左边的值比右边的都小,随后再将左右边的数值进行排序以达到整个数组有序的效果。
代码示例:
void sort(int *data, int start, int end) {
if (start >= end) {
return;
}
int i = start;
int j = end;
int key = data[start];
while (i != j) {
while (key <= data[j]) {
j--;
}
data[i] = data[j];
while (key >= data[i]) {
i++;
}
data[j] = data[i];
}
data[i] = key;
sort(data, start, i - 1);
sort(data, i + 1, end;)
}
void quickSort(int *data, length)
{
sort(data, 0, length - 1);
}
时间复杂度:O(nlogn)最好:O(nlogn)最坏:O(n2)
以上就是介绍的常用的几种排序算法。如有错误,望大家指正。