文章目录
前言
本章节主要讲解的是时间复杂度为O(n^2)的排序算法以及优化,帮助大家从动图等多个角度理解排序算法。
一、冒泡排序
1、时间复杂度:O(n^2)
2、排序逻辑:
我们以升序为例,整体的实现逻辑就是每次比较出一个最大值,将其放到右面,第二次找到一个第二大的值放到右面,以此类推,实现排序。
3、代码实现:
相邻数字进行比较,满足条件则交换,最终将极大值或极小值放到右面。
void bubble_sort(int* arr, int len)
{
for (int i = 0; i < len - 1; i++)
{
for (int j = 0; j < len - 1 - i; j++)
{
if (arr[j] > arr[j + 1])
{
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
4、算法优化:
我们发现,在上面的代码中,无论这个数组是否在中途排好顺序,或者假设一种极端情况,我们传入的就是一个有序数组,逻辑上此时是不用排序的。但是我们刚才实现的冒泡排序完全无视了这样一种灵活的状态,只会机械地重复固定次数。
那么我们如何优化呢?
我们发现,如果一个数组已经有序了,那么不会发生任何一次交换,此时就说明已经有序了。那么我们只需要判断在某轮比较中是否发生过交换,只要没交换那么这个数列就已经有序了。
void bubble_sort(int* arr, int len)
{
for (int i = 0; i < len - 1; i++)
{
int is_swap = 0;
for (int j = 0; j < len - i - 1; j++)
{
if (arr[j] > arr[j + 1])
{
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
if (is_swap == 0)
{
return;
}
}
}
二、选择排序
1、时间复杂度:O(n^2)
2、排序逻辑:
选择排序其实和冒泡排序非常的相似,同样以升序序列为例,选择排序则是将最小值放到最右端。以此类推,完成排序。
3、代码实现:
void select_sort(int* arr, int len)
{
for (int i = 0; i < len; i++)
{
int k = i;
for (int j = i + 1; j < len; j++)
{
if (arr[j] < arr[k])
{
k = j;
}
}
int temp = arr[k];
arr[k] = arr[i];
arr[i] = temp;
}
}
4、算法优化
我们发现选择排序的时候基本上会遍历每个未排序的元素。也就是说,在这个过程中我们不仅仅能选出最小值,我们还能够选出最大值。所以我们不妨在每一轮遍历过程中将两种极值都找到,其中最小的放左面,最大的放右面。
void select_sort(int* arr, int len)
{
int left = 0;
int right = len - 1;
while (left < right)
{
int k_min = left;
int k_max = left;
for (int j = left; j <= right; j++)
{
if (arr[j] < arr[k_min])
{
k_min = j;
}
if (arr[j] > arr[k_max])
{
k_max = j;
}
}
swap(arr + k_min, arr + left);
/*排除一种特殊情况,即当最大值是最左侧数字的时候,因为如果是最大值是最左侧的数字,那么在最大值交换之前,这个位置已将被换 成了该轮的最小值,这样就会导致我们这一轮的最小值又与最大值发生了交换。所以我们应该再次记录交换后的最大值位置,再发生交 换。*/
if (k_max == left)
{
k_max = k_min;
}
swap(arr + k_max, arr + right);
left++;
right--;
}
}
三、插入排序
1、时间复杂度:O(n^2)
2、排序逻辑:
与前面两种排序方式相比,在一个数列基本有序的情况下,插入排序可以大大减少比较的次数。
3、代码实现:
void insert_sort(int* arr, int len)
{
for (int i = 1; i < len; i++)
{
for (int j = i ; j > 0 ; j--)
{
if((arr[j] < arr[j - 1]))
{
int temp = arr[j];
arr[j] = arr[j - 1];
arr[j - 1] = temp;
}
}
}
}
4、代码优化(希尔排序)
我们发现插入排序每次的跨度只是针对左右相邻的数据,当数据量很大的时候,插入排序就有点慢了。那么下面的希尔排序则是对插入排序进行了一定的优化。
四、希尔排序
1、时间复杂度:O(n^2)
2、排序逻辑:
优化版的插入排序,优化的地方在于,插入排序是移动一步,和前面的做对比。而希尔排序一步可以跨度很大,因此当一个数组非常大的时候,希尔排序大大增加了效率。
希尔排序就是将一组数字,分成若干小的数组,再分别进行插入排序,这样就能够大大的减少每次移动的步数。
3、代码实现:
void shell_sort(int* arr, int len)
{
int step = len / 2;
while (step >= 1)
{
for (int i = step; i < len; i++)
{
for (int j = i; j >= step; j -= step)
{
if (arr[j] < arr[j - step])
{
int temp = arr[j];
arr[j] = arr[j - step];
arr[j - step] = temp;
}
}
}
step /= 2;
}
}