睡前攻势——直接插入排序【以及瞎编扑克牌摸牌】

 注:本文相关代码描述参考耿国华教授主编的《数据结构——用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;

算法执行的时间耗费主要取决于数据分布情况,如果待排序记录是随机的,即待排序记录可能出现的各种排列的概率相同,则可以取上述最小值和最大值的平均值,约为n^2/4。因此,直接插入排序的时间复杂度为T(n) = O(n^2),空间复杂度为S(n)=O(1)

扑克牌摸牌程序:

#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;
}

结果演示:

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值