目录
一、插入排序:
每步将一个待排序的记录, 按其排序码大小, 插入到前面已经排好序的文件中的适当位置, 直到全部记录都被插入完为止
排好序的序列+待排序的序列
/*插入排序*/
typedef int KeyType;
typedef int DataType;
typedef struct
{
KeyType key; //排序码字段
DataType info; //记录的其他字段
};RecordNode;
typedef struct
{
int n; //文件中的记录个数
RecordNode * record;//记录的指针字段
}SortObject;
直接插入排序
/*插入排序:直接插入排序*/
void insertSort ( SortObject *pvector )
{
int i, j;
RecordNode temp;
RecordNode *data = pvector->record;
for( i = 1; i < pvector->n; ++i)
{
temp = data[i];
for ( j = i-1; temp.key < data[j].key && j >= 0; j--)
data[j+1] = data[j];
if( j != i-1 ) data[j+1] = temp;
}
}
二分法插入排序
/*二分法插入排序*/
void binSort ( SortObject * pvector )
{
int i, j, left, mid, right;
RecordNode temp;
RecordNode *data = pvector->record;
for( i = 1; i < pvector->n; i++ )
{ temp = data[i];
left = 0;
right = i –1; //区间:left-right
while (left <= right)
{
mid=(left+right)/2;
if (temp.key<data[mid].key) //小于中间
right=mid-1; //右侧
else
left=mid+1;
}
for( j=i-1; j>=left; j-- ) data[j+1] = data[j];
if ( left != i ) data[left] = temp;
}
}
表插入排序
在直接插入排序的基础上减少记录移动的次数
单链表代替顺序表(插入记录Ri时,R0至Ri-1已排好序, 先将Ri脱链, 采用顺序比较的方法找到Ri应插入的位置,将Ri插入链表)
/*表插入排序*/ struct Node; typedef struct Node ListNode;//链式 struct Node { KeyType key; //记录的排序码字段 DataType info; //记录的其他字段 ListNode *next; //记录的指针字段 }; typedef ListNode *LinkList; void listSort ( LinkList * plist ) { ListNode *now, *pre, *p, *q, *head; head=*plist; pre=head->next; if (pre==NULL) return; now=pre->next; if(now==NULL) return; while ( now!=NULL ) { q=head; p=head->next; while ( p!=now && p->key<=now->key ) { q=p; p=p->next; } if ( p==now ) { pre=pre->next; now=pre->next; continue ; } pre->next=now->next; q->next=now; now->next=p; now=pre->next; } }
Shell排序(每组自己排序)
把一个序列看成由间距
d
的元素构成的
d
个“
子序列
”。反复做:
¤
在每个子序列内部分别排序
¤
缩小
d
之后重复第
1
步
,
直到
d
缩小为
1
时再做一次排序
(
即对整个序列排序
)
/*shell排序*/
inc = d;
while ( inc > 0 )
{
对相距inc 的各组排序;
inc = decrease (inc) ;
};
//开始
void shellSort ( SortObject * pvector, int d )
{
int i, j, inc;
RecordNode temp, *data = pvector->record;
for (inc = d; inc > 0; inc /= 2) {
for (i = inc; i < pvector->n; i++) {
temp = data[i];
for (j = i -inc; j >= 0 && temp.key < data[j].key; j -= inc)
data[j+inc] = data[j];
data[j+inc] = temp;
}
}
}
二、选择排序
基本思想:
维护最小的i 个记录的已排序序列
每次从剩余未排序的记录中选取关键码最小的记录,排在已排序序列之后,作为序列的第i +1 个记录
直接选择排序
从空排序序列开始,每次从未排序记录中选排序码最小的记录,
与未排序段的第一个记录交换,直到所有记录排好序
PS:比较次数与文件初始状态无关
void selectSort ( SortObject * pvector ) {
int i, j, k;
RecordNode temp, *data = pvector->record;
for( i = 0; i < pvector->n-1; i++ ) {
k = i;
for (j = i+1; j < pvector->n; j++)
if (data[j].key < data[k].key)
k = j;
if (k != i)
{ temp = data[i]; data[i] = data[k]; data[k] = temp; }
}
}
堆排序
¨
直接选择排序效率较低,第i
趟排序需要作
n-
i
次比较,没有利用已做的比较
结果
把待排序的记录构造成堆,然后通过从堆中不断选出最大/小元素
n个元素的序列K={ k0, k1, …, kn-1 }
堆与完全二叉树的顺序表示:
- 大根堆:ki≥k2i+1且ki≥k2i+2
每个二叉树的结点均大于等于其左、右孩子结点。即根结点最大
- 小根堆:ki≤k2i+1且ki≤k2i+2 ( i=0,1,…,n/2-1) (优先队列中使用)
每个二叉树的结点均小于等于其左、右孩子结点。即根结点最小
void heapsort ( SortObject *pvector ) {
int i, n;
RecordNode temp;
n=pvector->n;
for ( i=n/2-1; i>=0; i--)
sift(pvector,n,i);
for(i=n-1; i>0; i--) {
temp=pvector->record[0];
pvector->record[0]=pvector->record[i];
pvector->record[i]=temp;
sift(pvector,i,0);
}
}
//将以pvector->record[p]为根的子树调整为大根堆
void sift ( SortObject * pvector, int size , int p ) {
RecordNode temp=pvector->record[p];
int child= 2* p +1;
while (child<size) {
if( ( child<size-1 )&& ( pvector->record[child].key<pvector->record[child+1].key ) )
child++;
if( temp.key < pvector->record[child].key ) {
pvector->record[p]=pvector->record[child];
p=child; child= 2*p +1;
}
else break;
}
pvector->record[p]=temp;
}
三、交换排序
依次比较相邻数据项,若某两项次序颠倒(逆序),则交换它们。重复这一过程直到不需要交换为止
1.起泡排序
将相邻元素进行两两比较和交换,进而得到一个有序序列
总的比较和移动次数比较多,相邻两个记录比较和交换,每次交换只能上移或者下移一个位置
2.快速排序(分区交换排序)
设法把待排序序列按某种标准分为大小两组。例如选择第一条记录R0作为区分标准,将所有大于R0的记录移到R0的右边, 所有小于R0的记录移到R0的左边
递归地分别对两组记录采用同样方式排序,直至划分到每个子部分只包含一个记录,整个序列的排序完成
【例题一】
【例题二】
void quicksort ( SortObject *pvector, int l, int r )
{
int i, j;
RecordNode temp, *data = pvector->record;
if ( l >= r ) return; //左边小不要动
i= l; j= r; temp = data[i];
while( i != j ) //i=j循环终止
{
while( i< j && data[j].key >= temp.key) j--; //右边大
if ( i< j ) data[i++] = data[j];
while( i< j && data[i].key <= temp.key) i++; //左边
if (i< j) data[j--] = data[i];
}
data[i] = temp;
quicksort ( pvector, l, i-1 );
quicksort ( pvector,i+1, r );
}
四、 分配排序
struct Node;
typedef struct Node *PNode;
struct Node {
KeyType key[D]; //D位排序码
DataType info;
PNode next;
};
typedef struct {
PNode f; //队列的头指针
PNode e; //队列的尾指针
} Queue;
typedef struct Node *RadixList; //待排序文件类型
void radixSort( RadixList *plist, int d, int r) { //plist指向带表头结点的单链表的头指针
int i, j, k; PNode p, head = (*plist)->next;
Queue queue[r]; //队列数组
for ( j = d-1; j >= 0; j-- ) { //进行d趟分配和收集
for ( i = 0; i < r; i++) queue[i].f = queue[i].e = NULL; //队列初始化
for ( p = head; p != NULL; p = p->next) {
k = p->key[j]; //按排序码的第j个分量分配
if ( queue[k].f == NULL ) queue[k].f = p;
else (queue[k].e)->next = p;
queue[k].e = p;
}
for (i = 0; queue[i].f == NULL; i++) ; //找到第一个非空队列
p = queue[i].e; head = queue[i].f; //head为收集链表的头指针
for( i++; i < r; i++) //收集其他非空队列
if (queue[i].f != NULL) { p->next = queue[i].f; p = queue[i].e; }
p->next = NULL;
}
(*plist)->next = head;
}
五、归并排序
¨
归并排序策略
¤
把待排序的文件分成
若干个子文件
,先将
每个子文件
内的记录排序,再将已排序的子文件合并,逐步得到完全排序的文件
¨
二
路归并排序
¤
把待排序序列的
n
个记录看成
n
个有序子序列
,
每个子序列的长度为
1
¤
把当前的有序子序列两两归并
,
得到两倍长度
、
数目减半的一组有序子序列
¤
重复做子序列的两两归并
,
最终得到长度为
n
的有序序列
/*两组有序子序列归并算法*/
//设r[low ]到r [m]和r[m+1 ]到r[high]是两个有序子序列,将其归并到r1[low ]到r1[high] 中
void merge ( RecordNode *r, RecordNode *r1, int low, int m, int high ) {
int i = low, j = m + 1, k = low;
while ( i <= m && j <= high ) {
if (r[i].key <= r[j].key)
r1[k++] = r[i++];
else
r1[k++] = r[j++];
}
while (i <= m) r1[k++] = r[i++];
while (j <= high) r1[k++] = r[j++];
}
/*一趟二路归并算法*/
//对r 做一趟归并, 结果放入r1; 当前有序段长length
void mergePass ( RecordNode *r, RecordNode *r1, int n, int length) {
int i = 0, j;
//归并长length的两个子段
while ( i + 2*length -1 < n ) {
merge(r, r1, i, i+length-1, i + 2*length -1);
i += 2*length;
}
if (i + length -1 < n -1) //剩下两段,后段长度小于length
merge(r, r1, i, i+length-1, n-1);
else //剩下一段,将它复制到数组r1
for(j = i; j < n; j++)
r1[j] = r[j];
}