目录

插入排序

交换排序

选择排序

归并排序

常用排序算法复杂度和稳定性总结


前言

排序是《数据结构》中最基本的学习内容。排序算法可以分为内部排序和外部排序,内部排序是数据记录在内存中进行排序。而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存。常见的内部排序算法有:插入排序、希尔排序、选择排序、冒泡排序、归并排序、快速排序、堆排序、基数排序等。知识框架如下:

插入排序

插入排序是一种简单直观的排序方法,其基本思想在于每次将一个待排序记录,按其关键字大小插入到前面已经排好序的子序列中,直到全部记录插入完成。

直接插入排序

直接插入排序把要排序的序列分成两部分:第一部分是有序子序列,而第二部分是无序序列。每次挑选无序序列中的第一个元素与第一部分的有序子序列从后往前逐个比较,当待排元素大于子序列中的元素时,将其插入到该元素的后方。直接插入排序会使用一个 "哨兵",用于存放元素。直接插入排序每次插入一个元素,所以排序趟数固定为 n-1。

空间复杂度:O(1)    时间复杂度:O(n^2)    稳定性:稳定

#include <stdio.h>void Insertsort(int a[], int low, int high)
{int i, j;int temp;for(i = low+1; i <= high; i++){temp = a[i];for(j = i-1; (j >= low)&&(a[j] > temp); j--)a[j+1] = a[j];a[j+1] = temp;}
}int main(int argc, char *argv[])
{int i,a[10];printf("输入10个数:");for(i = 0; i < 10; i++)scanf("%d", &a[i]);insertsort(a, 0, 9);printf("排序结果是:");for(i = 0; i < 10; i++)printf("%d ", a[i]);printf("\n");return 0;
}

折半插入排序

折半插入排序仅仅是减少了比较元素的次数,约为 O(nlogn),该比较次数与待排序列的初始状态无关,仅取决于序列中的元素个数n;而元素的移动次数没有改变,它依赖于待排序列的初始状态。因此,折半插入排序的时间复杂度仍是 O(n^2)。

空间复杂度:O(1)     时间复杂度:O(n^2)     稳定性:稳定

#include <stdio.h>void BinaryInsertSort(int *arry, int n)
{int i, j;int high, low, mid;int temp;for(i = 2; i < n; i++){arry[0] = arry[i];low = 1;high = i-1;while (low <= high){mid = (low+high)/2;if (arry[mid] > arry[0])high = mid-1;else if (arry[mid] <= arry[0])low = mid+1;}for(j = i-1; j >= low; j--){arry[j+1] = arry[j];}arry[low] = arry[0];}
}int main(int argc, char *argv[])
{int a[] = {0,2,45,7,8,45,3,6,0};int iLen =sizeof(a)/sizeof(a[0]);for(int i = 1; i < iLen; i++)printf("%d ", a[i]);printf("\n");BinaryInsertSort(a, iLen);for(int i = 1; i < iLen; i++)printf("%d ", a[i]);printf("\n");return 0;
}

希尔排序

希尔排序是插入排序的一种又称 "缩小增量排序",是直接插入排序算法的一种更高效的改进版本。基本思想:先取一个小于 n 的整数 d1 作为第一个增量,把文件的全部记录分组。所有距离为 d1 的倍数的记录放在同一个组中。先在各组内进行直接插入排序;然后,取第二个增量 d2<d1 重复上述的分组和排序,直至所取的增量  = 1(<…<d2<d1),即所有记录放在同一组中进行直接插入排序为止。

空间复杂度:O(1)     时间复杂度:O(n^2)     稳定性:不稳定

#include <stdio.h>void ShellInsertSort(int *arry, int n, int d)
{int i,j;for(i = d+1; i < n; i++){if (arry[i-d] > arry[i]){arry[0] = arry[i];for(j = i-d; j > 0 && arry[0] < arry[j]; j = j-d){arry[j+d] = arry[j];}arry[j+d] = arry[0];}}
}void ShellSort(int *arry, int n, int d)
{int i;for(i = d; i > 0; i--)ShellInsertSort(arry, n, i);
}int main(int argc, char *argv[])
{int a[] = {0,2,4,7,8,1,3,6,0};int iLen = sizeof(a)/sizeof(a[0]);for(int i = 1; i < iLen; i++)printf("%d ", a[i]);printf("\n");ShellSort(a, iLen, 4);for(int i = 1; i < iLen; i++)printf("%d ", a[i]);printf("\n");return 0;
}

交换排序

交换排序就是根据序列中两个元素关键字的比较结果来对换这两个记录在序列中的位置。常用的交换排序算法有冒泡和快排。交换类的排序,其趟数和原始序列状态有关。

冒泡排序

冒泡排序算法的基本思想是:假设待排序列长为 n,从前往后比较相邻元素的值,若为逆序,则交换它们,直到序列比较完。每趟冒泡至少把序列中的一个元素放到序列的最终位置,且最多做 n-1 趟冒泡就可以把所有元素排好序。注意:冒泡排序中所产生的有序子序列一定是全局有序(不同于直接插入排序),也就是说有序子序列中的所有元素的关键字一定小于或大于无序序列中所有元素的关键字,这样每一趟排序都会将一个元素放置到其最终的位置上。

空间复杂度:O(1)     时间复杂度:O(n^2)     稳定性:稳定

#include <stdio.h>
void bubblesort1(int *arry, int n)
{int i, j, k;int temp, flag;for(i = 0; i < n; i++){    flag = 0;for(j = 0; j < n-i-1; j++){if(arry[j] > arry[j+1]){temp = arry[j];arry[j] = arry[j+1];arry[j+1] = temp;flag = 1;}}if(flag == 0)  break;}
}void bubblesort2(int a[], int n)   //双向冒泡排序
{int low = 0,high = n-1;int i, t, flag = 1;while (low < high && flag){flag = 0;for(i = low;i < high; i++){if (a[i] > a[i+1]){t = a[i];a[i] = a[i+1];a[i+1] = t; flag  = 1;}}high--;for(i = high;i > low; i--){if (a[i] < a[i-1]){t = a[i-1];a[i-1] =  a[i];a[i] = t;flag = 1;}}low++;}
}int main(int argc, char *argv[])
{int a[10]={5,4,8,7,9,5,4,6,3,2};int i;for(i=0;i<10;i++)printf("%d ",a[i]);bubblesort1(a,10);printf("\n");for(i=0;i<10;i++)printf("%d ",a[i]);printf("\n");return 0;
}

快速排序

快排算法是基于分治策略的排序算法,其基本思想是,对于输入的数组 a[low, high],按以下三个步骤进行排序。

(1) 分解:以 a[p] 为基准将a[low: high]划分为三段 a[low:p-1],a[p] 和 a[p+1:high],使得 a[low:p-1] 中任何一个元素小于等于 a[p], 而 a[p+1: high] 中任何一个元素大于等于 a[p]。

(2) 递归求解:通过递归调用快速排序算法分别对 a[low:p-1] 和 a[p+1:high] 进行排序。

(3) 合并:由于对 a[low:p-1] 和 a[p+1:high] 的排序是就地进行的,所以在 a[low:p-1] 和 a[p+1:high] 都已排好序后,不需要执行任何计算,a[low:high] 就已经排好序了。

想要更详细的了解快排,可以看这篇文章:快速排序的4种优化

空间复杂度:O(logn)     时间复杂度:O(nlogn)     稳定性:不稳定

快排动图(网上找的动图,其中有一个基准为 6 的标识错误。虽然基准选择方法不一样,但排序过程还是一样的):

#include <stdio.h>int Partition(int a[], int low, int high)
{int i,j,k,temp;i = low; j = high+1;k = a[low];while(1){while(a[++i] < k && i < j);while(a[--j] > k);if(i >= j) break;else{temp = a[i];a[i] = a[j];a[j] = temp;}}a[low] = a[j];a[j] = k;return j;
}void QuickSort(int a[], int low, int high)
{if(low < high){int q = Partition(a, low, high);QuickSort(a, low, q-1);QuickSort(a, q+1, high);}
}int main(int argc, char *argv[])
{int i;int a[10] = {3,4,5,6,1,2,0,7,8,9};QuickSort(a, 0, 9);for(i = 0; i < 10; ++i)printf("[%d]", a[i]);printf("\n");return 0;
}

选择排序

选择排序是一种简单直观的排序算法。它的工作原理是:第一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后再从剩余的未排序元素中寻找到最小(大)元素,然后放到已排序的序列的末尾。以此类推,直到全部待排序的数据元素的个数为零。选择排序是不稳定的排序方法。

简单选择排序

空间复杂度:O(1)     时间复杂度:O(n^2)     稳定性:不稳定

#include <stdio.h>
void selectionsort(int a[], int n)
{int i,j,k,t;for(i = 0; i < n-1; i++){k = i;for(j=i+1;j<n;j++){if(a[j] < a[k])k = j;}if(k != i){t = a[i];a[i] = a[k];a[k] = t;}}
}int main(int argc, char *argv[])
{int i, a[10];for(i = 0; i < 10; i++)scanf("%d", &a[i]);selectionsort(a, 10);for(i = 0; i < 10; i++)printf("%d ", a[i]);printf("\n");return 0;
}

堆排序

堆排序是指利用堆这种数据结构所设计的一种排序算法。分为两种方法:大顶堆:每个节点的值都大于或等于其子节点的值,在堆排序算法中用于升序排列;小顶堆:每个节点的值都小于或等于其子节点的值,在堆排序算法中用于降序排列;

想要更详细的了解堆,可以看这篇文章:通俗易懂的讲解堆排序(含Gif图)

空间复杂度:O(1)     时间复杂度:O(nlogn)     稳定性:不稳定

#include <stdio.h>
#include <math.h>void heap_ajust_min(int *b, int i, int size)  //a为数组,size为堆的大小
{int lchild = 2*i;        //i的左孩子节点序号 int rchild = 2*i +1;     //i的右孩子节点序号 int min = i;             //记录根和左右子树中最小的数的下标int temp;if(i <= size/2)          //调整不需要从叶结点开始{if(lchild<=size && b[lchild]<b[min]){min = lchild;}    if(rchild<=size && b[rchild]<b[min]){min = rchild;}                    //两个if语句寻找三个结点中最小的数if(min != i)         //如果min不等于i,说明最小的值在左右子树中{temp = b[i];     //交换a[i]和a[min]的值b[i] = b[min];b[min] = temp;heap_ajust_min(b, min, size); //被交换的子树可能不满足堆的定义,需要对被交换的子树重新调整}}
}void build_heap_min(int *b, int size)      //建立小根堆
{int i;for(i = size/2; i >= 1; i--){          //非叶子节点最大序号值为size/2,从这个结点开始调整                        heap_ajust_min(b, i, size);        //注意for中的循环条件(i = size/2; i >= 1; i--)     }
}void heap_sort_min(int *a, int size)
{int i;int temp;for(i = size; i >= 1; i--){temp = a[1];a[1] = a[i];a[i] = temp;               //交换堆顶和最后一个元素heap_ajust_min(a, 1, i-1); //再一次调整堆顶节点成为小顶堆}
} int main(int argc, char *argv[])
{int a[] = {0,5,8,45,9,36,35,22,46,37,10,79,100,63,12,18,77,88,50,99,95};int size = sizeof(a)/sizeof(int) -1;int i,j;int count = 1;build_heap_min(a, size);printf("小顶堆:\n"); for(i = 0; i <= 4; i++){for(j = 0; j < pow(2,i); j++){if(count <= size){printf("%d ", a[count++]);}else{break;}}printf("\n");}printf("\n");heap_sort_min(a, size);printf("堆排序之后的序列为:\n");for(i = 1; i <= size; i++)printf("%d ", a[i]);printf("\n");return 0;
}

归并排序

归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。归并排序是一种稳定的排序方法。注意:一般而言,对于 N 个元素进行 k-路 归并排序时,排序的趟数 m 满足 k^m = N,从而 m = logk(N)向上取整。

空间复杂度:O(n)     时间复杂度:O(nlogn)     稳定性:稳定

#include <iostream>
using namespace std;
int *temp;//将两个非降序序列low--mid,mid+1--high合并为一个新的非降序序列
void Merge(int a[], int low, int mid, int high)
{int len = high - low + 1;int i = low, j = mid+1;    //i,j分别为两个子序列的游标int k = 0;                 //为新合并序列的游标while(i <= mid && j <= high){if(a[i] <= a[j]){temp[k++] = a[i++];}else{temp[k++] = a[j++];}}while(i <= mid){    //若第一个子序列有剩余,则直接接到尾部temp[k++] = a[i++];}while(j <= high){    //若第二个子序列有剩余,则直接接到尾部temp[k++] = a[j++];}//copy到a[]for(k = 0; k < len; k++)a[low+k] = temp[k];
}//low high为待排序列左右边界
void MergeSort(int a[], int low, int high)
{if(low < high){int mid = (low + high)/2;MergeSort(a, low, mid);     //递归的划分左右两个子序列MergeSort(a, mid+1, high);Merge(a, low, mid, high);   //合并}
}int main()
{int a[10] = {9,8,7,6,5,4,3,2,1,0};temp = new int[10];MergeSort(a, 0, 9);for(int i = 0; i < 10; i++)cout << a[i] <<" ";cout <<endl;delete []temp;return 0;
}

常用排序算法复杂度和稳定性总结

动态图片来源于:

https://blog.csdn.net/qq_42453117/article/details/99680831

https://www.cnblogs.com/fivestudy/p/10064969.html

数据结构——排序算法(含动态图片)相关推荐

  1. 数据结构---排序算法的总结

    数据结构-排序算法的总结 分类 冒泡排序,时间复杂度O(n x n),空间复杂度O(1),稳定 简单选择排序,时间复杂度O(n x n),空间复杂度O(1),不稳定 希尔排序,时间复杂度O(n^1.3 ...

  2. 【数据结构排序算法系列】数据结构八大排序算法

    排序算法在计算机应用中随处可见,如Windows操作系统的文件管理中会自动对用户创建的文件按照一定的规则排序(这个规则用户可以自定义,默认按照文件名排序)因此熟练掌握各种排序算法是非常重要的,本博客将 ...

  3. 数据结构-排序算法总结与感悟

    数据结构-排序算法总结 一,排序的基本概念 排序:有n个记录的序列{R1,R2,-,Rn},其相应关键字的序列是{K1,K2, -,Kn },相应的下标序列为1,2,-, n.通过排序,要求找出当前下 ...

  4. C++基础-介绍·数据结构·排序·算法

    C++基础-介绍·数据结构·排序·算法 特点 使用方向 RPC Data Struct 数据结构 栈 Stack 内存分配中的栈 队列 List 数组 Array 链表 LinkTable 树 Tre ...

  5. 数据结构-排序算法(c语言实现篇)

    数据结构-排序算法(c语言实现篇) 排序算法是非常常用的算法,从介绍排序的基本概念,到介绍各种排序算法的思想.实现方式以及效率分析.最后比较各种算法的优劣性和稳定性. 1 排序的概念及应用 1.1 排 ...

  6. java array 元素的位置_数据结构与算法:动态图解十大经典排序算法(含JAVA代码实现)...

    点击上方"JAVA",星标公众号 重磅干货,第一时间送达 本文将采取动态图+文字描述+正确的java代码实现来讲解以下十大排序算法: 冒泡排序 选择排序 插入排序 希尔排序 归并排 ...

  7. 数据结构排序算法 内部排序(冒泡、鸡尾酒、选择、简单插入、二分插入、快排、希尔、归并、堆排)C语言实现

    文章目录 排序 冒泡排序 鸡尾酒排序 选择排序: 简单插入排序: 二分插入排序 快速排序: 希尔排序: 归并排序: 堆排序: 排序 点击以下图片查看大图: 冒泡排序 1.比较相邻的元素,如果前一个比后 ...

  8. 数据结构排序算法实验报告_数据结构与算法-堆排序

    堆排序 堆排序是指利用堆这种数据结构所设计的一种排序算法.堆是一个近似完全二叉树的结构,并同时满足堆的性质:即子节点的键值或索引总是小于(或者大于)它的父节点,堆排序的时间复杂度为O(nlogn).( ...

  9. 选择排序稳定吗_最常见的四种数据结构排序算法你不知道?年末怎么跳槽涨薪...

    前言 在学习数据结构的时候必然接触过排序算法,而且在日常开发中相信排序算法用得也比较多.而排序算法众多,各个效率又不同,难以记住.小编今天会介绍一些常用排序算法的特点和实现,对比不同排序算法的效率. ...

最新文章

  1. MySQL之主从切换
  2. 用什么表示python异常_求大神给讲讲python的异常问题!以下是我的代码!没看出来有什么不同的呀?...
  3. ORACLE百万记录SQL语句优化技巧
  4. OPEN ERP相关财务的基本概念
  5. python3 系统监控脚本(CPU,memory,网络,disk等)
  6. 【oracle】dbms_output:控制台输出
  7. 设置tomcat自动启动的相关脚本
  8. 《给年青数学人的信》:为何学数学?
  9. 从外网 SSH 进局域网,反向代理+正向代理解决方案
  10. Tensorflow 十六 用tf.keras函数重写Mnist识别
  11. Fragment生命周期及其使用
  12. Leetcode每日一题:88.merge-sorted-array(合并两个有序数组)
  13. opencart出现no such file or dictionary
  14. 14.初步解析document的核心元数据以及图解剖析index创建反例
  15. Tapestry 教程(四)探索项目结构
  16. Atitit 编程语言语言规范总结 语法部分 目录 1. 语言规范 3 2. Types 3 2.1.1. Primitive types 3 2.1.2. Compound types 4 3.
  17. 鲲鹏BoostKit虚拟化使能套件,让数据加密更安全
  18. 玩转流量,天下无锅——IT运维人员的九阳神功(上)| 技术分享
  19. 忘记网站后台密码 PHP+mysql+md5 破解
  20. ListView嵌套viewpager 头部图片轮换,listview 进行下拉刷新

热门文章

  1. Zookeeper 安装和配置---学习三
  2. 透过汇编另眼看世界之函数调用
  3. 给Source Insight做个外挂系列之二--将本地代码注入到Source Insight进程
  4. 排序算法——插入排序
  5. 0_0 SimpleFactoryMode 简单工厂模式
  6. 监听以太网(三) Packet32数据结构说明
  7. 让程序在崩溃时体面的退出之SEH
  8. Go命令行库Cobra的使用
  9. JAVA通信编程(三)——TCP通讯
  10. 写 Go 时如何优雅地查文档