专题讨论:1-算法时空分析与线性表

算法思想 

可以使用队列来实现扑克牌的排序。

基本思路:

1、将扑克牌按照花色和数字的组合读入队列中。

2、通过比较扑克牌的花色和数字,将队列中的元素按照花色和数字的升序规则进行重新排列。

自然语言描述 

1. 数据结构定义

Card 结构体:表示一张扑克牌,包含两个成员: suit:表示花色(如 A, B, C, D)。 number:表示数字(如 1, 2, 3, 4, 5)。

Queue 结构体:表示一个队列,用于存储排序后的牌。包含以下成员: cards:一个数组,用于存储牌。 front 和 rear:分别表示队列的前后指针,用于管理队列的入队和出队操作。

2. 队列操作函数

initializeQueue:初始化队列,将 front 和 rear 设置为 -1,表示队列为空。

isQueueEmpty:检查队列是否为空。如果 front 为 -1,则队列为空。

isQueueFull:检查队列是否已满。如果 rear 的下一个位置是 front,则队列已满。

enqueue:将一张牌加入队列。如果队列已满,则提示错误;否则将牌放入队列的 rear 位置,并更新 rear。

dequeue:从队列中取出一张牌。如果队列为空,则提示错误;否则取出 front 位置的牌,并更新 front。

3. 排序逻辑

getSuitValue:将花色转换为数值,便于比较。例如: A 对应 1,B 对应 2,C 对应 3,D 对应 4;

compareCards:比较两张牌的优先级。先比较花色,如果花色相同,则比较数字。

sortCards:使用标准库函数 qsort 对牌进行排序。排序规则由 compareCards 函数定义。

4. 主函数逻辑

初始化牌组:定义一组牌,例如 A3, D3, A1, B5, C3, B2, C1, D4。

排序牌组:调用 sortCards 函数,按照花色和数字的优先级对牌进行排序。

初始化队列:创建一个空队列。

将排序后的牌入队:将排序后的牌依次加入队列。

打印排序结果:从队列中依次取出牌,并打印每张牌的花色和数字。

5. 代码执行流程

定义一组牌。

1)对牌进行排序,排序规则为: 先按花色排序:A < B < C < D。

2)同花色按数字排序:1 < 2 < 3 < 4 < 5。

3)将排序后的牌加入队列。

4)从队列中依次取出牌并打印,最终输出排序结果。

伪代码

定义结构体 Card
    字符类型 suit  
    整数类型 number  
结束定义

定义结构体 Queue
    数组 cards[MAX_CARDS]  
    整数类型 front  
    整数类型 rear  
结束定义

函数 initializeQueue(队列 q)
    q.front = -1
    q.rear = -1
结束函数

函数 isQueueEmpty(队列 q)
    如果 q.front 等于 -1
        返回 true
    否则
        返回 false
    结束如果
结束函数

函数 isQueueFull(队列 q)
    如果 (q.rear + 1) 对 MAX_CARDS 取余 等于 q.front
        返回 true
    否则
        返回 false
    结束如果
结束函数

函数 enqueue(队列 q, 扑克牌 card)
    如果 isQueueFull(q) 为 true
        输出 "Queue is full"
        返回
    结束如果
    如果 isQueueEmpty(q) 为 true
        q.front = 0
        q.rear = 0
    否则
        q.rear = (q.rear + 1) 对 MAX_CARDS 取余
    结束如果
    q.cards[q.rear] = card
结束函数

函数 dequeue(队列 q)
    如果 isQueueEmpty(q) 为 true
        输出 "Queue is empty"
        返回 空扑克牌 { '\0', 0 }
    结束如果
    扑克牌 card = q.cards[q.front]
    如果 q.front 等于 q.rear
        q.front = -1
        q.rear = -1
    否则
        q.front = (q.front + 1) 对 MAX_CARDS 取余
    结束如果
    返回 card
结束函数

函数 getSuitValue(字符 suit)
    开关 (suit)
        情况 'A': 返回 1
        情况 'B': 返回 2
        情况 'C': 返回 3
        情况 'D': 返回 4
        默认情况: 返回 0
    结束开关
结束函数

函数 compareCards(指向扑克牌 a 的指针, 指向扑克牌 b 的指针)
    扑克牌指针 cardA = 指向扑克牌 a 的指针 转换为 Card 指针类型
    扑克牌指针 cardB = 指向扑克牌 b 的指针 转换为 Card 指针类型
    整数 suitA = getSuitValue(cardA.suit)
    整数 suitB = getSuitValue(cardB.suit)
    如果 suitA 不等于 suitB
        返回 suitA - suitB
    否则
        返回 cardA.number - cardB.number
    结束如果
结束函数

函数 sortCards(扑克牌数组 cards, 整数 n)
    使用 qsort 函数对 cards 数组进行排序,排序依据为 compareCards 函数
结束函数

主函数 main()
    扑克牌数组 cards = { {'A', 3}, {'D', 3}, {'A', 1}, {'B', 5}, {'C', 3}, {'B', 2}, {'C', 1}, {'D', 4} }
    整数 n = cards 数组元素个数
    sortCards(cards, n)
    队列 q
    initializeQueue(q)
    对于 i 从 0 到 n - 1
        enqueue(q, cards[i])
    结束循环
    输出 "Sorted cards: "
    当!isQueueEmpty(q) 时
        扑克牌 card = dequeue(q)
        输出 card.suit 和 card.number
    结束循环
    输出换行符
    返回 0
结束主函数

代码实现 

1、qsort快速排序:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_CARDS 100 // 定义最大牌数

// 牌的结构体
typedef struct {
    char suit; // 花色
    int number; // 数字
} Card;

// 队列的结构体
typedef struct {
    Card cards[MAX_CARDS]; // 存储牌的数组
    int front, rear; // 队列的前后指针
} Queue;

// 初始化队列
void initializeQueue(Queue* q) {
    q->front = q->rear = -1; // 初始时队列为空
}

// 判断队列是否为空
int isQueueEmpty(Queue* q) {
    return q->front == -1; // 如果 front 为 -1,队列为空
}

// 判断队列是否已满
int isQueueFull(Queue* q) {
    return (q->rear + 1) % MAX_CARDS == q->front; // 如果 rear 的下一个位置是 front,队列已满
}

// 入队
void enqueue(Queue* q, Card card) {
    if (isQueueFull(q)) {
        printf("Queue is full\n"); // 如果队列已满,打印提示信息
        return;
    }
    if (isQueueEmpty(q)) {
        q->front = q->rear = 0; // 如果队列为空,初始化 front 和 rear
    }
    else {
        q->rear = (q->rear + 1) % MAX_CARDS; // 否则,rear 向后移动
    }
    q->cards[q->rear] = card; // 将牌放入队列
}

// 出队
Card dequeue(Queue* q) {
    if (isQueueEmpty(q)) {
        printf("Queue is empty\n"); // 如果队列为空,打印提示信息
        Card emptyCard = { '\0', 0 }; // 返回一张空牌
        return emptyCard;
    }
    Card card = q->cards[q->front]; // 取出队首的牌
    if (q->front == q->rear) {
        q->front = q->rear = -1; // 如果队列中只有一张牌,出队后队列为空
    }
    else {
        q->front = (q->front + 1) % MAX_CARDS; // 否则,front 向后移动
    }
    return card; // 返回取出的牌
}

// 获取花色的数值,用于比较
int getSuitValue(char suit) {
    switch (suit) {
    case 'A': return 1; // A 对应 1
    case 'B': return 2; // B 对应 2
    case 'C': return 3; // C 对应 3
    case 'D': return 4; // D 对应 4
    default: return 0; // 其他情况返回 0
    }
}

// 比较两张牌的优先级
int compareCards(const void* a, const void* b) {
    Card* cardA = (Card*)a; // 将 void 指针转换为 Card 指针
    Card* cardB = (Card*)b; // 将 void 指针转换为 Card 指针
    int suitA = getSuitValue(cardA->suit); // 获取 cardA 的花色值
    int suitB = getSuitValue(cardB->suit); // 获取 cardB 的花色值
    if (suitA != suitB) {
        return suitA - suitB; // 如果花色不同,按花色排序
    }
    return cardA->number - cardB->number; // 如果花色相同,按数字排序
}

// 对牌进行排序
void sortCards(Card cards[], int n) {
    qsort(cards, n, sizeof(Card), compareCards); // 使用 qsort 函数进行排序
}

int main() {
    // 初始化牌组
    Card cards[] = {
        {'A', 3}, {'D', 3}, {'A', 1}, {'B', 5},
        {'C', 3}, {'B', 2}, {'C', 1}, {'D', 4}
    };
    int n = sizeof(cards) / sizeof(cards[0]); // 计算牌的数量

    sortCards(cards, n); // 对牌进行排序

    Queue q;
    initializeQueue(&q); // 初始化队列

    // 将排序后的牌入队
    for (int i = 0; i < n; i++) {
        enqueue(&q, cards[i]);
    }

    // 打印
    printf("Sorted cards: ");
    while (!isQueueEmpty(&q)) {
        Card card = dequeue(&q); // 出队
        printf("%c%d ", card.suit, card.number); // 打印牌
    }
    printf("\n");

    return 0;
}

2、基数排序:

#include <iostream>
#include <queue>
#include <vector>

// 牌的结构体
struct Card {
    char suit; // 花色
    int number; // 数字
};

// 获取花色的数值,用于比较
int getSuitValue(char suit) {
    switch (suit) {
    case 'A': return 1; // A 对应 1
    case 'B': return 2; // B 对应 2
    case 'C': return 3; // C 对应 3
    case 'D': return 4; // D 对应 4
    default: return 0; // 其他情况返回 0
    }
}

// 基数排序
void radixSortCards(std::vector<Card>& cards) {
    // 先按数字进行排序
    std::vector<std::queue<Card>> queuesNumber(13);
    for (const auto& card : cards) {
        queuesNumber[card.number - 1].push(card);
    }
    cards.clear();
    for (auto& queue : queuesNumber) {
        while (!queue.empty()) {
            cards.push_back(queue.front());
            queue.pop();
        }
    }

    // 再按花色进行排序
    std::vector<std::queue<Card>> queuesSuit(4);
    for (const auto& card : cards) {
        int suitValue = getSuitValue(card.suit) - 1;
        queuesSuit[suitValue].push(card);
    }
    cards.clear();
    for (auto& queue : queuesSuit) {
        while (!queue.empty()) {
            cards.push_back(queue.front());
            queue.pop();
        }
    }
}

int main() {
    // 初始化牌组
    std::vector<Card> cards = {
        {'A', 3}, {'D', 3}, {'A', 1}, {'B', 5},
        {'C', 3}, {'B', 2}, {'C', 1}, {'D', 4}
    };

    radixSortCards(cards); // 对牌进行排序

    std::queue<Card> q; // 创建一个队列

    // 将排序后的牌入队
    for (const auto& card : cards) {
        q.push(card);
    }

    // 输出
    std::cout << "Sorted cards: ";
    while (!q.empty()) {
        Card card = q.front(); // 获取队首元素
        q.pop(); // 出队
        std::cout << card.suit << card.number << " "; 
    }
    std::cout << std::endl;

    return 0;
}    

其中对基数排序函数进行解释:

void radixSortCards(std::vector<Card>& cards) {
    // 函数定义,接收一个 std::vector<Card> 类型的引用作为参数,用于存储扑克牌集合
    // 函数没有返回值,因为它直接修改传入的向量

    // 先按数字进行排序
    std::vector<std::queue<Card>> queuesNumber(13);
    // 创建一个包含 13 个元素的 std::vector,每个元素是一个 std::queue<Card> 类型的队列
    // 13 代表扑克牌的数字范围(1 - 13),每个队列对应一个数字

    for (const auto& card : cards) {
        // 遍历传入的扑克牌向量
        // const auto& 表示使用常量引用,避免不必要的复制,提高效率
        queuesNumber[card.number - 1].push(card);
        // 根据当前扑克牌的数字,将其放入对应的队列中
        // 数组索引从 0 开始,使用 card.number - 1 作为队列的索引
    }

    cards.clear();
    // 清空原有的扑克牌向量,为重新收集排序后的扑克牌做准备

    for (auto& queue : queuesNumber) {
        // 遍历存储扑克牌的队列向量
        // auto& 表示使用引用,以便直接操作队列

        while (!queue.empty()) {
            // 当前队列不为空则继续循环

            cards.push_back(queue.front());
            // 将最早进入队列的扑克牌添加到原向量的末尾

            queue.pop();
            // 从队列中移除队首元素,以处理下一个元素
        }
    }
}

 

运行结果展示 

与冒泡排序对比 

冒泡排序:

1、时间复杂度为O(n²)

2、空间复杂度为O(1)

3、适用范围:小规模的数据处理、需要自行编写代码实现排序。

qsort排序:

1、时间复杂度为O(n log n)

2、空间复杂度为O(n)

3、适用范围:大规模的数据处理、在stdlib.h头文件基础上调用C语言函数库中的qsort函数进   行快速排序,无需自行编写代码,操作方便。

学习心得 

 1、qsort函数的学习

功能:qsort 函数是 C 标准库中用于对数组进行排序的函数,它具有通用性,可以对各种不同类型的数组进行排序。使用 qsort 函数,可以对整数数组、结构体数组、字符串数组等各种类型的数组进行排序,只需要提供合适的比较函数来定义排序规则即可,大大提高了代码的复用性和灵活性。

qsort函数的原型:

void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));

qsort 函数的各个参数含义如下:

1)base:指向要排序的数组的起始地址,在 sortCards 函数中,传入的是 cards 数组名,在 C 语言中数组名会被自动转换为指向数组首元素的指针。在本文中 cards 作为 qsort 函数的第一个参数,指向了要排序的扑克牌数组的起始位置。

2)nmemb:表示数组中元素的数量。在本文中传入的是 n,即 cards 数组中扑克牌的数量。

3)size:表示每个数组元素的大小(以字节为单位)。在本文中使用 sizeof(Card) 来获取 Card 结构体类型的大小,确保 qsort 函数能够正确地处理每个元素。

4)compar:compar 参数是 qsort 函数中用于接受一个函数指针的参数,该函数指针指向一个比较函数,该比较函数用于确定两个元素的顺序:比较函数要返回一个 bool 类型的值,以此表明两个元素的相对顺序。 若比较函数返回 true,表示第一个参数应排在第二个参数前面;若返回 false,则反之。 并且比较函数要满足严格弱序的要求,也就是具有传递性、非自反性和反对称性。在本文中, sortCards 函数里,传入的是 compareCards 函数名,compareCards 函数会根据扑克牌的花色和数字来比较两张牌的优先级,并返回相应的比较结果(小于 0、等于 0 或大于 0),qsort 函数会根据这个结果来对数组元素进行排序。

2、基数排序


定义:基数排序是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。
 
基本原理:
将所有待比较数值统一为同样的数位长度,数位较短的数前面补零。然后从最低位开始,依次进行排序。这样从最低位排序一直到最高位排序完成后,数列就变成有序的了。
 
譬如:
 
以十进制为例,假设有一组数据  {329, 457, 657, 839, 436, 720, 355} 。
 
1. 首先按照个位数字进行排序,将数字分配到对应的桶(队列)中。个位数字为0的放入第0个桶,为1的放入第1个桶,以此类推。第一轮排序后,数据在桶中的顺序为  720, 436, 355, 457, 657, 329, 839 。
 
2. 接着按照十位数字进行排序,将上一轮桶中的数据依次取出,再根据十位数字放入相应桶中。第二轮排序后顺序为  720, 329, 436, 839, 355, 457, 657 。
 
3. 最后按照百位数字进行排序,得到最终的有序序列  329, 355, 436, 457, 657, 720, 839 。
 
算法特点:

 
1)时间复杂度:基数排序的时间复杂度为 O(d(n + k)),其中 d 是数字的最大位数,n 是待排序元素的个数,k 是基数(如十进制的基数是10)。
 
2)空间复杂度:空间复杂度为 O(n + k),需要额外的空间来存储桶(队列)。
 
3)稳定性:基数排序是稳定的排序算法,即相同数值的元素在排序前后的相对位置不变。
 
适用情况:

基数排序适用于元素取值范围不大、位数固定的情况,如对大量的身份证号码、邮政编码等进行排序。但如果数据的位数相差很大,或者取值范围非常大,可能会导致性能下降。

思考:
基数排序先从个位开始比较而不是先从百位开始,原因是:
 
1)保证排序稳定性
 
基数排序是稳定的排序算法,先从个位开始比较能确保相同高位数字的元素,在低位排序后相对顺序不变。如果先从百位开始排序,那么在后续对十位、个位排序时,可能会改变百位相同元素的相对顺序,从而破坏稳定性。
 
2)符合排序逻辑
 
先从个位排序,再依次到十位、百位等高位,是按照数字的权重从低到高进行排序。只有先将低位排序稳定,才能在更高位排序时,不会因低位的不确定性而影响整体排序结果。如果先对高位进行排序,当高位相同时,低位的无序会导致整体排序不准确。例如,对于  329  和  355 ,若先按百位排序,它们在同一组,此时无法确定它们的顺序,只有先对个位、十位排序后,才能准确确定它们的顺序。
 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值