c++实现7种经典的排序算法+原理讲解
主函数
int main()
{
int n[] = {
2,3,5,1,4 };
vector<int> a(n, n + 5);
vector<int> b(5);
//C++的sort()则是改进的快速排序算法,不用compare就是默认升序
//sort(a.begin(),a.end(),compare);
bubble_sort(a);
//selection_sort(a);
//shell_sort(a);
//merge_sort(a, 0,5 ,b);
//quick_sort(a, 0, 4);
//heap_sort(a, a.size());
for (int i = 0; i < a.size(); i++)
{
cout << a[i] << endl;
}
system("pause");
return 0;
}
1.冒泡排序
简单粗暴的排序方法,每次从左到右两两比较,把大的往后移动,每一躺移动可以保证把最大值移到最右边。
步骤:
1.从左开始比较相邻两元素x,y; 若x>y则交换
2.往后一个元素依次比较,满足条件则交换,直到到达数组的最后一个元素
3.重复步骤1,2,直到结束。
void bubble_sort(vector<int> &nums)
{
for (int i = 0; i < nums.size() - 1; i++)
{
for (int j = 0; j < nums.size() - 1 - i; j++)
{
if (nums[j] > nums[j + 1])
{
int temp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = temp;
}
}
}
}
2.插入排序
从左往右,选出一个数和其前面的所有数比较,找到合适的位置插入,使其前面有序排列。
步骤:
1.从左开始,选出当前位置的数x,和它之前的数y比较,如果x < y则交换两者
2.对x之前的数都执行1步骤,直到前面的数字都有序
3.选择有序部分后一个数字,插入到前面有序部分,直到没有数字可选择
//插入排序
void insertion_sort(vector<int> &nums)
{
for (int i = 1; i < nums.size(); i++)
{
for (int j = i; j > 0; j--)
{
if (nums[j] < nums[j - 1])
{
int temp = nums[j];
nums[j] = nums[j - 1];
nums[j - 1] = temp;
}
}
}
}
3.选择排序
每次从乱序数组中选出最小值放在最左边,依次往后。
步骤:
1.从最左开始,筛选出后面呢元素中的最小值,和最左边元素交换。
2.到下一个元素的位置,重复步骤1,直到最后一个元素。
//选择排序
void selection_sort(vector<int> &nums)
{
for (int i = 0; i < nums.size(); i++)
{
int min = i;
for (int j = i + 1; j < nums.size(); j++)
{
if (nums[j] < nums[min])
{
min = j;
}
}
int temp = nums[i];
nums[i] = nums[min];
nums[min] = temp;
}
}
4.希尔排序
即“递减增量排序算法”,该算法可以说是插入排序的升级版,为了提升比较的跨度,希尔排序将数组按照一定步长分成几个子数组进行排序,通过逐渐减短步长来完成最终排序。
步骤(这里采用2的倍数步长,初始步长设为数组长度除2):
1.计算当前步长,按步长划分子数组
2.子数组内插入排序
3.步长除以2后继续12两步,直到步长最后变成1
有关希尔排序图解教程网址
void shell_sort(vector<int> &nums)
{
for (int gap = nums.size() >>1 ; gap > 0; gap >>= 1)
{
//对各个分组进行插入时并不是先对一个组别进行排序完再对另一个组进行排序
//而是轮流对每个组进行插入排序
for (int i = gap; i < nums.size(); i++)
{
int temp = nums[i];
int j;
for (j = i - gap; j >= 0 && nums[j] > temp; j -= gap)
{
nums[j + gap] = nums[j];
}
nums[j + gap] = temp;
}
}
}
5.归并排序
采用了分治法的一个典型例子(分而治之:分解、解决、合并)。
步骤:
1.把整个数组拆散成一个个单位长度的小数组,每两个数组进行排序,排序完合并这两个数组。
2.接着,将两个单位长度的数组与另外单位长度的数组进行排序并合并
3.继续按着这样的步骤,以2的倍数进行数组之间的排序合并,直到合并排序完毕。
(以上过程可以采用分治法递归实现)
//归并排序
//排序、合并函数
void merge_array(vector<int> &nums, int b, int m, int e, vector<int> &temp)
{
int lb = b, rb = m, tb = b;
while (lb != m && rb != e)
if (nums[lb] < nums[rb])
temp[tb++] = nums[lb++];
else
temp[tb++] = nums[rb++];
while (lb < m)
temp[tb++] = nums[lb++];
while (rb < e)
temp[tb++] = nums[rb++];
for (int i = b; i < e; i++)
nums[i] = temp[i];
}
void merge_sort(vector<int> &nums, int b, int e, vector<int> &temp)
{
int m = (b + e) / 2;
if (m != b) {
merge_sort(nums, b, m, temp);
merge_sort(nums, m, e, temp);
merge_array(nums, b, m, e, temp);
}
}
6.快速排序
引用自百度百科链接
步骤
快速排序算法通过多次比较和交换来实现排序,其排序流程如下:
(1)首先设定一个分界值*(一般选择为数组头部)*,通过该分界值将数组分成左右两部分。
(2)将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。此时,左边部分中各元素都小于或等于分界值,而右边部分中各元素都大于或等于分界值。
(3)然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理。
(4)重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左、右两个部分各数据排序完成后,整个数组的排序也就完成了。
//快速排序
void quick_sort(vector<int> &nums, int low, int high)
{
if (high <= low) return;
int i = low;
int j = high + 1;
int key = nums[low];
while (true)
{
/*从左向右找比key大的值*/
while (nums[++i] < key)
{
if (i == high)
{
break;
}
}
/*从右向左找比key小的值*/
while (nums[--j] > key)
{
if (j == low)
{
break;
}
}
if (i >= j) break;
//交换i,j对应的值 (将比基准值key小的与比标准值key大的交换)
swap(nums[i], nums[j]);
}
//将基准值key与j对应值交换, 把基准值key作为边界,将左边与右边继续递归
swap(nums[low], nums[j]);
//左右递归一波
quick_sort(nums, low, j - 1);
quick_sort(nums, j + 1, high);
}
7.堆排序
堆一般指的是二叉堆,顾名思义,二叉堆是完全二叉树或者近似完全二叉树。
每个节点的值都大于或等于其子节点的值,为最大堆;反之为最小堆。
一般用数组来表示堆,下标为 i 的结点:
1.其父结点下标为(i-1)/2;
2.其左右子结点分别为 (2i + 1)、(2i + 2)
在堆的数据结构中,堆中的最大值总是位于根节点(在优先队列中使用堆的话堆中的最小值位于根节点)。
堆中定义以下几种操作:
① 最大堆调整(Max_Heapify):将堆的末端子节点作调整,使得子节点永远小于父节点
② 创建最大堆(Build_Max_Heap):将堆所有数据重新排序
③ 堆排序(HeapSort):移除位在第一个数据的根节点,并做最大堆调整的递归运算
可以参考图解过程:堆排序图解
说白了就是首先创建最大堆,使每个父节点都比子节点的值大;
然后将最顶的节点与最底的节点交换,这样交换后的最底节点(它的值是目前最大的)之后将不再参与堆排序即最大堆调整;
接下来重复进行最大堆调整重复上述过程。
最后排列出来的树形就是从根节点开始遍历即为一个递增的序列。
//堆排序
//堆排序(最大堆调整)
void max_heapify(vector<int> &nums, int start, int end)
{
//建立父节点指标和子节点指标
int dad = start;
int son = dad * 2 + 1;
while (son <= end) //若子节点指标在范围内才做比较
{
if (son + 1 <= end && nums[son] < nums[son + 1]) //先比较两个子节点大小,选择最大的
son++;
if (nums[dad] > nums[son]) //如果父节点大於子节点代表调整完毕,直接跳出函数
return;
else //否则交换父子内容再继续子节点和孙节点比较
{
swap(nums[dad], nums[son]);
dad = son;
son = dad * 2 + 1;
}
}
}
void heap_sort(vector<int> &nums, int len)
{
//初始化,i从最後一个父节点开始调整
for (int i = len / 2 - 1; i >= 0; i--)
max_heapify(nums, i, len - 1);
//先将第一个元素和已经排好的元素前一位做交换,再从新调整(刚调整的元素之前的元素),直到排序完毕
for (int i = len - 1; i > 0; i--)
{
swap(nums[0], nums[i]);
max_heapify(nums, 0, i - 1);
}
}
最后附上各排序算法的复杂度表: