学数据结构和算法的时候接触到了不少排序算法,在这里整理一下。
涉及到的排序算法有冒泡排序、选择排序、插入排序、希尔排序、归并排序、快速排序
一、暴力排序(我也不知道叫什么,感觉很暴力就这样叫了)
主要思想是:
将数组的第一个数和接下来的所有数比较,若该数小于第一个数,则他俩交换位置,第一轮遍历交换完就能找出最小的值放在第一位,然后找数组的第二个数和后面的所有数比较,也是交换出在后面的数中最小的(在整个数组中就是第二小的)
依次类推,一直排完整个数组
void ForceSort(vector<int> &arr)
{
int temp,len=arr.size();
for (int i = 0; i < len - 1; ++i)
{
for (int j = i + 1; j < len; ++j)
{
if (arr[i] > arr[j])
{
temp = arr[j];
arr[j] = arr[i];
arr[i] = temp;
}
}
}
}
二、冒泡排序
主要是比较两两相邻的数,如果是左边比右边大,则交换,否则就跳过,每这样从数组的尾到头两两交换一轮,就能选出一个最小值,然后下一轮就不用再管这个最小值了(++i)
下面还使用了一个tag作为标志位,当某轮冒泡从尾到头都已经没有出现过交换,就证明已经排序好了,不用再进行比较了,直接退出循环。就是因为他能用tag作为一种优化,所以在某些不完全乱序的排序任务中,要比上面的暴力排序要好
void BubbleSort(vector<int>& arr)
{
int temp, len = arr.size();
bool tag=true;
for (int i = 0; (i < len - 1) && tag; ++i)
{
tag = false;
for (int j = len - 1; j >= i+1; --j)//从最后面开始,执行完一次此循环则将最小的数排到了最前面
{
if (arr[j] < arr[j - 1])//如果前一个数大于后一个数,就交换位置
{
temp = arr[j - 1];
arr[j - 1] = arr[j];
arr[j] = temp;
tag = true;
}
}
}
}
三、选择排序
将第一个数和后面的每一个数作比较,找出最小的数的下标(所以每次都需要遍历完才知道谁最小)。找到最小的时候,就交换他俩的位置。这样就选出最小值放在第一位了,然后找重复操作找次小值(++i)。直到排序完成
选择排序的迭代次数其实和暴力排序是一样的,但是选择排序减少了交换次数,不用说每次一比他小就换位置,而是找到最小值才换
(即将交换语句放在了第二个循环的外面)
void SelectSort(vector<int>& arr)
{
int temp,min_index, len = arr.size();
for (int i = 0; i < len - 1; ++i)
{
min_index = i;//min_index记录最小值的下标
for (int j = i + 1; j < len; ++j)
{
if (arr[min_index] > arr[j])//进入里面就说明能找到更小的值
{
min_index = j;
}
}
if (min_index != i)//找到了比原本当前位置i更小的值就交换
{
temp = arr[min_index];
arr[min_index] = arr[i];
arr[i] = temp;
}
}
}
四、插入排序
把每个数和上一个相比,如果这个数小于上一个数就考虑将这个数往前插,至于插到什么位置,就看这个数的大小,他要和前面的每一个数相比,若是小于就一直往前走,直到排在某个比他小的数的后面(然后他后面的数就往后挪位置给他)。(如果比所有数小就排第一了)
这种算法如果倒序比较多的话,其实效率并不比选择算法高,
但如果是完全乱序,这是一种比较快的算法。
void InsertSort(vector<int>& arr)
{
int temp,j, len = arr.size();
for (int i = 1; i < len; ++i)
{
if (arr[i] < arr[i - 1])
{
temp = arr[i];
for (j = i-1; arr[j]>temp; --j)
{
arr[j+1] = arr[j];
if (j == 0)
{
--j;
break;
}
}
arr[j+1] = temp;
}
}
}
五、希尔排序
插入排序的升级版,插入排序是每次和他上一个比较,
希尔排序是第一轮和他前gap个比较,就是把arr[i]和arr[i-gap]每两个数当成一组插入排序,进行排序。(一共有gap组)
第二轮是gap缩小一倍,把arr[i-gap],arr[i],arr[i+gap],arr[i+2*gap]每四个数当成一组插入排序。(一共有gap组)
…
依次类推
一般来讲只要理解了插入排序,然后再插入排序的基础上加个外循环,再把原本的间隔1,改成gap即可
void HillSort(vector<int>& arr)
{
int temp, j, len = arr.size(),gap;
gap = len;
do
{
gap = gap/2;
for (int i = gap; i < len; i+=gap)
{
if (arr[i] < arr[i - gap])
{
temp = arr[i];
for (j = i - gap; arr[j] > temp; j-=gap)
{
arr[j + gap] = arr[j];
if (j == 0)
{
j-=gap;
break;
}
}
arr[j + gap] = temp;
}
}
} while (gap > 1);
}
六、归并排序
主要思想是先将n长的数组无限二分,直到每份都只有两个数,然后将这两个数排序,重新回退到上一层(每份只有四个数的时候)再进行排序,在原来两个两个已有顺序的基础上,这是比较好排序的,排完后又回退到上一层
依次类推
用递归的思想比较好实现,但递归有点影响效率了
void merging(vector<int>::iterator begin1, vector::iterator<int> end1, vector::iterator<int> begin2, vector::iterator<int> end2)
{
auto t1 = begin1, t2 = begin2;
vector t3;
while (end1 > t1&& end2 > t2)
{
if (*t1 < *t2)
{
t3.push_back(t1);
++t1;
}
else
{
t3.push_back(t2);
++t2;
}
}
while (t1 < end1)
{
t3.push_back(t1);
++t1;
}
while (t2 < end1)
{
t3.push_back(t2);
++t2;
}
t1 = begin1;
for (int i = 0; i < t3.size(); ++i)
{
*t1 = t3[i];
++t1;
}
}
void MeregeSort(vector<int>::iterator begin, vector<int>::iterator end)
{
if (end-begin > 1)
{
auto new_begin1 = begin;
auto new_end1 = begin+(end- begin)/2;
auto new_begin2 = begin + (end - begin) / 2 ;
auto new_end2 = end;
MeregeSort(new_begin1, new_end1);
MeregeSort(new_begin2, new_end2);
merging(new_begin1, new_end1, new_begin2, new_end2);
}
}
七、堆排序
是简单选择排序的升级版。
可以理解为数组是按完全二叉树层叠排列的,从最右下小二叉树起,将三个数选出最大的一个排到根的位置。继续从右到左从上到下依次执行,执行完后,根部的位置即当前最大值(即数组里第0位)。然后将这个最大值放到完全二叉树最末位,(数组里最后一位),然后再依次类推选出第二大,第三大。。。最终完成排序。
void SwapTheMax(vector<int>& arr, int i, int len)
{
int max_index = i;
if (2 * i + 1 < len && arr[max_index] < arr[i * 2 + 1])
max_index = 2 * i + 1;
if (2 * i + 2 < len && arr[max_index] < arr[i * 2 + 2])
max_index = 2 * i + 2;
if (max_index != i)
{
int temp = arr[i];
arr[i] = arr[max_index];
arr[max_index] = temp;
SwapTheMax(arr, max_index, len);
}
}
void HeapSort(vector<int>& arr)
{
int len = arr.size();
while (len > 1)
{
//初始化堆
for (int i = len / 2 - 1; i >= 0; --i)
{
SwapTheMax(arr, i, len);
}
int temp = arr[0];
arr[0] = arr[len - 1];
arr[len - 1] = temp;
//交换堆顶和当前最后一个的位置
--len;
}
}
八、快速排序
是一种冒泡算法的升级版!
每次取一个数组中的(当前第一位)一个数作为基准点,然后把数组里大于他的数放在右边,小于它的数放在左边,把左边和右边又看成是一个数组,进行递归操作。当数组里递归到只剩一个数的时候就返回。最终就能实现一个排序效果。
int CalculatePoint(vector<int>::iterator begin, vector<int>::iterator end)
{
int point = begin;
auto e = end-1, b = begin;
while (e - b > 0)
{
while (b < e)
{
if (e < point)
{
int temp = *e;
*e = point;
b = temp;
break;
}
--e;
}
while (b < e)
{
if (b > point)
{
int temp = *b;
*b = point;
*e = temp;
break;
}
++b;
}
}
return (b - begin);
}
void QuickSort(vector<int>::iterator begin, vector<int>::iterator end)
{
if (end - begin > 1)
{
int point = CalculatePoint(begin, end);
QuickSort(begin, begin + point+1);
QuickSort(begin + point+1, end);
}
}
开始测评
生成999个测试数据

各排序算法耗时

headsort是后来加的,这里没测评
换了组数据再测

结论:
一些简单的排序比如冒泡排序暴力排序速度是比较慢的,选择排序的速度也不快,插入排序简单但是速度还算快,改进后的希尔排序有一定提升。归并排序用了递归,实测起来反而速度慢了,快速排序就算用了递归速度也很快(快速排序牛逼!)
最后一句话总结:
有STL库的sort在,写个屁的排序,溜了溜了