在很多情况下,有序表比无序表的效率要高得多。对于排序,不仅是对于普通的数字编号进行排序,对于抽象的文章,图片,视频等等信息都可进行排序。(这里的排序不是说给它们编号在对编号排序,而是真正对其所包含的关键字排序,这种规律是可以联系到其本身的含义的!)由于编者水平有限,以下介绍的均为对数据的排序。
对于一个表想让其转化成有序表,则需要表中每个数据项都有可以相互比较的元素。为此引入关键字,通过关键字的比较排列,使整个表呈非递增和非递减的规律;这种加了关键字的数据元素被定义为“记录”。
typedef int KeyType; //为简单起见,定义关键字类型为整型
typedef struct {
KeyType key; //关键字项
InfoType otherinfo; //其他数据项
}RcdType; //记录类型
此为定义记录时的内容
排序根据过程中涉及的存储器不同可分为 (1),内部排序:排序过程不使用计算机外部存储器.(2),外部排序:排序过程中需要调用外存。
const MAXSIZE = 20; //一个用作示例的小顺序表的最大长度
typedef struct {
RcdType r[MAXSIZE + 1]; //r[0]闲置或作为判别标志的“哨兵”单元
int length; //顺序表排序的记录空间为r[1..length]
}SqList; //顺序表类型
此为顺序表,其中的每个元素都是RcdType定义的记录
选择排序
对于选择排序,思想很简单,关键是如何建立一个存储数据为记录的顺序表。步骤如下:一,对于记录和顺序表进行定义,(定义顺序表存储数组时还是先定义指向记录的指针。)二,建表,即为顺序表分配内存空间,同时初始化表长为零。三,对于表中数据的赋值,可以在主函数中进行,赋值时千万不要忘记增加表长。最后对其进行排序就完成了。
同时需要注意的一点是,顺序表中 L.r[0] 作为哨兵单元,不存储数据,所以在排序循环中要注意循环变量的起始值和范围的确定。
#include<stdio.h>
/******************************************************************************
对记录这个数据类型的定义
******************************************************************************/
typedef int KeyType;
typedef struct {
KeyType key;
char data;
}RcdType;
/*******************************************************************************
对每一个元素都是记录的顺序表的定义
********************************************************************************/
const int MAXSIZE = 10;
typedef struct {
RcdType *r; //r[0]是哨兵单元,不记录数值 r[i]表示记录
int length; //表示顺序表的实时长度
}SqList;
/*********************************************************************************
建表
**********************************************************************************/
void InitList_L(SqList &L)
{ //定义过数据类型后引用时要先给它分配内存空间
L.r = new RcdType[MAXSIZE + 1];
L.length = 0; //初始化表长为0
}
/********************************************************************************
排序
*********************************************************************************/
void SelectSort(SqList &L)
{
//对顺序表L进行选择排序
int i, j, k;
RcdType w; //用于交换的辅助变量
for (i = 1; i < L.length; i++)
{
j = i;
for (k = i + 1; k <= L.length; k++)
if (L.r[k].key < L.r[j].key)
j = k;
if (i != j)
{
w = L.r[j];
L.r[j] = L.r[i];
L.r[i] = w;
}
}
}
void main()
{
int i;
SqList L;
InitList_L(L);
char a[MAXSIZE] = { 'Y','L','V','X','O','L','!','I','U','E' };
int key[MAXSIZE] = { 3,5,7,2,6,1,10,4,9,8 };
for (i = 1; i <= MAXSIZE; i++)
{
L.r[i].data = a[i - 1];
L.r[i].key = key[i - 1];
L.length++; //千万不要忘记每输入一个记录,表长要加一
}
printf("排序前顺序表中数据为:");
for (i = 1; i <= MAXSIZE; i++)
{
printf("%c ", L.r[i].data);
}
printf("\n");
SelectSort(L);
printf("排序后顺序表中数据为:");
for (i = 1; i <= MAXSIZE; i++)
printf("%c ", L.r[i].data);
}
插入排序
插入排序的思路就是,定义循环变量 i 和 j ,最初让 i 从第二个数据开始,与前一个数据进行比较,如果 i 较小,则将 i 放到哨兵单元待定。然后令 j 从 i 前面一个数据开始,循环将前面的数据后移,然后 j 减一,后移循环的条件是哨兵单元处的数据小于 j 此时的数据。当跳出此循环时说明哨兵单元的数据比 j 此时所指的数据和前面的数据都大,是时候将哨兵单元的数据插回到表中。由于判断循环条件前 j 减了一,所以将哨兵单元数据插回到 j 的后一位,即 L.r[j + 1] = L.r[0]; 此处 j + 1 就很细节。
#include<stdio.h>
/******************************************************************************
对记录这个数据类型的定义
******************************************************************************/
typedef int KeyType;
typedef struct {
KeyType key;
char data;
}RcdType;
/*******************************************************************************
对每一个元素都是记录的顺序表的定义
********************************************************************************/
const int MAXSIZE = 10;
typedef struct {
RcdType *r; //r[0]是哨兵单元,不记录数值 r[i]表示记录
int length; //表示顺序表的实时长度
}SqList;
/*********************************************************************************
建表
**********************************************************************************/
void InitList_L(SqList &L)
{ //定义过数据类型后引用时要先给它分配内存空间
L.r = new RcdType[MAXSIZE + 1];
L.length = 0; //初始化表长为0
}
/********************************************************************************
排序
*********************************************************************************/
void InsertSort(SqList &L)
{
//对顺序表进行插入排序
int i, j;
for (i = 2; i <= L.length; i++)
if (L.r[i].key < L.r[i - 1].key)
{
L.r[0] = L.r[i]; //将r[i]复制到哨兵单元中
for (j = i - 1; L.r[0].key < L.r[j].key; j--)
L.r[j + 1] = L.r[j]; //记录后移
L.r[j + 1] = L.r[0];
}
}
void main()
{
int i;
SqList L;
InitList_L(L);
char a[MAXSIZE] = { 'Y','L','V','X','O','L','!','I','U','E' };
int key[MAXSIZE] = { 3,5,7,2,6,1,10,4,9,8 };
for (i = 1; i <= MAXSIZE; i++)
{
L.r[i].data = a[i - 1];
L.r[i].key = key[i - 1];
L.length++; //千万不要忘记每输入一个记录,表长要加一
}
printf("排序前顺序表中数据为:");
for (i = 1; i <= MAXSIZE; i++)
{
printf("%c ", L.r[i].data);
}
printf("\n");
InsertSort(L);
printf("排序后顺序表中数据为:");
for (i = 1; i <= MAXSIZE; i++)
printf("%c ", L.r[i].data);
}
冒泡排序
冒泡排序即相邻的两个数据进行比较,较大的那个数排在两者中的后者,然后下标加一继续排列,这样一趟下来,整个序列中最大的数就会被交换到最后的位置,该过程小的数向前移动,抽象为冒泡。为了提高算法的效率,设置lastExchangeIndex记录最后一次交换发生时的下标,此时该下标的后续数据不经历交换意味着已经排序完成。
#include<stdio.h>
/******************************************************************************
对记录这个数据类型的定义
******************************************************************************/
typedef int KeyType;
typedef struct {
KeyType key;
char data;
}RcdType;
/*******************************************************************************
对每一个元素都是记录的顺序表的定义
********************************************************************************/
const int MAXSIZE = 10;
typedef struct {
RcdType *r; //r[0]是哨兵单元,不记录数值 r[i]表示记录
int length; //表示顺序表的实时长度
}SqList;
/*********************************************************************************
建表
**********************************************************************************/
void InitList_L(SqList &L)
{ //定义过数据类型后引用时要先给它分配内存空间
L.r = new RcdType[MAXSIZE + 1];
L.length = 0; //初始化表长为0
}
/********************************************************************************
排序
*********************************************************************************/
void BubbleSort(SqList &L)
{
//对顺序表L作起泡排序
RcdType W;
int i, j;
int lastExchangeIndex; //用于记录相邻两个数据比较大小时最后一次交换位置时的下标
i = L.length;
while (i > 1) //i>1 表明上一趟曾进行过记录的交换
{
lastExchangeIndex = 1;
for (j = 1; j < i; j++)
{
if (L.r[j + 1].key < L.r[j].key)
{
W = L.r[j];
L.r[j] = L.r[j + 1];
L.r[j + 1] = W;
lastExchangeIndex = j;
}
}
i = lastExchangeIndex; //一趟排列中无序序列中最后一个记录的位置
}
}
void main()
{
int i;
SqList L;
InitList_L(L);
char a[MAXSIZE] = { 'Y','L','V','X','O','L','!','I','U','E' };
int key[MAXSIZE] = { 3,5,7,2,6,1,10,4,9,8 };
for (i = 1; i <= MAXSIZE; i++)
{
L.r[i].data = a[i - 1];
L.r[i].key = key[i - 1];
L.length++; //千万不要忘记每输入一个记录,表长要加一
}
printf("排序前顺序表中数据为:");
for (i = 1; i <= MAXSIZE; i++)
{
printf("%c ", L.r[i].data);
}
printf("\n");
BubbleSort(L);
printf("排序后顺序表中数据为:");
for (i = 1; i <= MAXSIZE; i++)
printf("%c ", L.r[i].data);
}
但是形象地来说,本算法应该叫下沉排序,因为每一趟最大的数都落在了最后,若要变成真正的冒泡排序,应该从序列的末尾数据进行比较。此时 j 由末尾数据开始向上遍历,初始 j = L.length; 然后初始 lastExchangeIndex = L.length; 而 i 初始值为 1,当 while(i < L.length) 时进入大循环。其中 i 用于记录由下向上排列时上方有序序列的最大末尾下标 当i的值为L.length时,表示已经排列好整个序列。
#include<stdio.h>
/******************************************************************************
对记录这个数据类型的定义
******************************************************************************/
typedef int KeyType;
typedef struct {
KeyType key;
char data;
}RcdType;
/*******************************************************************************
对每一个元素都是记录的顺序表的定义
********************************************************************************/
const int MAXSIZE = 10;
typedef struct {
RcdType *r; //r[0]是哨兵单元,不记录数值 r[i]表示记录
int length; //表示顺序表的实时长度
}SqList;
/*********************************************************************************
建表
**********************************************************************************/
void InitList_L(SqList &L)
{ //定义过数据类型后引用时要先给它分配内存空间
L.r = new RcdType[MAXSIZE + 1];
L.length = 0; //初始化表长为0
}
/********************************************************************************
排序
*********************************************************************************/
void BubbleSort(SqList &L)
{
//对顺序表L作起泡排序
RcdType W;
int i, j;
int lastExchangeIndex; //用于记录相邻两个数据比较大小时最后一次交换位置时的下标
i = 1;
while (i < L.length) //i用于记录由下向上排列时上方有序序列的最大末尾下标 当i的值为L.length时,表示已经排列好整个序列
{
lastExchangeIndex = L.length;
for (j = L.length; j > i; j--)
{
if (L.r[j].key < L.r[j-1].key)
{
W = L.r[j];
L.r[j] = L.r[j-1];
L.r[j-1] = W;
lastExchangeIndex = j;
}
}
i = lastExchangeIndex; //i用于记录由下向上排列时上方有序序列的最大末尾下标
}
}
void main()
{
int i;
SqList L;
InitList_L(L);
char a[MAXSIZE] = { 'Y','L','V','X','O','L','!','I','U','E' };
int key[MAXSIZE] = { 3,5,7,2,6,1,10,4,9,8 };
for (i = 1; i <= MAXSIZE; i++)
{
L.r[i].data = a[i - 1];
L.r[i].key = key[i - 1];
L.length++; //千万不要忘记每输入一个记录,表长要加一
}
printf("排序前顺序表中数据为:");
for (i = 1; i <= MAXSIZE; i++)
{
printf("%c ", L.r[i].data);
}
printf("\n");
BubbleSort(L);
printf("排序后顺序表中数据为:");
for (i = 1; i <= MAXSIZE; i++)
printf("%c ", L.r[i].data);
}
本笔记所依据的教材为严薇敏版的《数据结构及应用算法教程》
所有代码在Visual Studio 2017上均可正常运行
如有错误欢迎指出