我是靠谱客的博主 机智鱼,最近开发中收集的这篇文章主要介绍数据结构—八种排序算法稳定性:一、冒泡排序二、选择排序三、插入排序四、希尔排序五、归并排序六、快速排序七、基数排序八、堆排序,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

目录

稳定性:

一、冒泡排序

        概念:

        特点:

        复杂度:

        代码演示:

二、选择排序

        概念:

        特点:

        复杂度:

        代码演示:

三、插入排序

        概念:

        特点:

        复杂度:

        代码演示:

四、希尔排序

        概念:

        特点:

        复杂度:

        代码演示:

五、归并排序

        概念:

        特点:

        复杂度:

        代码演示:

六、快速排序

        概念:

        特点:

        复杂度:

        代码演示:

七、基数排序

        概念:

        特点:

        复杂度:

        代码演示:

八、堆排序

        概念:

        特点:

        复杂度:

        代码演示:


稳定性:

        在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序后,这些记录的相对次序保持不变,则称该排序算法是稳定的。

        例如:数组a  =  {1, 2, 3, 4, 1},经过排序后为{1, 1, 2, 3, 4}。若第一个1为原先下标为0,第二个1为原先下标为4,则该排序算法是稳定的;若第一个1为原先下标为4,第二个1为原先下标为0,则该排序算法是不稳定的。

        稳定性的排序算法有:冒泡排序,插入排序、归并排序、基数排序。

        不稳定的排序算法有:选择排序、希尔排序、快速排序、堆排序。

以下排序默认为从小到大排序

一、冒泡排序

        概念:

        冒泡排序通过不断比较两个相邻的元素,若前一个元素较大则交换,重复(n-i-1)次为1轮(i表示第i轮,从0开始),重复(n-1)轮后排序完成(最后一轮只有一个元素,所以不用比较)。较小的元素会经由交换慢慢“浮”到数列的前端。

        特点:

        ①冒泡排序是稳定的排序方法②每次比较只比较相邻的两个元素。③每交换一轮就会将一个最大的元素置于数组的最后,即每经过一轮比较,需要比较的元素减一。通俗点说就是每进行一轮就会将当前数组中最大的元素放最后,之后要比较的数组元素减一。

        复杂度:

        时间复杂度O(N²)。空间复杂度O(1)。

        代码演示:

#include <iostream>
using namespace std;
void bubble_sort(int a[], int n) {
//传入数组,数组长度
for (int i = 0; i < (n-1); i++) {
//总共会进行n轮
for (int j = 0; j < (n - i - 1); j++) {
//一轮会比较n-i-1次(还要减去1防止越界)
if (a[j] > a[j + 1]) {
int temp = a[j];
a[j] = a[j + 1];
a[j + 1] = temp;
}
}
}
}
int main()
{
int a[] = { 2, 5, 6, 8, 2, 4, 3 };
for (int i : a) { cout << i << " "; }
cout << endl;
bubble_sort(a, sizeof(a) / sizeof(a[0]));
for (int i : a) { cout << i << " "; }
cout << endl;
return 0;
}

二、选择排序

        概念:

        找出数组中未排序部分的最小值下标,然后将其与未排序部分的首元素交换,重复n-1次即可。

        特点:

        ①选择排序是不稳定排序。②每排序一次都会将未排序数组的最小值放到最前,即每次排序后,最前面的是元素都比后面小。

        复杂度:

        时间复杂度O(n²),空间复杂度O(1)。

        代码演示:

#include <iostream>
using namespace std;
void selection_sort(int a[], int n) {
for (int i = 0; i < (n - 1); i++) {
//总共查找n-1次最小值
int t = i;
//当前的最小值下标
for (int j = i + 1; j < n; j++) {
//寻找未排序部分的最小值下标。
if (a[j] < a[t]) {
t = j;
}
}
if (t != i) {
//如果第a[i]不是当前最小的
int temp = a[i];
a[i] = a[t];
a[t] = temp;
}
}
}
int main()
{
int a[] = { 5, 2, 4, 6, 8, 6, 4 };
for (int i : a) { cout << i << " "; }
cout << endl;
selection_sort(a, sizeof(a) / sizeof(a[0]));
for (int i : a) { cout << i << " "; }
cout << endl;
return 0;
}

三、插入排序

        概念:

        将一个元素插入到已经排好序的有序表中,从而得到一个新的、元素数增1的有序表。

        特点:

        ①插入排序是稳定的②进行 k次排序后前k+1个元素是有序的。

        复杂度:

        时间复杂度O(n²), 空间复杂度O(1)。

        代码演示:

#include <iostream>
using namespace std;
void snsertion_sort(int a[], int n) {
for (int i = 1; i < n; i++) {
//默认第一个元素a[0]是排好序的,所以从1开始
int t = i;
//待插入元素的位置
for (int j = i - 1; j >= 0; j--) {
//注意是从后往前比较,所以是j--
if (a[t] < a[j]) {
int temp = a[t];
a[t] = a[j];
a[j] = temp;
t = j;
//注意哦,下标也要变化
}
else {
break;
}
}
}
}
int main()
{
int a[] = { 5, 2, 4, 6, 8, 6, 4 };
for (int i : a) { cout << i << " "; }
cout << endl;
snsertion_sort(a, sizeof(a) / sizeof(a[0]));
for (int i : a) { cout << i << " "; }
cout << endl;
return 0;
}

四、希尔排序

        概念:

        希尔排序基于插入排序改进,先取一个小于n的整数d1作为第一个增量(一般是n/2),把文件的全部记录分组。所有距离为d1的倍数的记录放在同一个组中。先在各组内进行插入排序,然后,取第二个增量d2(一般取d1的一半),重复以上步骤直到增量为1进行最后一次插入排序。

        特点:

        ①希尔排序是不稳定的②每次排序都会将增量为d的元素排好序。

        复杂度:

        时间复杂度为O(nlog2n),空间复杂度为O(1)。

        代码演示:

#include <iostream>
using namespace std;
void shells_sort(int a[], int n) {
int d = n / 2;
while (d) {
//当d不为0时进行排序
for (int i = 0; i < d; i++) {
//分成d组
for (int j = i + d; j < n; j += d) {
//对每一组进行插入排序
int t = j;
for (int k = j - d; j >= i; j -= d) {
if (a[t] < a[k]) {
int temp = a[t];
a[t] = a[k];
a[k] = temp;
t = k;
}
else {
break;
}
}
}
}
d /= 2;
}
}
int main()
{
int a[] = { 5, 2, 4, 6, 8, 6, 4 };
for (int i : a) { cout << i << " "; }
cout << endl;
shells_sort(a, sizeof(a) / sizeof(a[0]));
for (int i : a) { cout << i << " "; }
cout << endl;
return 0;
}

五、归并排序

        概念:

       先将数组不断二分出子数组直到每个子数组的元素数目均为1,此时每个子数组都是有序的,然后两两合并有序的子数组,重复该合并步骤。(合并方式为:比较两个子数组的首个元素,将小的放进新的空间中,然后重复这一步骤)

        特点:

        ①归并排序是稳定的②每一次合并都会合并2的倍数个有序元素。即第i次合并后每2的i次方个元素是有序的。

        复杂度:

        时间复杂度O(nlogn),空间复杂度O(n)

        代码演示:

#include <iostream>
using namespace std;
void merge(int a[], int left, int middle, int right) {
int* b = new int[right - left + 1];
//创建一个数组b用来接收有序数组
int i = left, j = middle + 1;
int k = 0;
for (; (i <= middle) && (j <= right); ) {
//将小的元素按序存入直到一个子数组全存入
if (a[i] <= a[j]) {
//这里要用小于等于,不然可能会变成不稳定的排序
b[k++] = a[i++];
}
else {
b[k++] = a[j++];
}
}
if (i > middle) {
//将剩余的也按序存入
for (; j <= right; j++) { b[k++] = a[j]; }
}
else {
for (; i <= middle; i++) { b[k++] = a[i]; }
}
for (int i = left; i <= right; i++) { a[i] = b[i-left]; }
//赋值回数组a
delete[]b;
//new完先delete
}
void merge_sort(int a[], int left, int right) {
//归并排序,参数为数组,数组下标的最小值和最大值。
if (right <= left) { return; }
//当子数组长度为1时停止二分。
int middle = (left + right) / 2;
//先将数组不断二分
merge_sort(a, left,
middle);
merge_sort(a, middle+1, right);
//合并二分后的有序数组。
merge(a, left, middle, right);
}
int main()
{
int a[] = { 5, 2, 4, 6, 8, 6, 4 };
for (int i : a) { cout << i << " "; }
cout << endl;
merge_sort(a, 0, sizeof(a) / sizeof(a[0])-1);
//注意,是下标值,所以要减一
for (int i : a) { cout << i << " "; }
cout << endl;
return 0;
}

六、快速排序

        概念:

        先设置一个分界值(通常是第一个元素),将数组分为小于分界值和大于分界值两部分,重复这一步骤。

        特点:

        ①快速排序是一个不稳定排序②每排序一次,分界值左边的值都小于分界值,右边的值都大于分界值。

        复杂度:

        时间复杂度为O(nlog2n),空间复杂度为O(log2n)

        代码演示:

#include <iostream>
using namespace std;
void swap(int a[], int l, int r) {
int temp = a[l];
a[l] = a[r];
a[r] = temp;
}
void quick_sort(int a[], int left, int right) {
if (left >= right) { return; }
int l = left, r = right, t = left;
while (l < r) {
while (a[r] >= a[t]) { r--; }
if (r > l) { swap(a, t, r); t = r; }
else { break; }
while (a[l] <= a[t]) { l++; }
if (l < r) { swap(a, t, l); t = l; }
}
quick_sort(a, left, t - 1);
quick_sort(a, t+1, right);
}
int main()
{
int a[] = { 5, 2, 4, 6, 8, 6, 4 };
for (int i : a) { cout << i << " "; }
cout << endl;
quick_sort(a, 0, sizeof(a) / sizeof(a[0])-1);
//注意,是下标值,所以要减一
for (int i : a) { cout << i << " "; }
cout << endl;
return 0;
}

七、基数排序

        概念:

        先创建一个10个桶(编号从0到9)(可以是二维的动态数组),然后将数组中的元素按个位数的大小放入对应的桶中(这算第零轮)。找出数组中最大的位数k,重复以下操作k-1次:按顺序取出桶中元素(先取出编号为0的桶的所有元素,再取出为1的...),找出他们在该位数的数(第一轮就是十位数,第二轮就是百位数...)放入对应的新桶中(也是编号从0到9的10个桶)。重复完成后再遍历所有的桶,将值按序赋给数组。

        特点:

        ①基数排序是稳定的②每一轮的桶中都是按基数进行排序的。

        复杂度:

        时间复杂度O (nlog(r)m),其中r为所采取的基数(桶的数量),而m为堆数(最大值的位数),空间复杂度O(n+m),m为桶的数量。

        代码演示:

#include <iostream>
#include<vector>
using namespace std;
int maxbit(int a[], int n) {
int t = a[0], bit = 0;
//最大值与其位数
for (int i = 1; i < n; i++) {
//找出最大值
if (a[i] > t) { t = a[i]; }
}
while (t) {
//求位数
bit++;
t /= 10;
}
return bit;
}
void radix_sort(int a[], int n) {
vector<vector<int> > v(10);
//其实用链表存更好,但我懒。注意,这里第一行全是0.
for (int i = 0; i < n; i++) {
//先进行一轮,之后就不用对a操作了
int yu = a[i] % 10;
v[yu].emplace_back(a[i]);
}
int k = maxbit(a, n);
//先找出所有元素中最大值的位数
for (int i = 1; i < k; i++) {
//一共进行k轮(由于先进行了一轮,所以从1开始)
vector<vector<int> > v1(10);
for (int i1 = 0; i1 < v[0].size(); i1++) { v1[0].emplace_back(v[0][i]); }
for (int j = 1; j < 10; j++) {
//对数组v遍历,不用对v【0】的数操作。
int m = v[j].size();
for (int u = 0; u < m; u++) {
int t = (v[j][u] / (i * 10)) % 10;
//求余数
v1[t].emplace_back(v[j][u]);
}
}
v = v1;
}
int tm = 0;
for (int i = 0; i < 10; i++) {
for (int j = 0; j < v[i].size(); j++) {
a[tm++] = v[i][j];
}
}
}
int main()
{
int a[] = { 5, 2, 24, 16, 8, 6, 4, 54};
for (int i : a) { cout << i << " "; }
cout << endl;
radix_sort(a, sizeof(a) / sizeof(a[0]));
//注意,是下标值,所以要减一
for (int i : a) { cout << i << " "; }
cout << endl;
return 0;
}

八、堆排序

        概念:

        利用堆的性质所设计的(大根(顶)堆:父节点的值大于子节点的值)。将待排序的数组构造成一个大根堆,将顶端的数与末尾的数交换,此时末尾的数为最大值,再将剩下的数重复以上步骤。(第i个节点的子节点是2i+1和2i+2)。

        特点:

        ①堆排序是不稳定的②没进行一次堆排序后,最大值会被换到最后,且除了a[0]和a[n-1]外,a[i] > max(a[2*i+1], a[2*i+2])。

        复杂度:

        时间复杂度O(nlogn),空间复杂度O(1)。其中建堆的时间复杂度是O(n),之后的排序时间复杂度是O(log n)。

        代码演示:

#include <iostream>
#include<vector>
using namespace std;
void swap(int a[], int i, int j) {
int t = a[i];
a[i] = a[j];
a[j] = t;
}
void maxhead_sort(int a[], int n) {
//升序,所以是大根(顶)堆
if (n <= 1) { return; }
for (int i = (n - 2) / 2; i >= 0; i--) {
//最后一个节点下标是(n-2)/2。
int t = 2 * i + 1;
if (t < n - 1) {
if (a[2 * i + 1] < a[2 * i + 2]) { t += 1; }
}
if (a[t] > a[i]) { swap(a, i, t); }
}
swap(a, 0, n - 1);
//将最大的数移到最后
maxhead_sort(a, n - 1);
//剩余的数继续
}
int main()
{
int a[] = { 5, 2, 24, 16, 8, 6, 4, 54 };
for (int i : a) { cout << i << " "; }
cout << endl;
maxhead_sort(a, sizeof(a) / sizeof(a[0]));
//注意,是下标值,所以要减一
for (int i : a) { cout << i << " "; }
cout << endl;
return 0;
}

最后

以上就是机智鱼为你收集整理的数据结构—八种排序算法稳定性:一、冒泡排序二、选择排序三、插入排序四、希尔排序五、归并排序六、快速排序七、基数排序八、堆排序的全部内容,希望文章能够帮你解决数据结构—八种排序算法稳定性:一、冒泡排序二、选择排序三、插入排序四、希尔排序五、归并排序六、快速排序七、基数排序八、堆排序所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(51)

评论列表共有 0 条评论

立即
投稿
返回
顶部