数据结构—排序
数据结构这本书自己已经翻了好几遍,每次看都有不一样的收获,这几天花了些时间重新看了一下排序这一方面,虽然总的来说排序就那么几种,但是每一种排序代表了一种思想,还是要花时间认真看看的。
下面主要就是就是我学习的收获,下面贴的代码也主要是数据结构(严蔚敏)书上的,都是通过自己测试过的,通过写书上的代码也算是加深了一点认识吧!常用的内部排序算法主要分为五类:插入、交换、选择、归并、基数排序。这里也只分析内部排序不涉及到外部排序。
一、插入排序
在一个已经排好序的序列中,将未被排进的元素按照原先的规定插入到指定位置
1、直接插入排序
我觉得这是比较简单的排序方法,它的基本基本操作是将一个记录插入到已排好序的有序表中,从而得到一个新的、记录数增加1的有序表。
void InsertSort(SqList &L)
{ /* 对顺序表L作直接插入排序。*/
int i,j;
for(i=2;i<=L.length;++i)
if LT(L.r[i].key,L.r[i-1].key) /* "<",需将L.r[i]插入有序子表 */
{
L.r[0]=L.r[i]; /* 复制为哨兵 */
for(j=i-1;LT(L.r[0].key,L.r[j].key);--j)
L.r[j+1]=L.r[j]; /* 记录后移 */
L.r[j+1]=L.r[0]; /* 插入到正确位置 */
}
}
2、折半插入排序
这种插入排序是上一种排序的改进,因为是已经确定了前部分是有序序列,所以在查找插入位置的时候可以用折半查找的方法进行查找,提高效率,但是时间复杂度还是O(n²)
void BInsertSort(SqList &L)
{ /* 对顺序表L作折半插入排序。*/
int i,j,m,low,high;
for(i=2;i<=L.length;++i)
{
L.r[0]=L.r[i]; /* 将L.r[i]暂存到L.r[0] */
low=1;
high=i-1;
while(low<=high)
{ /* 在r[low..high]中折半查找有序插入的位置 */
m=(low+high)/2; /* 折半 */
if LT(L.r[0].key,L.r[m].key)
high=m-1; /* 插入点在低半区 */
else
low=m+1; /* 插入点在高半区 */
}
for(j=i-1;j>=high+1;--j)
L.r[j+1]=L.r[j]; /* 记录后移 */
L.r[high+1]=L.r[0]; /* 插入 */
}
}
3、2-路插入排序
2-路插入排序是在折半插入排序的基础上再改进的,其目的是减少排序过程中移动记录的次数,但需要n个记录的辅助空间
void P2_InsertSort(SqList &L)
{ /* 2_路插入排序 */
int i,j,first,final;
RedType &d;
d=(RedType*)malloc(L.length*sizeof(RedType)); /* 生成L.length个记录的临时
空间 */
d[0]=L.r[1]; /* 设L的第1个记录为d中排好序的记录(在位置[0]) */
first=final=0; /* first、final分别指示d中排好序的记录的第1个和最后1个记录的位
置 */
for(i=2;i<=L.length;++i)
{ /* 依次将L的第2个~最后1个记录插入d中 */
if(L.r[i].key<d[first].key)
{ /* 待插记录小于d中最小值,插到d[first]之前(不需移动d数组的元素) */
first=(first-1+L.length)%L.length; /* 设d为循环向量 */
d[first]=L.r[i];
}
else if(L.r[i].key>d[final].key)
{ /* 待插记录大于d中最大值,插到d[final]之后(不需移动d数组的元素) */
final=final+1;
d[final]=L.r[i];
}
else
{ /* 待插记录大于d中最小值,小于d中最大值,插到d的中间(需要移动d数组的元素)*/
j=final++; /* 移动d的尾部元素以便按序插入记录 */
while(L.r[i].key<d[j].key)
{
d[(j+1)%L.length]=d[j];
j=(j-1+L.length)%L.length;
}
d[j+1]=L.r[i];
}
}
for(i=1;i<=L.length;i++) /* 把d赋给L.r */
L.r[i]=d[(i+first-1)%L.length]; /* 线性关系 */
}
4、希尔排序
又称缩小增量排序法。把待排序序列分成若干较小的子序列,然后逐个使用直接插入排序法排序,最后再对一个较为有序的序列进行一次排序,主要是为了减少移动的次数,提高效率。原理应该就是从无序到渐渐有序,要比直接从无序到有序移动的次数会少一些
void ShellInsert(SqList &L,int dk)
{ /* 对顺序表L作一趟希尔插入排序。本算法是和一趟直接插入排序相比, */
/* 作了以下修改: */
/* 1.前后记录位置的增量是dk,而不是1; */
/* 2.r[0]只是暂存单元,不是哨兵。当j<=0时,插入位置已找到。*/
int i,j;
for(i=dk+1;i<=L.length;++i)
if LT(L.r[i].key,L.r[i-dk].key)
{ /* 需将L.r[i]插入有序增量子表 */
L.r[0]=L.r[i]; /* 暂存在L.r[0] */
for(j=i-dk;j>0&<(L.r[0].key,L.r[j].key);j-=dk)
L.r[j+dk]=L.r[j]; /* 记录后移,查找插入位置 */
L.r[j+dk]=L.r[0]; /* 插入 */
}
}
二、交换排序
通过交换逆序元素进行排序的方法
1、冒泡排序
冒泡排序也是一种很简单的排序,主要操作就是反复扫描待排序序列,在扫描的过程中顺次比较相邻的两个元素的大小,若逆序就交换位置。第一趟,从第一个数据开始,比较相邻的两个数据,(以升序为例)如果大就交换,得到一个最大数据在末尾;然后进行第二趟,只扫描前n-1个元素,得到次大的放在倒数第二位。以此类推,最后得到升序序列。如果在扫描过程中,发现没有交换,说明已经排好序列,直接终止扫描。所以最多进行n-1趟扫描
void bubble_sort(int a[],int n)
{ /* 将a中整数序列重新排列成自小至大有序的整数序列(起泡排序) */
int i,j,t;
Status change;
for(i=n-1,change=TRUE;i>1&&change;--i)
{
change=FALSE;
for(j=0;j<i;++j)
if(a[j]>a[j+1])
{
t=a[j];
a[j]=a[j+1];
a[j+1]=t;
change=TRUE;
}
}
}
2、快速排序
快速排序的时间复杂度达到O(nlgn),被公认为最快的排序方法之一。在所有同数量级O(nlgn)的排序当中,其平均性能最好。它其实是冒泡排序的改进,当一列数据基本有序的时候,快速排序将为蜕化为冒泡排序,时间复杂度为O(n平方)。基本思想是取一个数作为中间数,比它小的都排左边,比它大的都排右边(如果是从大到小排序的话,就反过来),再对每一边用同样的思路进行递归求解。快速排序是不稳定的排序方式。
int Partition(SqList &L,int low,int high)
{ /* 交换顺序表L中子表L.r[low..high]的记录,使枢轴记录到位, */
/* 并返回其所在位置,此时在它之前(后)的记录均不大(小)于它。*/
RedType t;
KeyType pivotkey;
pivotkey=L.r[low].key; /* 用子表的第一个记录作枢轴记录 */
while(low<high)
{ /* 从表的两端交替地向中间扫描 */
while(low<high&&(*L).r[high].key>=pivotkey)
--high;
t=L.r[low]; /* 将比枢轴记录小的记录交换到低端 */
L.r[low]=L.r[high];
L.r[high]=t;
while(low<high&&L.r[low].key<=pivotkey)
++low;
t=L.r[low]; /* 将比枢轴记录大的记录交换到高端 */
L.r[low]=L.r[high];
L.r[high]=t;
}
return low; /* 返回枢轴所在位置 */
}
void QSort(SqList &L,int low,int high)
{ /* 对顺序表L中的子序列L.r[low..high]作快速排序。*/
int pivotloc;
if(low<high)
{ /* 长度大于1 */
pivotloc=Partition(L,low,high); /* 将L.r[low..high]一分为二 */
QSort(L,low,pivotloc-1); /* 对低子表递归排序,pivotloc是枢轴位置 */
QSort(L,pivotloc+1,high); /* 对高子表递归排序 */
}
}
void QuickSort(SqList &L)
{ /* 对顺序表L作快速排序。*/
QSort(L,1,L.length);
三、选择排序
1、简单选择排序
简单选择排序思路也很简单,主要操作就是第一趟时,从第一个记录开始,通过n – 1次关键字的比较,从n个记录中选出关键字最小的记录,并和第一个记录进行交换。第二趟从第二个记录开始,选择最小的和第二个记录交换。以此类推,直至全部排序完毕。。时间复杂度也是 O(n^2),是不稳定的排序方式
int SelectMinKey(SqList L,int i)
{ /* 返回在L.r[i..L.length]中key最小的记录的序号 */
KeyType min;
int j,k;
k=i; /* 设第i个为最小 */
min=L.r[i].key;
for(j=i+1;j<=L.length;j++)
if(L.r[j].key<min) /* 找到更小的 */
{
k=j;
min=L.r[j].key;
}
return k;
}
void SelectSort(SqList &L)
{ /* 对顺序表L作简单选择排序。*/
int i,j;
RedType t;
for(i=1;i<L.length;++i)
{ /* 选择第i小的记录,并交换到位 */
j=SelectMinKey(&L,i); /* 在L.r[i..L.length]中选择key最小的记录 */
if(i!=j)
{ /* 与第i个记录交换 */
t=L.r[i];
L.r[i]=L.r[j];
L.r[j]=t;
}
}
}
2、堆排序
堆排序是一种常用到的排序方法,主要操作时把待排序记录的关键字存放在数组r[1…n]中,将r看成是一刻完全二叉树的顺序表示,每个节点表示一个记录,第一个记录r[1]作为二叉树的根,一下个记录r[2…n]依次逐层从左到右顺序排列,任意节点r[i]的左孩子是r[2i],右孩子是r[2i+1],双亲是r[i/2向下取整]。然后对这棵完全二叉树进行调整建堆,堆排序的时间复杂度是O(nlgn),也是最快的排序方法之一,在最坏的情况下,其时间复杂度还是O(nlgn),相对于快速排序来说,这是堆排序的最大优点。此外,堆排序仅需要一个记录大小供交换用的辅助存储空间。堆排序也是不稳定的排序。
void HeapAdjust(HeapType &H,int s,int m)
{ /* 已知H.r[s..m]中记录的关键字除H.r[s].key之外均满足堆的定义,本函数 */
/* 调整H.r[s]的关键字,使H.r[s..m]成为一个大顶堆(对其中记录的关键字而言) */
RedType rc;
int j;
rc=H.r[s];
for(j=2*s;j<=m;j*=2)
{ /* 沿key较大的孩子结点向下筛选 */
if(j<m&<(H.r[j].key,H.r[j+1].key))
++j; /* j为key较大的记录的下标 */
if(!LT(rc.key,H.r[j].key))
break; /* rc应插入在位置s上 */
H.r[s]=H.r[j];
s=j;
}
H.r[s]=rc; /* 插入 */
}
void HeapSort(HeapType &H)
{ /* 对顺序表H进行堆排序。*/
RedType t;
int i;
for(i=H.length/2;i>0;--i) /* 把H.r[1..H.length]建成大顶堆 */
HeapAdjust(H,i,H.length);
for(i=H.length;i>1;--i)
{ /* 将堆顶记录和当前未经排序子序列H.r[1..i]中最后一个记录相互交换 */
t=H.r[1];
H.r[1]=H.r[i];
H.r[i]=t;
HeapAdjust(H,1,i-1); /* 将H.r[1..i-1]重新调整为大顶堆 */
}
}
四、归并排序
归并排序的主要思想是假设初始序列右n个记录,首先将这n个记录看成n个有序的子序列,每个子序列的长度为1,然后两两归并,得到n/2向上取整 个长度为2(n为奇数时,最后一个序列的长度为1)的有序子序列。在此基础上,在对长度为2的有序子序列进行两两归并,得到若干个长度为4的有序子序列。如此重复,直至得到一个长度为n的有序序列为止。与快速排序相比,归并排序的最大特点是:它是一种稳定的排序方法
void Merge(RedType SR[],RedType TR[],int i,int m,int n)
{ /* 将有序的SR[i..m]和SR[m+1..n]归并为有序的TR[i..n] */
int j,k,l;
for(j=m+1,k=i;i<=m&&j<=n;++k) /* 将SR中记录由小到大地并入TR */
if LQ(SR[i].key,SR[j].key)
TR[k]=SR[i++];
else
TR[k]=SR[j++];
if(i<=m)
for(l=0;l<=m-i;l++)
TR[k+l]=SR[i+l]; /* 将剩余的SR[i..m]复制到TR */
if(j<=n)
for(l=0;l<=n-j;l++)
TR[k+l]=SR[j+l]; /* 将剩余的SR[j..n]复制到TR */
}
void MSort(RedType SR[],RedType TR1[],int s, int t)
{ /* 将SR[s..t]归并排序为TR1[s..t]。*/
int m;
RedType TR2[MAXSIZE+1];
if(s==t)
TR1[s]=SR[s];
else
{
m=(s+t)/2; /* 将SR[s..t]平分为SR[s..m]和SR[m+1..t] */
MSort(SR,TR2,s,m); /* 递归地将SR[s..m]归并为有序的TR2[s..m] */
MSort(SR,TR2,m+1,t); /* 递归地将SR[m+1..t]归并为有序的TR2[m+1..t] */
Merge(TR2,TR1,s,m,t); /* 将TR2[s..m]和TR2[m+1..t]归并到TR1[s..t] */
}
}
四、基数排序
基数排序
基数排序的主要思路是,将所有待比较数值(注意,必须是正整数)统一为同样的数位长度,数位较短的数前面补零,然后从最低位开始, 依次进行一次稳定排序.这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列,由于实现起来比较复杂,我还没写出来实现的代码,有兴趣的可以自己实现一下
分享到:
相关推荐
本资料重点讨论了数据结构中的排序问题及其实现。 排序是数据处理的基础操作之一,它将无序的元素序列转换为有序序列。在数据结构中,有多种排序算法,每种算法都有其独特的性能特征。例如,冒泡排序是一种简单的...
### 数据结构排序实验报告知识点解析 #### 实验背景与目的 - **实验背景**:本实验报告来源于南昌大学科学技术学院信息学科部计算机系的一次专业课程实验。《数据结构》作为一门重要的计算机基础课程,其目的在于...
总结来说,数据结构中的排序稳定性是一个关键特性,不同的排序算法根据其内部逻辑,有的能够保持相等元素的相对顺序(如冒泡排序、插入排序和归并排序),而有的则不能(如选择排序和快速排序)。在选择排序算法时,...
C++数据结构排序算法总结 在计算机科学中,排序算法是最基本和最重要的算法之一。在实际应用中,排序算法广泛应用于各种领域,如数据分析、机器学习、数据库管理等。C++数据结构排序算法总结将为您提供详细的排序...
### 数据结构之排序知识点解析 #### 一、题目背景与要求概述 本次解析的主题围绕着一个具体的排序场景:已知有8个初始归并段,它们的长度分别为10、20、25、30、45、12、16、2,现在需要用三条磁带(T0、T1、T2)...
在“数据结构实验总结”中,我们可以期待涵盖以下几个关键知识点: 1. **数组**:数组是最基础的数据结构,它允许我们通过索引来访问元素。在实验中,可能会涉及到一维、二维数组以及动态数组的概念,比如C语言中的...
文档格式是chm文档,方便查看,点击即可快速浏览排序算法,里面的程序可以直接拿来用,实现语言是标准的C程序。
数据结构各种算法总结,冒泡法等排序算法的比较,有助于更深刻的理解
根据给定的文件信息,我们可以总结出以下几个关键的数据结构与排序算法的知识点: ### 一、数据类型定义 首先,程序定义了一些基本的数据类型和结构体。这些定义为后续的操作提供了必要的基础。 #### 1.1 基本...
根据给定文件的信息,我们可以总结出以下关于“数据结构排序试验 C语言完整版”的相关知识点: ### 一、概述 该文档提供了一种基于C语言实现的数据结构排序实验的完整版本。通过该文档,读者可以详细了解多种排序...
这篇数据结构实验报告主要关注的是排序算法的实现和比较,特别是在链表数据结构上的应用。实验涉及了五种常见的排序算法:插入排序、冒泡排序、快速排序、简单选择排序,以及可能包括的其他排序算法。以下是这些算法...
总结来说,这个实验项目涵盖了数据结构的基础——排序算法,包括直接排序(如插入排序、选择排序和冒泡排序)和高级排序算法(快速排序)。通过实践,你可以深化对这些概念的理解,提升编程技能,并为未来在算法和...
### 数据结构中几种常用的排序算法总结 #### 一、引言 在计算机科学与数学领域,排序算法是一种能够按照特定顺序(如数值顺序或字典顺序)排列一系列数据的算法。排序算法对于许多其他算法(如搜索算法和合并算法)...
排序是数据结构和算法的重要组成部分,无论是插入排序还是交换排序,都有其适用的场景和优缺点。理解排序的基本概念和不同排序算法的工作原理,可以帮助我们在实际编程中选择最适合的排序方法,从而提高程序的效率和...
【排序算法】是计算机科学中一个重要的领域,它涉及到如何有效地组织和整理数据,以便于快速查找、访问和处理。本文将对几种常见的排序算法进行详细的解析,包括冒泡排序、交换排序、选择排序和插入排序,这些都是在...
数据结构排序超级总结.doc
将数据结构中的排序算法,如选择排序,冒泡排序,插入排序,希尔排序,快速排序,堆排序等排序算法做出总结,IT程序员笔试面试必备。
总结来说,堆排序是一种利用堆数据结构进行排序的有效方法,它包括了建堆和调整堆两个关键步骤。在C++中,堆排序可以通过自定义函数实现,如`heapify()`、`buildHeap()`和`heapSort()`。理解并实践这些函数的编写,...