前言
本文是基于笔者针对算法和数据结构面试的复习而整理的,这一块主要是针对基础排序算法,即冒泡排序,选择排序,插入排序。虽然在实际项目中应用较少,但是其原理是必须懂得的。具体到笔试基本用不到,面试的话基本也用不到。
一、冒泡排序
入门中的入门,基础中的基础,暴力中的暴力。
这里以例子来说明:现在有无序数组a[5],如下:
a[0] | a[1] | a[2] | a[3] | a[4] |
---|---|---|---|---|
3 | 2 | 7 | 1 | 0 |
数组中发生的交换过程
时间复杂度:O(N^2) = O((N-1 + 1)*N/2)
N-1 + N-2 + … + 2 + 1 = (N-1 + 1)*N/2
总结成代码
void BubbleSort(vector<int> &v)
{
int n = v.size();
//边界判断,数组大小小于2不需要进行排序
if (n<2)
{
return;
}
//外层循环,每一轮循环冒泡出一个最大的数
for (int i = 0; i < n-1; i++)
{
//内存循环,依次比对相邻两个数的大小关系,满足条件就交换
for (int j = 0; j < n-1-i; j++)
{
if (v[j]>v[j+1])
{
//交换
swap(v[j], v[j + 1]);
}
}
}
return;
}
二、选择排序
选择排序的基本思想就是找极值(极大值或极小值)。
如:从小到大排序的话,则是通过N-1次遍历,在每轮遍历的过程中找寻当前目标数组段的最小值。
同样图解如下:数组中发生的交换过程
时间复杂度:O(N^2) = O((N + 2)*N/2)
N + N-1 + … + 2 = (N + 2)*N/2
总结成代码
void SelectSort(vector<int> &v)
{
int n = v.size();
if (n<2)
{
return;
}
for (int i = 0; i < n-1; i++)
{
int min = i; //记录最小值的下标
for (int j = i+1; j < n; j++)
{
if (v[j]<v[min])
{
min = j;
}
}
swap(v[min], v[i]); //交换当前i标位置的值与最小值
}
return;
}
三、插入排序
插入排序的过程可以与抓牌的过程类比,都是将数字插入有序数组的过程。
我们在抓牌过程中,每抓一张都会进行一次整理,以找到该牌的正确位置并插入。
具体的算法过程是:从数组第二个数据元素开始遍历,直到第N-1个数据元素停止,每个数据元素在插入有序数组的过程需要逐项去比对,以便确认是否需要前插。
图解过程如下:
时间复杂度:插入排序与数据状况是有关系的(选择排序和冒泡排序和数据状况没有关系)。数组有序,O(N),只是顺序遍历数组;数组逆序,O(N^2),在遍历顺组(O(N))的情况下,对每个数据需要进行O(N)复杂度的插入运算。
总结成代码
void InsertSort(vector<int> &v)
{
int n = v.size();
if (n < 2)
{
return;
}
int i = 0;
//外循环,从第二项开始摸牌
while (i<n-1)
{
int j = i + 1;
//内循环,需要去完成摸牌过后的查找位置以及插入过程
while (j>0)
{
if (v[j]<v[j-1])
{
swap(v[j], v[j - 1]);
}
j--;
}
i++;
}
return;
}
总结
本文进行了三种基础排序算法的快速回顾,其中并没有涉及到相关的优化(如基于冒泡的flag优化,基于插入排序演变的希尔排序)。如果有错误或是有任何有可能造成误解的地方,欢迎指正。