上一篇大概写了序列的查找算法,这次就聊聊序列的几种重要的排序(大神自动飘过~~~)

一、算法分析

1.1 直接插入排序

基本思想:将文件中的记录分为有序区、无序区,不断地从无序区中顺序提取记录,按关键字的大小插入到有序区中的适当位置,直到无序区为空。
直接插入排序的性能指标: 最小的比较次数: 约为 n, 最多的比较次数: 约为 n^2/2, 最小的移动次数: 约为 n, 最多的移动次数: 约为 n^2/2, 时间复杂度: O(n^2), 算法是稳定的。

1.2 折半排序

对半插入排序在寻找插入位置时,不是逐个比较而是利用折半查找的原理寻找插入位置。待排序元素越多,改进效果越明显。
对分插入排序的性能指标: 比较次数明显减少, 移动次数: 约 n^2/2, 时间复杂度: O(n^2),空间复杂度:O(1), 算法是不稳定的。

1.3 选择排序

首先从1-n个元素中选出关键字最小的记录交换到第一个位置上;然后再从第2个到第n个元素中选出次小的记录交换到第二个位置上,依次类推。

平均时间复杂度为O(n^2),空间复杂度:O(1) ,算法是不稳定的,适用于待排序元素较少的情况。

1.4 堆排序

堆是具有特定条件的顺序存储的完全二叉树,其特定条件是:任何一个非叶子结点的关键字大于等于(或小于等于)子女的关键字的值。
堆排序的算法思想:
首先设法把原始序列构造成一个堆,使得n个元素的最大值处于序列的第一个位置;然后交换序列第一个元素(最大值元素)与最后一个元素。再把序列的前n-1个元素组成的子序列构
成一个新堆,得到第二大元素,把序列的第一个元素与第n-1个元素交换。此后再把序列的前n-2个元素构成一个新堆,如此操作,最终整个序列成为有序序列。 
平均时间复杂度为:O(nlog2n) ,空间复杂度:O(1) ,算法是不稳定的
大家如果对二叉树不是很熟悉,可以看看上篇文章二叉树的基本性质

1.4.1由无序建初始堆的过程( 25,56,49,78,11,65,41,36 ),如下图:

1.4.2 筛选算法(构造堆)

设关键字集合为{k0, k1,…, kn-1},以kj为根的子树均满足堆的定义(j=i+1,i+2,…, ),筛选算法结束后以ki为根的子树也满足堆的定义。 n个记录存放在数组t[]中,代码如下:
void SIFT(table t[],int len ,int k){table t1;int i,j;i = k;j = 2 * i;t1 = t[i];/*t1 是 t[i]的左节点*/while(j < len){if((j < (len -1)) && (t[j].key < t[j+1].key ))j++;  if(t1.key < t[j].key)  // child 指向Ri的左、右子女中排序码较大的结点{t[i] = t[j];i = j;j = 2 * i;/*进入下一层继续调整*/}elsebreak;}t[i] = t1;// 将记录Ri放入正确位置}

1.4.3堆排序算法

先将无序序列构造为堆,然后将堆顶与最后一记录交换,将最后记录删除后,重新构造堆,反复进行,直到堆空为止,代码如下:
void heapSort(table t[],int len){  int i;  table t1;  /*建立初始堆*/  for(i = len/2 -1;i >= 0; i--){  SIFT(t,len,i);  }  /* 进行n-1趟堆排序*/  for(i = len -1;i>0;i--){  /* 堆顶和最后一个互换 */  t1 = t[0];  t[0] = t[i];  t[i] = t1;  SIFT(t,i,0);// 从R0到Ri-1重建堆  }
}  


1.5 冒泡排序

冒泡排序,若按升序进行,则小的浮起,大的沉底;
第一趟:第1个与第2个记录比较,逆序则交换;第2个与第3个记录比较,逆序则交换;直到对最后两个记录比较、交换完成;一趟排序完成,会有一个极值存储到最终位置。

第二趟:缩小待排序区,对省余的n-1个记录进行同,样的操作,关键字次大(小)的记录交换到,第n-1个位置上;依次类推,则完成排序。
对于初始为正序的文件,比较次数为 n-1,移动次数为 0,时间复杂度为 O(n);
对于初始为逆序的文件,比较次数为 O(n^2),移动次数为 O(n^2),平均时间复杂度为 O(n^2);
适合于数据较少的情况,排序n个记录的文件最多需要n-1趟冒泡排序。算法稳定。

1.6 快速排序

算法描述:附设两个指针low和high,初值分别指向第一个记录和最后一个记录,设关键字为 key,首先从high所指位置起向前搜索,找到第一个小于基准值的记录与基准记录交换,然后从low所指位置起向后搜索,找到第一个大于基准值的记录与基准记录交换,重复这两步直至low=high为止。
快速排序的算法分析:比较次数: O(nlog2n) ,平均时间复杂度:O(nlog2n) ,空间复杂度:O(log2n) ,算法是不稳定的,快速排序有可能出现最坏情况,这时快速排序算法的时间复杂度为O(n2),且递归深度为n,即所需栈空间为O(n)。

1.7 归并排序

按每次归并的子文件数量可以分为二路归并,三路归并等等~~~这里就着重聊聊二路归并~~~
基本思想:把具有n个记录的表看成是n个有序的子表,每个子表的长度为1,然后两两归并,得到[n/2]个长度为2或为1的有序子表;再两两归并,如此重复,直到得到一个长度为n的有序表为止。
归并算法分析:平均时间复杂度:O(nlog2n) ,空间复杂度:O(n) ,算法是稳定的。

1.7.1两组归并算法

首先是两组归并算法。设t1[low]到t1[mid]和t1[mid+1]到t1[high]是存储在同一个数组中且相邻的两个有序的子文件,现在将这两个子文件合并为一个有序的文件,并存储在t2[low]到t2[high]中。

void Merge(table t1[],table t2[],int low ,int mid, int hight){
int i,j,k;
i = low;
j = mid + 1;
k = low;
while(( i <= mid) && (j <= hight)){
if(t1[i].key < t1[j].key){
t2[k++] = t1[i++];
}
else
t2[k++] = t1[j++];
}
/*将剩余的数组复制到新的数组中*/
while(i <=mid)
t2[k++] = t1[i++];
while(j <= hight)
t2[k++] = t1[j++];
}

1.7.2一趟归并算法

设各子文件长度为len(最后一个子文件长度可能小于len),则归并前t1[0]到t1[n-1]中共有[n/len]个有序的子文件∶t1[0]到t1[len-1],t1[len]到t1[2*len-1],......,,t1[(-1)*length]到t1[n-1],调用归并操作将相邻的一对子文件归并。

void MergePass(table t1[],table t2[],int n, int len){
int i,j;
i = 0;
while((i + 2*len - 1) < n){
Merge(t1,t2,i,(i+len - 1),(i + 2*len -1));/*并归两个长度为len的子文件到t2中*/
i += 2*len;
}
if(( i + len -1 ) < (n-1))/*剩下两个子文件,其中一个长度小于len*/
Merge(t1,t2,i,i+len - 1,n-1);
else
for(j = i;j < n ;j++)/*将最后一个子文件复制到t2数组中*/
t2[j] = t1[j];
}

1.7.3 二路归并算法

二路归并排序就是多次调用“一趟归并”过程,每趟归并后有序子文件的长度length扩大一倍。

void MergeSort(table t[],int len){
table record[MAX];
int lenght = 1;/*子文件长度*/
while(lenght < MAX){
MergePass(t,record,len,lenght);
lenght *=2;
MergePass(record,t,len,lenght);
lenght *=2;
}
}

二、代码分析

分析完算法,下面直接上干粮~~~

#include<stdio.h>
#define MAX 10//数组长度
typedef struct table table;struct table{int key;int other;
};
table t[MAX];/*直接插入,len 为数组长度*/
void insert(table t[],int len){int i,j;/*i 的前面是已排序,i的后面还未排序*/table flag;for(i = 1 ;i < len;i++){       /*每次将i后面的第一个插入到i的前面已排序列*/if(t[i].key < t[i-1].key){flag = t[i];j = i-1;while( (flag.key < t[j].key) && (j >= 0)){/*按升序排列*/t[j+1] = t[j];j--;}t[j+1] = flag;}}
}
/*折半插入,len为数组长度*/
void BinsertSort(table t[],int len){int i,j,low,hight,mid;table t1;for(i = 1;i < len;i++){t1 = t[i];/*i 为要排序的点*/low = 0;hight = i-1;while(low <= hight){mid = (low + hight)/2;if(t1.key < t[mid].key)hight = (mid -1);elselow = mid + 1;}for(j = i-1;j >=low;j--)t[j + 1] = t[j];t[low] = t1;}
}
/*选择排序,len是数组长度*/
void selectSort(table t[],int len){int i,j,k;table t1;for(i = 0;i < len; i++ ){k = i;/*k 是i轮最小的数*/for(j = i+1;j< len;j++){if(t[j].key < t[k].key)k = j;}/*将最小的换到i处*/if(k!=i){t1 = t[k];t[k] = t[i];t[i] = t1;}}
}
/*构造堆,len是数组长度,k是树根*/
void SIFT(table t[],int len ,int k){table t1;int i,j;i = k;j = 2 * i;t1 = t[i];/*t1 是 t[i]的左节点*/while(j < len){if((j < (len -1)) && (t[j].key < t[j+1].key ))j++;  if(t1.key < t[j].key)  // child 指向Ri的左、右子女中排序码较大的结点{t[i] = t[j];i = j;j = 2 * i;/*进入下一层继续调整*/}elsebreak;}t[i] = t1;// 将记录Ri放入正确位置}
/*堆排序算法*/
void heapSort(table t[],int len){int i;table t1;/*建立初始堆*/for(i = len/2 -1;i >= 0; i--){SIFT(t,len,i);}/* 进行n-1趟堆排序*/for(i = len -1;i>0;i--){/* 堆顶和最后一个互换 */t1 = t[0];t[0] = t[i];t[i] = t1;SIFT(t,i,0);// 从R0到Ri-1重建堆}
}
/*冒泡排序*/
void bubSort(table t[],int len){int i,j = 1,flag = 1;table t1;while((j < len ) && (flag > 0)){flag =0;/*记录第j次比较中有多少个错位的,若没有则停止排序*/for(i = 0;i < len -j;i++){if(t[i].key > t[i +1].key){flag++;t1 = t[i];t[i] = t[i +1];t[i + 1] = t1;}}j++;}
}
/*快速排序*/
void quickSort(table t[],int low,int hight){int i,j;i = low;j = hight;if(i >= j) return;table t1;t1 = t[i];/*将小于t1的都放在前面,大于t1的都在后面*/while(i != j){/*从高处向低处扫*/while((t1.key <= t[j].key) && (j>i))j--;if(i <j)t[i] = t[j];/*从低处向高处扫*/while((t[i].key <= t1.key ) && (j>i)){i++;}if(i < j)t[j] = t[i];}t[i] = t1;quickSort(t,low,i-1);quickSort(t,i+1,hight);
}
/*两组并归算法,将t1 并归到 t2数组中*/
void Merge(table t1[],table t2[],int low ,int mid, int hight){int i,j,k;i = low;j = mid + 1;k = low;while(( i <= mid) && (j <= hight)){if(t1[i].key < t1[j].key){t2[k++] = t1[i++];}elset2[k++] = t1[j++];}/*将剩余的数组复制到新的数组中*/while(i <=mid)t2[k++] = t1[i++];while(j <= hight)t2[k++] = t1[j++];
}
/*一趟并归,n 为数组长度,len为子文件长度,将t1 并归到 t2数组中*/
void MergePass(table t1[],table t2[],int n, int len){int i,j;i = 0;while((i + 2*len - 1) < n){Merge(t1,t2,i,(i+len - 1),(i + 2*len -1));/*并归两个长度为len的子文件到t2中*/i += 2*len;}if(( i + len -1 ) < (n-1))/*剩下两个子文件,其中一个长度小于len*/ Merge(t1,t2,i,i+len - 1,n-1);elsefor(j = i;j < n ;j++)/*将最后一个子文件复制到t2数组中*/t2[j] = t1[j];
}
/*二路并归算法*/
void MergeSort(table t[],int len){table record[MAX];int lenght = 1;/*子文件长度*/while(lenght < MAX){MergePass(t,record,len,lenght);lenght *=2;MergePass(record,t,len,lenght);lenght *=2;}
}
void init(table t[],int len){int n[10] = {15,25,10,2,100,58,46,12,45,45};int i;for(i = 0;i < MAX;i++){t[i].key = n[i];}printf("\n\n\n原数组: \n\n");for(i = 0 ;i < MAX;i++){printf("[ %d ] ",t[i].key);}
}
void show(table t[]){int i;printf("\n");for(i = 0 ;i < MAX;i++){printf("[ %d ] ",t[i].key);}
}
void main(){init(t,MAX);printf("\n直接插入排序:\n");insert(t,MAX);show(t);init(t,MAX);printf("\n折半插入排序:\n");BinsertSort(t,MAX);show(t);init(t,MAX);printf("\n 选择插入:\n");selectSort(t,MAX);show(t);init(t,MAX);printf("\n 堆排序:\n");heapSort(t,MAX);show(t);init(t,MAX);printf("\n冒泡排序:\n");bubSort(t,MAX);show(t);init(t,MAX);printf("\n快速排序:\n");quickSort(t,0,(MAX-1));show(t);init(t,MAX);printf("\n二路并归排序:\n");MergeSort(t,MAX);show(t);
}

C语言基本数据结构之五(折半插入,堆排序,冒泡排序,快速排序,并归排序)相关推荐

  1. 【八大排序详解~C语言版】直接插入排序-希尔排序- 直接选择排序-堆排序-冒泡排序-快速排序-归并排序-计数排序

    八大排序 1.直接插入排序 2.希尔排序 3.直接选择排序 直接选择排序改进 4.堆排序 1.建堆 2.利用堆删除思想来进行排序 5.冒泡排序 6.快速排序 递归实现 非递归实现 7.归并排序 递归实 ...

  2. 数据结构(八):排序 | 插入排序 | 希尔排序 | 冒泡排序 | 快速排序 | 简单选择排序 | 堆排序 | 归并排序 | 基数排序 | 外部排序 | 败者树 | 置换-选择排序 | 最佳归并树

    文章目录 第八章 排序 一.排序的基本概念 (一)什么是排序 (二)排序的应用 (三)排序算法的评价指标 (四)排序算法的分类 (五)总结 二.插入排序 (一)算法思想 (二)算法实现 (三)算法效率 ...

  3. 直接插入排序 希尔排序 冒泡排序 快速排序 直接选择排序 堆排序 归并排序 基数排序的算法分析和具体实现 ...

    排序分为内部排序和外部排序 内部排序是把待排数据元素全部调入内存中进行的排序. 外部排序是因数量太大,把数据元素分批导入内存,排好序后再分批导出到磁盘和磁带外存介质上的排序方法. 比较排序算法优劣的标 ...

  4. 数据结构:直接插入排序 希尔排序 选择排序 堆排序 冒泡排序 快速排序 归并排序

    一.什么是排序 排序就是将一组杂乱无章的数据按照一定的次序组织起来,此次序可以是升序也可以是降序 二.为什么需要进行排序 为了满足一些需求,比如在比较学生的成绩时,我们就需要给所有学生的成绩排一个顺序 ...

  5. 七大排序算法—图文详解(插入排序,希尔排序,选择排序,堆排序,冒泡排序,快速排序,归并排序)

    作者:渴望力量的土狗 博客主页:渴望力量的土狗的博客主页 专栏:数据结构与算法 工欲善其事必先利其器,给大家介绍一款超牛的斩获大厂offer利器--牛客网 点击免费注册和我一起刷题吧 目录 插入排序: ...

  6. 【数据结构(C语言)】数据结构-内部排序

    内部排序 文章目录 内部排序 一.概述 (1)排序定义 (2)稳定性 (3)内部排序和外部排序 (4)两种基本操作 (5)数据类型定义 二.分类 (1)插入排序 (2)交换排序 (3)选择排序 (4) ...

  7. 考研专业课c语言与数据结构,南开大学816 C语言与数据结构2018考研专业课大纲...

    南开大学816 C语言与数据结构2018考研专业课大纲 2017-09-29 14:41 | 考研集训营 南开大学2018考研专业课大纲已经公布,每个学校自专业课考试大纲及专业课考试内容不一样,小编会 ...

  8. 【数据结构(C语言)】数据结构-查找

    查找 文章目录 查找 一.基本概念 1.查找表 2.关键字 3.查找 二.查找算法的性能分析 1.平均查找长度(Average Search Length) 三.基于线性表的查找 1.存储结构的定义 ...

  9. C语言向有序数组中插入一个数使该数组仍保持有序

    C语言向有序数组中插入一个数使该数组仍保持有序 #include<stdio.h> int main() {     int n,i,j,t,k;     printf("您喜欢 ...

最新文章

  1. 昇腾AI 软硬件全栈平台
  2. BZOJ4590 [Shoi2015]自动刷题机
  3. Java3y文章目录导航
  4. Sharepoint学习笔记—Ribbon系列-- 3.在Ribbon中找到正确的Location
  5. 模型视图控制器mvc
  6. 安卓登录以及会话保持的解决方案
  7. Holer实现手机APP应用外网访问本地WEB应用
  8. 2.11 计算机视觉现状
  9. 前端入门-day2(常见css问题及解答)
  10. golang实现AES ECB模式的加密和解密
  11. a标签加onclick点击事件
  12. Android.mk转换到Android.bp方法
  13. 华为USG防火墙配置
  14. CASE WHEN 及 SELECT CASE WHEN的用法
  15. Spring Tool Suite4安装和配置
  16. 灵格斯:很好很强大的免费电子辞典
  17. android控制wifi,基于 Android 手机操作和控制的 Wifi 小车程序设计
  18. oracle基础|什么是数据字典|数据字典的作用
  19. 三维地理信息可视化·城市篇 技术解析
  20. 树状数组 之 poj 3067

热门文章

  1. matlab 方波_matlab实现方波与三角波波形
  2. supervisor 守护多个进程_supervisor守护进程管理实操笔记
  3. java 音乐api接口_关于网易云音乐爬虫的api接口?
  4. java按钮监听休眠_java-休眠监控解决方案
  5. 13、计算机图形学——阴影贴图
  6. C++中static关键字用法
  7. 曲面拟合之最小二乘法(矩形域)
  8. 数据查询分页 获取总数时间太长_干货:SQL Server 查询语句
  9. cds.data:=dsp.data赋值有时会出现AV错误剖析
  10. Effective_STL 学习笔记(二十八) 了解如何通过 reverse_iterator 的 base 得到 iterator...