注:本文相关代码描述参考耿国华教授主编的《数据结构——用C语言描述》(第2版)
直接插入排序顾名思义是一种十分直接的排序方法,就好像打扑克牌的摸牌阶段,大家逐次摸牌,每摸一张牌将它置于手牌中的合适的位置,便于玩家思考出牌。为了更加贴切表述,本文章将模拟扑克牌摸牌情况。
算法描述:将第i个记录插入到前面已经排好顺序的记录中(”记录“释义:数据元素的通俗叫法)。即第i个记录的关键字K同前面已经排好序的i-1个关键字依次比较,将所有关键字大于K的记录向后移动一格位置,直到遇见关键字小于或等于K的记录,这时,小于或等于K的记录的后一位是空的,直接将关键字K的记录插入该空位置。完整的直接插入排序是从i = 2开始的。为什么从i = 2开始?i = 0的记录是用于监视哨,i = 1的记录视为我们摸到的第一张牌,没有先前的有序记录进行比较,即将i = 1的记录视作已经排好的序列。
算法代码:
void insSort(Elements e[],int len){
int i,j;
for(i= 2;i <= len;i++){ //遍历手牌
e[0] = e[i];
j = i - 1;
while(e[0].key < e[j].key){ //比较
e[j+1] = e[j];
j = j - 1;
}
e[j + 1] = e[0];
}
}
算法的要点:①使用监视哨e[0]临时保存待插入的记录;②从后往前查找应插入的位置;③查找和移动同时进行
算法分析:1、从空间角度来看,它只需要一个辅助空间e[0]。
2、从时间角度来看,主要时间耗费在关键字的比较和元素移动上。
①最好情况:e[i].key>=e[i-1].key,原因是只需要执行一次while循环并且直接插入有序序列,不进行元素移动操作。此时,总的比较次数为n-1次,总的移动次数为2(n-1)【即每次循环中e[0] = e[i]和e[j+1] = e[0]两次】;
②最坏情况:e[i].key<e[1].key,while循环中关键字的比较次数和移动记录次数为i-1。此时,总的比较次数为(n+2)(n-1)/2,总的记录移动次数为:(n+4)(n-1)/2;
算法执行的时间耗费主要取决于数据分布情况,如果待排序记录是随机的,即待排序记录可能出现的各种排列的概率相同,则可以取上述最小值和最大值的平均值,约为/4。因此,直接插入排序的时间复杂度为,空间复杂度为。
扑克牌摸牌程序:
#include<stdio.h>
typedef char* anyType;//一张扑克牌中能够想到的其它属性,比如花色
typedef char* Character;//扑克牌的表面名称属性
typedef int Key;//用于比较扑克牌大小的关键属性
const int MAXSIZE = 100;
//定义扑克牌
typedef struct{
Key key;
Character role;
anyType anything;
}Elements;
//直接插入算法
void insSort(Elements e[],int len){
int i,j;
for(i= 2;i <= len;i++){ //遍历手牌
e[0] = e[i];
j = i - 1;
while(e[0].key < e[j].key){ //比较
e[j+1] = e[j];
j = j - 1;
}
e[j + 1] = e[0];
}
}
//使用扑克牌关键属性匹配扑克牌表面属性
void cardsMatching(Elements e[],int len){
for(int i = 1;i <= len;i++){
switch(e[i].key){
case 3: e[i].role = "3";break;
case 4: e[i].role = "4";break;
case 5: e[i].role = "5";break;
case 6: e[i].role = "6";break;
case 7: e[i].role = "7";break;
case 8: e[i].role = "8";break;
case 9: e[i].role = "9";break;
case 10: e[i].role = "10";break;
case 11: e[i].role = "J";break;
case 12: e[i].role = "Q";break;
case 13: e[i].role = "K";break;
case 14: e[i].role = "A";break;
case 15: e[i].role = "2";break;
case 16: e[i].role = "pokerShortJoker";break;
case 17: e[i].role = "pokerBigToker";break;
default: printf("wrong!\n");break;
}
}
}
//展示手牌
void outPut(Elements e[],int len){
printf("手牌是:\n");
for(int k = len;k >= 1;k--){
printf("\t%s\n",e[k].role);
}
}
int main(){
Elements a[MAXSIZE];
int num,length;
int i = 1;
printf("请摸牌:\n");
while(scanf("%d",&num)){
if(num < 3 || num > 17){ //本程序的局限性,应当有一个有效数的判断
printf("错误的摸牌,请重摸牌\n");
continue;
}
a[i++].key = num;
}
length = i - 1;
insSort(a,length);
cardsMatching(a,length);
outPut(a,length);
return 0;
}
结果演示: