C语言经典算法学习-4

文章目录

21.最大访客数

说明:现将举行一个餐会,让访客事先填写到达时间与离开时间,为了掌握座位的数目,必须先估计不同时间的最大访客数。

解法:这个题目看似有些复杂,其实相当简单,单就计算访客数这个目的,同时考虑同一访客的来访时间与离开时间,反而会使程式变得复杂;只要将来访时间与离开时间分开处理就可以了,假设访客 i 的来访时间为x[i],而离开时间为y[i]。

在资料输入完毕之后,将x[i]与y[i]分别进行排序(由小到大),道理很简单,只要先计算某时之前总共来访了多少访客,然后再减去某时之前的离开访客,就可以轻易的解出这个问题。

c 复制代码
#include <stdio.h> 
#include <stdlib.h> 
#define MAX 100 
#define SWAP(x,y) {int t; t = x; x = y; y = t;} 

int partition(int[], int, int); 
void quicksort(int[], int, int); // 快速排序法
int maxguest(int[], int[], int, int); 

int main(void) { 
    int x[MAX] = {0}; 
    int y[MAX] = {0}; 
    int time = 0; 
    int count = 0; 

    printf("\n输入来访与离开125;时间(0~24):"); 
    printf("\n范例:10 15"); 
    printf("\n输入-1 -1结束"); 
    while(count < MAX) { 
        printf("\n>>"); 
        scanf("%d %d", &x[count], &y[count]); 
        if(x[count] < 0) 
            break; 
        count++; 
    } 

    if(count >= MAX) { 
        printf("\n超出最大访客数(%d)", MAX); 
        count--; 
    } 

    // 预先排序 
    quicksort(x, 0, count); 
    quicksort(y, 0, count); 

    while(time < 25) { 
        printf("\n%d 时的最大访客数:%d", 
                   time, maxguest(x, y, count, time)); 
        time++; 
    } 

    printf("\n"); 

    return 0; 
} 

int maxguest(int x[], int y[], int count, int time) { 
    int i, num = 0; 

    for(i = 0; i <= count; i++) { 
        if(time > x[i]) 
            num++; 
        if(time > y[i]) 
            num--; 
    } 

    return num; 
} 

int partition(int number[], int left, int right) { 
    int i, j, s; 

    s = number[right]; 
    i = left - 1; 

    for(j = left; j < right; j++) { 
        if(number[j] <= s) { 
            i++; 
            SWAP(number[i], number[j]); 
        } 
    } 

    SWAP(number[i+1], number[right]); 
    return i+1; 
} 

void quicksort(int number[], int left, int right) { 
    int q; 

    if(left < right) { 
        q = partition(number, left, right); 
        quicksort(number, left, q-1); 
        quicksort(number, q+1, right); 
    } 
} 

22.中序式转后序式(前序式)

说明平常所使用的运算式,主要是将运算元放在运算子的两旁,例如a+b/d这样的式子,这称之为中序(Infix)表示式,对于人类来说,这样的式子很容易理 解,但由于电脑执行指令时是有顺序的,遇到中序表示式时,无法直接进行运算,而必须进一步判断运算的先后顺序,所以必须将中序表示式转换为另一种表示方 法。

可以将中序表示式转换为后序(Postfix)表示式,后序表示式又称之为逆向波兰表示式(Reverse polish notation),它是由波兰的数学家卢卡谢维奇提出,例如(a+b)(c+d)这个式子,表示为后序表示式时是ab+cd+

解法用手算的方式来计算后序式相当的简单,将运算子两旁的运算元依先后顺序全括号起来,然后将所有的右括号取代为左边最接近的运算子(从最内层括号开始),最后去掉所有的左括号就可以完成后序表示式,例如:

a+bd+c/d => ((a+(bd))+(c/d)) -> bd*+cd/+

如果要用程式来进行中序转后序,则必须使用堆叠,演算法很简单,直接叙述的话就是使用回圈,取出中序式的字元,遇运算元直接输出,堆叠运算子与左括号, ISP>ICP的话直接输出堆叠中的运算子,遇右括号输出堆叠中的运算子至左括号。

如果要将中序式转为前序式,则在读取中序式时是由后往前读取,而左右括号的处理方式相反,其余不变,但输出之前必须先置入堆叠,待转换完成后再将堆叠中的 值由上往下读出,如此就是前序表示式。

c 复制代码
#include <stdio.h> 
#include <stdlib.h> 

int postfix(char*); // 中序转后序 
int priority(char); // 决定运算子优先顺序 

int main(void) { 
    char input[80]; 

    printf("输入中序运算式:"); 
    scanf("%s", input); 
    postfix(input); 

    return 0; 
} 

int postfix(char* infix) { 
    int i = 0, top = 0; 
    char stack[80] = {'\0'}; 
    char op; 

    while(1) { 
        op = infix[i]; 

        switch(op) { 
            case '\0': 
                while(top > 0) { 
                    printf("%c", stack[top]); 
                    top--; 
                } 
                printf("\n"); 
                return 0; 
            // 运算子堆叠 
            case '(': 
                if(top < (sizeof(stack) / sizeof(char))) { 
                    top++; 
                    stack[top] = op; 
                } 
                break; 
            case '+': case '-': case '*': case '/': 
                while(priority(stack[top]) >= priority(op)) { 
                    printf("%c", stack[top]); 
                    top--; 
                } 
                // 存入堆叠 
                if(top < (sizeof(stack) / sizeof(char))) { 
                    top++; 
                    stack[top] = op; 
                } 
                break; 
            // 遇 ) 输出至 ( 
            case ')': 
                while(stack[top] != '(') { 
                    printf("%c", stack[top]); 
                    top--; 
                } 
                top--;  // 不输出( 
                break; 
            // 运算元直接输出 
            default: 
                printf("%c", op); 
                break; 
        } 
        i++; 
    } 
} 

int priority(char op) { 
    int p; 

    switch(op) { 
       case '+': case '-': 
            p = 1; 
            break; 
        case '*': case '/': 
            p = 2; 
            break; 
        default: 
            p = 0; 
            break; 
    } 

    return p; 
} 

23.后序式的运算

说明 将中序式转换为后序式的好处是,不用处理运算子先后顺序问题,只要依序由运算式由前往后读取即可。

解法

c 复制代码
#include <stdio.h> 
#include <stdlib.h> 

void evalPf(char*); 
double cal(double, char, double); 

int main(void) { 
    char input[80]; 
    printf("输入后序式:"); 
    scanf("%s", input); 
    evalPf(input); 
    return 0; 
} 

void evalPf(char* postfix) { 
    double stack[80] = {0.0}; 
    char temp[2]; 
    char token; 
    int top = 0, i = 0; 

    temp[1] = '\0'; 

    while(1) { 
        token = postfix[i]; 
        switch(token) { 
            case '\0': 
                printf("ans = %f\n", stack[top]); 
                return; 
            case '+': case '-': case '*': case '/': 
                stack[top-1] = 
                       cal(stack[top], token, stack[top-1]); 
                top--; 
                break; 
            default: 
                if(top < sizeof(stack) / sizeof(float)) { 
                    temp[0] = postfix[i]; 
                    top++; 
                    stack[top] = atof(temp); 
                } 
                break; 
        } 
        i++; 
    } 
} 

double cal(double p1, char op, double p2) { 
    switch(op) { 
        case '+': 
            return p1 + p2; 
        case '-': 
            return p1 - p2; 
        case '*': 
            return p1 * p2; 
        case '/': 
            return p1 / p2; 
    } 
} 

24.洗扑克牌(乱数排列)

说明

洗扑克牌的原理其实与乱数排列是相同的,都是将一组数字(例如1~N)打乱重新排列,只不过洗扑克牌多了一个花色判断的动作而已。

解法

初学者通常会直接想到,随机产生1~N的乱数并将之存入阵列中,后来产生的乱数存入阵列前必须先检查阵列中是否已有重复的数字,如果有这个数就不存入,再重新产生下一个数,运气不好的话,重复的次数就会很多,程式的执行速度就很慢了,这不是一个好方法。

以1~52的乱数排列为例好了,可以将阵列先依序由1到52填入,然后使用一个回圈走访阵列,并随机产生1~52的乱数,将产生的乱数当作索引取出阵列值,并与目前阵列走访到的值相交换,如此就不用担心乱数重复的问题了,阵列走访完毕后,所有的数字也就重新排列了。

至于如何判断花色?这只是除法的问题而已,取商数判断花色,取余数判断数字,您可以直接看程式比较清楚。

实作

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define N 52

int main(void) {
    int poker[N + 1];
    int i, j, tmp, remain;

    // 初始化阵列 
    for(i = 1; i <= N; i++)
        poker[i] = i; 

    srand(time(0));

    // 洗牌 
    for(i = 1; i <= N; i++) {
        j = rand() % 52 + 1;
        tmp = poker[i];
        poker[i] = poker[j]; 
        poker[j] = tmp; 
    }

    for(i = 1; i <= N; i++) {
        // 判断花色 
        switch((poker[i]-1) / 13) { 
            case 0: 
                printf("桃"); break;
            case 1: 
                printf("心"); break;
            case 2: 
                printf("砖"); break;
            case 3: 
                printf("梅"); break;
        } 

        // 扑克牌数字 
        remain = poker[i] % 13;
        switch(remain) { 
            case 0: 
                printf("K "); break;
            case 12: 
                printf("Q "); break;
            case 11: 
                printf("J "); break;
            default: 
                printf("%d ", remain); break;
        } 

        if(i % 13 == 0)
            printf("\n");
    } 

    return 0;
} 

25.Craps赌博游戏

说明一个简单的赌博游戏,游戏规则如下:玩家掷两个骰子,点数为1到6,如果第一次点数和为7或11,则玩家胜,如果点数和为2、3或12,则玩家输,如果和 为其它点数,则记录第一次的点数和,然后继续掷骰,直至点数和等于第一次掷出的点数和,则玩家胜,如果在这之前掷出了点数和为7,则玩家输。

解法 规则看来有些复杂,但是其实只要使用switch配合if条件判断来撰写即可,小心不要弄错胜负顺序即可。

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define WON 0
#define LOST 1
#define CONTINUE 2

int rollDice() { 
    return (rand() % 6) + (rand() % 6) + 2;
}

int main(void) {
    int firstRoll = 1;
    int gameStatus = CONTINUE;
    int die1, die2, sumOfDice;
    int firstPoint = 0;
    char c;

    srand(time(0));

    printf("Craps赌博游戏,按Enter键开始游戏****");

    while(1) {
         getchar();

        if(firstRoll) {
            sumOfDice = rollDice();
            printf("\n玩家掷出点数和:%d\n", sumOfDice);

            switch(sumOfDice) {
                case 7: case 11:
                    gameStatus = WON; break;
                case 2: case 3: case 12:
                    gameStatus = LOST; break;
                default:
                    firstRoll = 0;
                    gameStatus = CONTINUE;
                    firstPoint = sumOfDice;
                    break;
            }
        }
        else {
            sumOfDice = rollDice();
            printf("\n玩家掷出点数和:%d\n", sumOfDice);

            if(sumOfDice == firstPoint)
                gameStatus = WON;
            else if(sumOfDice == 7)
                gameStatus = LOST;
        }

        if(gameStatus == CONTINUE)
            puts("未分胜负,再掷一次****\n");
        else {
            if(gameStatus == WON)
                puts("玩家胜");
            else
                puts("玩家输");

            printf("再玩一次?");
            scanf("%c", &c);
            if(c == 'n') {
                puts("游戏结束");
                break;
            }
            firstRoll = 1;
        }
    }
    return 0;
} 
相关推荐
hopetomorrow27 分钟前
学习路之压力测试--jmeter安装教程
学习·jmeter·压力测试
hopetomorrow28 分钟前
学习路之PHP--使用GROUP BY 发生错误 SELECT list is not in GROUP BY clause .......... 解决
开发语言·学习·php
/**书香门第*/1 小时前
Cocos creator 3.8 支持的动画 7
学习·游戏·游戏引擎·游戏程序·cocos2d
带多刺的玫瑰1 小时前
Leecode刷题C语言之统计不是特殊数字的数字数量
java·c语言·算法
爱敲代码的憨仔1 小时前
《线性代数的本质》
线性代数·算法·决策树
美式小田1 小时前
单片机学习笔记 9. 8×8LED点阵屏
笔记·单片机·嵌入式硬件·学习
yigan_Eins1 小时前
【数论】莫比乌斯函数及其反演
c++·经验分享·算法
猫爪笔记2 小时前
前端:HTML (学习笔记)【2】
前端·笔记·学习·html
阿史大杯茶2 小时前
AtCoder Beginner Contest 381(ABCDEF 题)视频讲解
数据结构·c++·算法
陌小呆^O^2 小时前
Cmakelist.txt之win-c-udp-server
c语言·开发语言·udp