概念
- 排序
- 内部排序:指的是待排序记录存放在计算机随机存储器中进行的排序过程(不考虑读写硬盘)。而外部排序指的是当排序记录的数量很大,以致内存一次不能容纳全部记录,在排序过程中尚需对外存进行访问的排序过程。
- 排序的稳定性:
- 两个相同的关键字Ri与Rj,在排序前Ri在Rj的前面,排序之后, Ri依然在Rj之前,我们称这种排序方法是稳定的,反之,是不稳定的。
即 相同元素的前后顺序是否发生变化 - 排序的稳定性用于评价某个排序方法,而不是某一次排序
- 两个相同的关键字Ri与Rj,在排序前Ri在Rj的前面,排序之后, Ri依然在Rj之前,我们称这种排序方法是稳定的,反之,是不稳定的。
- 排序表的定义
#define MAXSIZE 20
typedef int KeyType; //定义关键字类型为 int
typedef struct {
KeyType key; //关键字项
InfoType otherinfo; //其它数据项
}RedType; //记录类型
typedef struct {
RedType r[MAXSIZE+1]; //r[0]闲置或用作哨兵单元
int length; //顺序表的长度
}SqList; //顺序表类型
插入排序
一、直接插入排序
现将序列中第一个记录看成一个有序子序列,然后从第二个记录开始,逐个进行插入,直至整个序列有序
稳定
T(n) = O(n2)
S(n) = O(1)
** n-1趟**
- 一趟步骤
- 在 R[1 … i -1] 中查找 R[i] 的插入位置,
R[1 … j].key <= R[i].key < R[ j+1 … i -1].key; - 将 R[ j+1 … i -1] 中的所有记录均后移一个位置;
- 将R[i]插入(复制)到R[j+1]的位置上
- demo
/*从小到大*/
void InsertSort(SqList L) {
int i,j;
for(i = 2 ;i<=L->length;++i) {
if(L->r[i].key<L->r[i-1].key) {//i之前的都是有序的,所以只需要和i的前一个比较即可
L->r[0] = L->r[i];//复制为监视哨
L->r[i] = L->r[i-1];
for(j = i-2;L->r[i].key<L->r[j].key;--j) {
L->r[j+1] = L->r[j];//记录后移
}
L->r[j+1] = L->r[0];//插入到正确的位置
}
}
}
二、折半插入排序
稳定
T(n) = O(n2)
S(n) = O(1)
- 减少了比较次数,移动次数不变
- 仅从结果来看,与直接插入排序无异
三、希尔排序(缩小增量排序)
不稳定
T(n) = O(n1.3)
先做宏观调整,再做微观调整
- 先取一个正整数d1<n,把所有相隔d1的记录放在一组内,组内进行直接插入排序;
- 然后取d2<d1,重复上述分组和排序操作
- 直至di = 1,即所有的记录都放进一个组中排序为止
- di称为增量
快速排序
四、起泡排序
稳定
T(n) = O(n2)
S(n) = O(1)
** n-1趟**
- demo
void BubbleSort(SqList L) {
int change = true;//交换开关
int i,j;
ElenType temp;
for(i = 1;i<L->length&&change;i++) {
change = false;
for(j = 1;j<=L->length-i;j++) {
if(L->a[j]>L->a[j+1]) {
temp = L->a[j];
L->a[j] = L->a[j+1];
L->a[j+1] = temp;
change = true;
}
}
}
}
五、快速排序
稳定
T(n) = O(nlnn) 但是最怀情况是O(n^2),受到基值的影响,涉及到一个算法
- 快速排序被认为是同数量级**O(nlogn)**中平均性能最好的排序方法
- 快速排序是对起泡排序的一种改进,有序且逆序的时候会蜕变为起泡排序(快排依赖于数组的初始状态)
- 任选一个记录,以它的关键字作为“枢纽”,凡关键字小于枢纽的记录均移到枢纽之前,凡关键字大于枢纽的记录均移至枢纽之后
- 附设两个指针low和high,从high所指的位置起向前搜索找到第一个关键字小于枢纽的关键字的记录与枢纽记录交换,然后从low所指位置起向后搜索找到第一个关键字大于枢纽的关键字的记录与枢纽记录交换
- demo
//一趟
int Partition(SqList L,int low,int high) {
//交换顺序表L中子表r[low..high]的记录,枢纽记录到位,并返回其所在的位置,此时在它之前(后)的记录均不能大(小)于它
L->[0] = L->[low].key;//以low位置上的元素为标准进行一次划分,用子表的第一个记录坐枢纽记录
pivotkey = L->r[low].key;//枢纽记录关键字
while(low<high) {//从表的两端交替的向中间进行扫描
while(low<high && L->r[high].key>=pivotkey)
high--;
L->r[low] = L->r[high];//将比枢纽记录小的记录移向低端
while(low<high && L->r[low].key<=pivotkey)
low++;
L->r[high] = L->r[low];//将比枢纽大的记录移到高端
}
L->r[low] = L->r[0];//枢纽记录到位
return low;//返回枢纽记录
}
选择排序
六、简单选择排序
不稳定
T(n) = O(n2)
S(n) = O(1)
** n-1趟**
- demo
#include <stdio.h>
#include <stdlib.h>
typedef int KeyType;
typedef char InfoType;
typedef struct
{
KeyType key;
InfoType otherinfo;
} ElemType;
typedef struct LNode
{
ElemType data;
struct LNode * next;
}LNode,* LinkList;
int InitList(LinkList *L){
(*L) = (LNode *)malloc(sizeof(LNode));
(*L)->next = NULL;
return 1;
}
//因为头指针始终指向头结点所以不用传*L也可
void SelectSort(LinkList L){
LinkList k,j;
KeyType n;
InfoType a;
for (L = L->next ; L ; L = L->next) { //是不是多比了一次
k = L;
for (j = k->next; j ; j= j->next)
{
if(j->data.key < k->data.key){
n = k->data.key;
a = k->data.otherinfo;
k->data.key= j->data.key;
j->data.key = n;
k->data.otherinfo = j->data.otherinfo;
j->data.otherinfo = a;
}
}
}
return;
}
void printfList(LinkList L){
for (L = L->next ; L ; L = L->next)
printf("(%d,%c)",L->data.key,L->data.otherinfo);
printf("\n");
return;
}
int main(void){
LinkList L,p;
int i;
KeyType arr[7] = {31,14,65,34,21,45,67};
InfoType c[7] = {'a','b','c','d','e','f','g'};
InitList(&L);
for (i = 0; i < 7; i++)
{
p = (LNode *)malloc(sizeof(LNode));
p->data.key = arr[i];
p->data.otherinfo = c[i];
p->next = L->next;
L->next = p;//注意此处赋值的时候,是空出了L的第一个data域
}
printfList(L);
SelectSort(L);
printfList(L);
free(L);
return 0;
}
七、堆排序
不稳定
T(n) = O(nlogn)
S(n) = O(1)
速度快 省空间
- n个元素的序列{k1,k2,k3,…,kn}当且仅当满足下列关系的时候,称之为堆
- 堆排序:将无序序列建成一个堆,得到关键字最小(大)的记录;输出堆顶的最小(大)值后,将剩余的n-1个元素重又建成一个堆,则可得到n个元素的次小值,如此重复执行,直到堆中只有一个记录为止,每个记录出堆的顺序就是一个有序序列
- 筛选:对深度为k的堆“筛选”所需进行的关键字比较的次数至多为2(k-1)
- 大根堆的堆顶元素是最大值
- 小根堆的堆顶元素是最小值
- 建堆
先化为 完全二叉树
- 效率分析
八、归并排序
2-路归并排序
稳定
T(n) = O(nlogn)
S(n) = O(n)
- 2-路归并排序:将两个位置相邻的记录有序子序列归并成一个记录有序的序列
九、多关键字排序
十、链式基数排序
一、步骤
- 以静态链表存储待排记录,并令表头指针指向第一个记录;
- “分配” 时,按当前“关键字位”所取值,将记录分配到不
同的 “链队列” 中,每个队列中记录的 “关键字位” 相同; - “收集”时,按当前关键字位取值从小到大将各队列首尾相
链成一个链表;(对于一列的要从下往上收集) - 对每个关键字位均重复 2 和 3 两步
2.
3.
二、效率分析