排序的基本概念
排序是对数据元素序列建立某种有序排列的过程。更确切地说,排序是把一个数据元素序列整理成按关键字递增(或递减)排列的过程。
关键字分主关键字和次关键字两种。对要排序的数据元素集合来说,如果关键字满足数据元素值不同时该关键字的值也一定不同,这样的关键字称为主关键字。换句话说,主关键字是能够惟一区分各个不同数据元素的关键字。不满足主关键字定义的关键字称为次关键字。
排序分内部排序和外部排序两种。内部排序是把待排数据元素全部调入内存中进行的排序。如果数据元素的数量太大,需要分批导入内存,分批导入内存的数据元素排好序后再分批导出到磁盘和磁带等外存介质上的排序方法称作外部排序。外部排序算法的原理与内部排序算法的原理在很多地方都类同,但因内存的读写速度与外存的读写速度差别很大,所以评价标准差别很大。
插入排序
插入排序的基本思想是:从初始有序的子集合开始,不断地把新的数据元素插入到已排列有序的子集合的合适位置上,使子集合中数据元素的个数不断增多,当子集合等于集合时,插入排序算法结束。常用的插入排序有直接插入排序和希尔排序两种。
直接插入排序
直接插入排序的基本思想是:顺序地把待排序的数据元素按其关键字值的大小插入到已排序数据元素子集合的适当位置。子集合的数据元素个数从只有一个数据元素开始逐次增大,当子集合大小最终与集合大小相同时排序完毕。
Sort.h
typedef struct
{
KeyType key;
} DataType;
//直接插入排序
void InsertSort(DataType a[],int n)
{
int i,j;
DataType temp;
for(i = 0;i < n-1; i++)
{
temp = a[i+1];
j = i;
while(j > -1 && temp.key < a[j].key)
{
a[j+1] = a[j];
j--;
}
a[j+1] = temp;
}
}
main.c
#include <stdio.h>
typedef int KeyType;
#include "sort.h"
#define length 6
void print(DataType num[],int len);
void main(void)
{
DataType num[length] = {
64,5,7,89,6,24
};
print(num,length);
printf("\n");
InsertSort(num,length);
print(num,length);
printf("\n");
}
void print(DataType num[],int len)
{
int i;
for(i = 0;i < len; i++)
printf("%d\t",num[i]);
}
/*
64 5 7 89 6 24
5 6 7 24 64 89
Press any key to continue
*/
希尔排序
希尔(shell)排序的基本思想是:把待排序的数据元素分成若干个小组,对同一小组内的数据元素用直接插入法排序;小组的个数逐次缩小,当完成了所有数据元素都在一个组内的排序后排序过程结束。希尔排序又称作缩小增量排序。
//希尔排序
//用希尔排序法对元素a[0]--a[n-1]排序,d[0]--d[numOfD-1]
//为希尔增量值
void ShellSort(DataType a[],int n,int d[],int numOfD)
{
int i,j,k,m,span;
DataType temp;
for(m = 0;m < numOfD;m++) //共numOfD次循环
{
span = d[m]; //取本次的增量值
for(k = 0;k < span; k++) //共span个小组
{
//组内是直接插入排序,区别是每次不是增1而是增span
for(i = k; i < n-span; i = i + span)
{
temp = a[i + span];
j = i;
while(j > -1 && temp.key <= a[j].key)
{
a[j + span] = a[j];
j = j - span;
}
a[j + span] = temp;
}
}
}
}
main.c
#include <stdio.h>
typedef int KeyType;
#include "sort.h"
#define length 6
void print(DataType num[],int len);
void main(void)
{
DataType num[length] = {
64,5,7,89,6,24
};
int cap[2] = {1,3};
print(num,length);
printf("\n");
// InsertSort(num,length);
ShellSort(num,length,cap,2);
print(num,length);
printf("\n");
}
void print(DataType num[],int len)
{
int i;
for(i = 0;i < len; i++)
printf("%d\t",num[i]);
}
/*
64 5 7 89 6 24
5 6 7 24 64 89
Press any key to continue
*/
选择排序
选择排序的基本思想是:每次从待排序的数据元素集合中选取关键字最小(或最大)的数据元素放到数据元素集合的最前(或最后),数据元素集合不断缩小,当数据元素集合为空时选择排序结束。常用的选择排序有直接选择排序和堆排序两种。堆排序是一种基于完全二叉树的排序。
直接选择排序
直接选择排序的基本思想是:从待排序的数据元素集合中选取关键字最小的数据元素并将它与原始数据元素集合中的第一个数据元素交换位置;然后从不包括第一个位置的数据元素集合中选取关键字最小的数据元素并将它与原始数据集合中的第二个数据元素交换位置;如此重复,直到数据元素集合中只剩一个数据元素为止。
//直接选择排序
void SelectSort(DataType a[],int n)
{
int i,j,small;
DataType temp;
for(i = 0;i < n-1; i++)
{
small = i; //设第i个数据元素关键字最小
for(j = i + 1;j < n; j++) //寻找关键字最小的数据元素
if(a[j].key < a[small].key) small = j; //记住最小元素的下标
if(small != i) //当最小元素的下标不为i时交换位置
{
temp = a[i];
a[i] = a[small];
a[small] = temp;
}
}
}
main.c
#include <stdio.h>
typedef int KeyType;
#include "sort.h"
#define length 6
void print(DataType num[],int len);
void main(void)
{
DataType num[length] = {
64,5,7,89,6,24
};
print(num,length);
printf("\n");
SelectSort(num,length);
print(num,length);
printf("\n");
}
void print(DataType num[],int len)
{
int i;
for(i = 0;i < len; i++)
printf("%d\t",num[i]);
}
/*
64 5 7 89 6 24
5 6 7 24 64 89
Press any key to continue
*/
堆排序
在直接选择排序中,待排序的数据元素集合构成一个线性表结构,要从有n个数据元素的线性表中选择出一个最小的数据元素需要比较n-1次。如果能把待排序的数据元素集合构成一个完全二叉树结构,则每次选择出一个最大(或最小)的数据元素只需比较完全二叉树的数值为高度的次数,即log2n次,所以排序算法的时间复杂度就是O(nlog2n)。这就是堆排序的基本思想。
//堆排序
void CreateHeap(DataType a[],int n,int h)
{
int i,j,flag;
DataType temp;
i = h; //i为要建堆的二叉树根结点下标
j = 2 * i + 1; //j为i的左孩子结点的下标
temp = a[i];
flag = 0;
//沿左右孩子中值较大者重复向下筛选
while(j < n && flag != 1)
{
//寻找左右孩子结点中的较大者,j为其下标
if(j < n - 1 && a[j].key < a[j + 1].key) j++;
if(temp.key > a[j].key) //a[i].key > a[j].key
flag = 1; //标记结束筛选条件
else
{
a[i] = a[j];
i = j;
j = 2 * i + 1;
}
}
a[i] = temp; //把最初的a[i]赋予最后的a[j]
}
//初始化创建最大堆
void InitCreateHeap(DataType a[],int n)
{
int i;
for(i = (n - 1) / 2;i >= 0; i--)
CreateHeap(a,n,i);
}
//堆排序算法
void HeapSort(DataType a[],int n)
{
int i;
DataType temp;
InitCreateHeap(a,n); //初始化创建最大堆
for(i = n - 1;i > 0; i--) //当前最大堆个数每次递减1
{
//把堆顶a[0]元素与当前最大堆的最后一个元素交换
temp = a[0];
a[0] = a[i];
a[i] = temp;
CreateHeap(a,i,0); //调整根结点满足最大堆
}
}
main.c
#include <stdio.h>
typedef int KeyType;
#include "sort.h"
#define length 6
void print(DataType num[],int len);
void main(void)
{
DataType num[length] = {
64,5,7,89,6,24
};
print(num,length);
printf("\n");
HeapSort(num,length);
print(num,length);
printf("\n");
}
void print(DataType num[],int len)
{
int i;
for(i = 0;i < len; i++)
printf("%d\t",num[i]);
}
/*
64 5 7 89 6 24
5 6 7 24 64 89
Press any key to continue
*/
交换排序
利用交换数据元素的位置进行排序的方法称作交换排序。常用的交换排序方法有冒泡排序法和快速排序法。快速排序法是一种分区交换排序方法。
冒泡排序
//冒泡排序
void BubbleSort(DataType a[],int n)
{
int i,j,flag = 1;
DataType temp;
for(i = 1;i < n && flag == 1; i++)
{
flag = 0;
for(j = 0;j < n - i; j++)
{
if(a[j].key > a[j + 1].key)
{
flag = 1;
temp = a[j];
a[j] = a[j+1];
a[j+1] = temp;
}
}
}
}
main.c
#include <stdio.h>
typedef int KeyType;
#include "sort.h"
#define length 6
void print(DataType num[],int len);
void main(void)
{
DataType num[length] = {
64,5,7,89,6,24
};
print(num,length);
printf("\n");
BubbleSort(num,length);
print(num,length);
printf("\n");
}
void print(DataType num[],int len)
{
int i;
for(i = 0;i < len; i++)
printf("%d\t",num[i]);
}
/*
64 5 7 89 6 24
5 6 7 24 64 89
Press any key to continue
*/
快速排序
快速排序是一种二叉树结构的交换排序方法。快速排序算法的基本思想是:设数组a中存放了n个数据元素,low为数组的低端下标,high为数组的高端下标,从数组a中任取一个元素(通常取a[low])作为标准,调整数组a中各个元素的位置,使排在标准元素前面元素的关键字均小于标准元素的关键字,排在标准元素后面元素的关键字均大于或等于标准元素的关键字。这样一次过程结束后,一方面将标准元素放在了未来排好序的数组中该标准 元素应在的位置上,另一方面将数组中的元素以标准元素为中心分成了两个子数组,位于标准 元素左边子数组中元素的关键 字均小于标准元素的关键 字,位于标准 元素右边子数组中元素的关键字均大于等于标准元素的关键字。然后对这两个子数组中的元素分别再进行方法类同的递归快速排序。递归算法的出口条件是high > low
//快速排序
//用递归方法对数据元素a[low]--a[high]进行快速排序
void QuickSort(DataType a[],int low,int high)
{
int i = low,j = high;
DataType temp = a[low]; //取第一个元素为标准数据元素
while(i < j)
{
while(i < j && temp.key <= a[j].key) j--; //在数组的右端扫描
if(i < j)
{
a[i] = a[j];
i++;
}
while(i < j && a[i].key < temp.key) i++; //在数组的左端扫描
if(i < j)
{
a[j] = a[i];
j--;
}
}
a[i] = temp;
if(low < i) QuickSort(a,low,i - 1); //对左端子集合进行递归
if(i < high) QuickSort(a,j + 1,high); //对右端子集合进行递归
}
main.c
#include <stdio.h>
typedef int KeyType;
#include "sort.h"
#define length 6
void print(DataType num[],int len);
void main(void)
{
DataType num[length] = {
64,5,7,89,6,24
};
print(num,length);
printf("\n");
QuickSort(num,0,length-1);
print(num,length);
printf("\n");
}
void print(DataType num[],int len)
{
int i;
for(i = 0;i < len; i++)
printf("%d\t",num[i]);
}
/*
64 5 7 89 6 24
5 6 7 24 64 89
Press any key to continue
*/
归递排序
归并排序主要是二路归并排序。二路归并排序的基本思想是:设数组a中存放了n个数据元素,初始时把它们看成是n个长度为1的有序子数组,然后从第一个子数组开始,把相邻的子数组两两合并,得到n/2的整数上界个长度为2的新的有序子数组(当n为奇数时最后一个新的有序子数组的长度为1);对这些新的有序子数组再两两归并;如此重复,直到得到一个长度为n的有序数组为止。
//归并排序
//k为有序子数组的长度,一次二路归并排序后的有序子序列存于数组swap中
void Merge(DataType a[],int n,DataType swap[],int k)
{
int m = 0,u1,l2,i,j,u2;
int l1 = 0; //第一个有序子数组下界为0
while(l1 + k <= n - 1)
{
l2 = l1 + k; //计算第二个有序子数组下界
u1 = l2 - 1; //计算第一个有序子数组上界
u2 = (l2 + k - 1 <= n-1) ? l2 + k - 1 : n-1;
//计算第二个有序子数组上界
//两个有序子数组合并
for(i = l1,j = l2;i <= u1 && j <= u2; m++)
{
if(a[i].key <= a[j].key)
{
swap[m] = a[i];
i++;
}
else
{
swap[m] = a[j];
j++;
}
}
//子数组2已归并完,将子数组1中剩余的元素存放到数组swap中
while(i <= u1)
{
swap[m] = a[i];
m++;
i++;
}
//子数组1已归并完,将子数组2中剩余的元素存放到数组swap中
while(j <= u2)
{
swap[m] = a[j];
m++;
j++;
}
l1 = u2 + 1;
}
//将原始数组中只够一组的数据元素顺序存放到数组swap中
for(i = l1;i < n; i++,m++)
swap[m] = a[i];
}
void MergeSort(DataType a[],int n)
{
int i,k = 1; //归并长度从1开始
DataType* swap;
swap = (DataType*)malloc(sizeof(DataType)*n); //申请动态数组空间
while(k < n)
{
Merge(a,n,swap,k); //调用归并函数
for(i = 0;i < n; i++)
a[i] = swap[i]; //将元素从临时数组swap放回数组中
k = 2 * k; //归并长度加倍
}
free(swap); //释放动态数组空间
}
main.c
#include <stdio.h>
typedef int KeyType;
#include "sort.h"
#define length 6
void print(DataType num[],int len);
void main(void)
{
DataType num[length] = {
64,5,7,89,6,24
};
print(num,length);
printf("\n");
MergeSort(num,length);
print(num,length);
printf("\n");
}
void print(DataType num[],int len)
{
int i;
for(i = 0;i < len; i++)
printf("%d\t",num[i]);
}
/*
64 5 7 89 6 24
5 6 7 24 64 89
Press any key to continue
*/
基数排序
基数排序也称作桶排序,是一种当关键字为整数类型时非常高效的排序方法。
基数排序算法的基本思想是:设待排序的数据元素关键字是m位d进制整数(不中m位的关键字在高位补0),设置d个桶,令其编号分别为0,1,2,…,d-1。首先,按关键字最低位的数值依次把各数据元素放到相应的桶中;然后,按照桶号从小到大和进入桶中数据元素的先后次序收入集分配在各桶中的数据元素;这样,就形成了数据元素集合的一个新的排列,称这样的一次排序过程为一次基数排序。再对一次基数排序得到的数据元素序列按关键 字次低位的数值依次把各数据元素放到相应的桶中,然后按照桶号从小到大和进入桶中数据元素的先后次序收入集分配在各桶中的数据元素。这样的过程重复进行,当完成了第 m次基数排序后,就得到了排好序的数据元素序列。
LinQueue.h
typedef struct qnode {
DataType data;
struct qnode * next;
}LQNode;
typedef struct {
LQNode* front; //队头指针
LQNode* rear; //队尾指针
}LQueue;
//1.初始化
void QueueInitiate(LQueue*Q)
{
Q->rear = NULL; //定义初始队尾指针
Q->front = NULL; //定义初始队头指针
}
//2.非空否
int QueueNotEmpty(LQueue Q)
{
//判断链式队列Q非空否,非空返回1,否则返回0
if(Q.front == NULL) return 0;
else return 1;
}
//3.入队列
int QueueAppend(LQueue*Q,DataType x)
{
//把数据元素值x插入链式队列Q的队尾,入队列成功返回1,否则返回0
LQNode*p;
if((p = (LQNode*)malloc(sizeof(LQNode))) == NULL)
{
printf("内存空间不中!");
return 0;
}
p->data = x;
p->next = NULL;
if(Q->rear != NULL) Q->rear->next = p;
Q->rear = p;
if(Q->front == NULL) Q->front = p;
return 1;
}
//4.出队列
int QueueDelete(LQueue*Q,DataType*d)
{
//删除链式队列Q的队头数据元素值到d,出队列成功返回1,否则返回0
LQNode*p;
if(Q->front == NULL)
{
printf("队列已空无数据元素出队列!\n");
return 0;
}
else
{
*d = Q->front->data;
p = Q->front;
Q->front = Q->front->next;
if(Q->front == NULL) Q->rear = NULL;
free(p);
return 1;
}
}
//5.取队头数据元素
int QueueGet(LQueue Q,DataType*d)
{
//取链式队列Q的当前队头数据元素值到d,成功返回1,否则返回0
if(Q.front == NULL)
{
printf("队列已空无数据元素出队列!\n");
return 0;
}
*d = Q.front->data;
return 1;
}
//6.撤消动态申请空间
void Destroy(LQueue Q)
{
LQNode*p,*p1;
p = Q.front;
while(p != NULL)
{
p1 = p;
p = p->next;
free(p1);
}
}
//基数排序
//对数据元素a[0]--a[n-1]进行关键字为m位d进制整型数值的基数排序
//桶采用链式队列
void RadixSort(DataType a[],int n,int m,int d)
{
int i,j,k,power = 1;
LQueue* tub;
//把d个队列定义为动态数组
tub = (LQueue*)malloc(sizeof(LQueue)*d);
for(i = 0;i < d; i++) //d个队列初始化
QueueInitiate(&tub[i]);
//进行m次放和收
for(i = 0;i < m; i++)
{
if(i == 0) power = 1;
else power = power * d;
//将数据元素按关键字第k位的数值放到相应的队列中
for(j = 0;j < n; j++)
{
k = a[j].key / power - (a[j].key / (power * d)) * d;
QueueAppend(&tub[k],a[j]); //把a[j]放入相应的队列中
}
//顺序回收各队列中的数据元素至数组a中
k = 0;
for(j = 0;j < d; j++)
while(QueueNotEmpty(tub[j]) != 0)
{
QueueDelete(&tub[j],&a[k]); //从各队列中回收
k++;
}
}
}
main.c
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
typedef int KeyType;
typedef struct
{
KeyType key;
} DataType;
#include "LinQueue.h"
#include "sort.h"
#define length 6
void print(DataType num[],int len);
void main(void)
{
DataType num[length] = {
64,5,7,89,6,24
};
print(num,length);
printf("\n");
RadixSort(num,length,2,10);
print(num,length);
printf("\n");
}
void print(DataType num[],int len)
{
int i;
for(i = 0;i < len; i++)
printf("%d\t",num[i]);
}
/*
64 5 7 89 6 24
5 6 7 24 64 89
Press any key to continue
*/