《数据结构》课程设计--c++栈

栈简单介绍

栈(Stack)是一种后进先出(LIFO,Last In First Out)的数据结构。它的操作特点是元素只能从一端进行添加和移除,这端叫做栈顶。常见操作包括:

  • 压栈(push):将元素添加到栈顶。
  • 出栈(pop):移除并返回栈顶元素。
  • 查看栈顶元素(peek):获取栈顶元素但不移除。

栈常用于实现递归、括号匹配、浏览器的前进/后退功能等。

实验题目

铁路调度系统

实验内容

任务目标

“丁”字型铁路调度系统如下图所示,由垂直的两条铁轨组成,水平方向的为主铁轨,竖直方向的为辅助铁轨。辅助铁轨用于对车厢次序进行调整,它在主铁轨中间,把主铁轨分成左右两部分。主铁轨左边的车厢只能从左边开到右边;或者从主铁轨左边进入辅助铁轨;辅助铁轨上的车厢只可以进入主铁轨右边。

现在有n节车厢,编号为1、2、...、n,在主铁轨左边以随机顺序排列,要求通过调度系统,在主铁轨的右边顺序开出(即按1、2、...、n的顺序)。

基于栈和队列相关知识和代码实现,实现该铁路调度系统对车厢调度过程。

任务描述 

  • 可以手动输入待调度的车厢初始序列(编号之间用空格或换行分开),输出是否能调度成功,以及调度每一步的调度过程;并输出栈的总入栈(出栈)次数;
  • 当n=100时,随机生成100个初始序列,测试这些初始序列能否被成功调度,并输出测试结果(包括100个初始序列以及其是否能被成功调度),并统计一下调度的成功率;
  • 提供菜单,实现以上功能;
  • 如要做的更好,可以考虑将调度过程动态显示,甚至是动画显示;

解决方案

设计思路

1. 数据结构选择

使用了模板类 SeqStack 表示栈,用于模拟火车调度系统中的辅轨道。栈的实现使得车厢的入栈和出栈操作能够高效进行。

2. 主程序结构

主程序采用了一个无限循环,允许用户选择手动输入车厢初始序列或随机生成100个初始序列并测试调度结果。这样的设计使得程序能够反复执行,并提供了不同的操作选择。

3. 手动输入调度

用户通过输入车厢数和初始序列,模拟了手动输入待调度的车厢初始序列的场景。程序根据输入的顺序,模拟车厢的进入主轨道和辅轨道的过程,并输出每一步的调度过程。

4. 随机生成调度

使用了 randperm 函数,该函数的实现未提供,但可以推测是用于生成随机的车厢初始序列。通过循环进行100次测试,每次测试都生成一个随机序列,并尝试进行调度,统计成功调度的次数和成功率。

算法设计

顺序栈模板类 的数据成员设计

template<class ElemType>
class SeqStack
{
protected:
    // 顺序栈的数据成员:
    int top;                                        // 栈顶指针 
    int maxSize;                                    // 栈的最大容量 
    ElemType* elems;                                // 元素存储空间
public:
    //  顺序栈的方法声明及重载编译系统默认方法声明:
    SeqStack(int size = DEFAULT_SIZE);              // 构造函数
    virtual ~SeqStack();                            // 析构函数
    int GetLength() const;                          // 求栈的长度             
    bool IsEmpty() const;                           // 判断栈是否为空
    void Clear();                                   // 将栈清空
    void Traverse(void (*Visit)(const ElemType&)) const;    // 遍历栈
    Status Push(const ElemType e);                  // 入栈
    Status Top(ElemType& e) const;                  // 取顶元素
    Status Pop(ElemType& e);                        // 出栈
    SeqStack(const SeqStack<ElemType>& s);      // 复制构造函数
    SeqStack<ElemType>& operator =(const SeqStack<ElemType>& s); // 赋值语句重载
};

这段代码定义了一个模板类 `SeqStack`,表示一个基于数组实现的顺序栈。

1. 数据成员:

  •    - `top`:栈顶指针,指示当前栈顶元素的位置。
  •    - `maxSize`:栈的最大容量。
  •    - `elems`:动态数组,用于存储栈中的元素。

2. 构造函数和析构函数:

  •    - 构造函数初始化栈,可以指定最大容量,默认为 `DEFAULT_SIZE`。
  •    - 析构函数释放动态分配的内存。

3. 基本操作:

  •    - `GetLength`:返回栈的长度。
  •    - `IsEmpty`:判断栈是否为空。
  •    - `Clear`:清空栈。
  •    - `Traverse`:遍历栈中的元素,对每个元素执行传入的函数。
  •    - `Push`:将元素入栈。
  •    - `Top`:获取栈顶元素但不出栈。
  •    - `Pop`:将栈顶元素出栈。

4. 复制构造函数和赋值语句重载:

  •    - 复制构造函数用于根据已有的栈创建一个新的栈。
  •    - 赋值语句重载允许通过一个栈的内容赋值给另一个栈。

这个模板类提供了基本的栈操作,可以适用于不同类型的数据。使用时,可以通过实例化这个类来创建特定类型的顺序栈。

顺序栈模板类 的成员函数实现

template<class ElemType>
SeqStack<ElemType>::SeqStack(int size)
// 操作结果:构造一个最大容量为size的空栈
{
    maxSize = size;                     // 栈的最大容量
    if (elems != NULL) {delete[]elems;} // 释放已有存储空间
    elems = new ElemType[maxSize];      // 分配新的存储空间
    top = -1;
}

template<class ElemType>
SeqStack<ElemType>::~SeqStack()
// 操作结果:销毁栈
{
    delete[]elems;                  // 释放栈的存储空间
}

template <class ElemType>
int SeqStack<ElemType>::GetLength() const
// 操作结果:返回栈中元素个数
{
    return top + 1;
}

template<class ElemType>
bool SeqStack<ElemType>::IsEmpty() const
// 操作结果:如栈为空,则返回true,否则返回false
{
    return top == -1;
}

template<class ElemType>
void SeqStack<ElemType>::Clear()
// 操作结果:清空栈
{
    top = -1;
}

template <class ElemType>
void SeqStack<ElemType>::Traverse(void (*Visit)(const ElemType&)) const
// 操作结果:从栈顶到栈底依次对栈的每个元素调用函数(*visit)访问 
{
    for (int i = top; i >= 0; i--)
        (*Visit)(elems[i]);
}

template<class ElemType>
Status SeqStack<ElemType>::Push(const ElemType e)
// 操作结果:将元素e追加到栈顶,如成功则返加SUCCESS,如栈已满将返回OVER_FLOW
{
    if (top == maxSize - 1) // 栈已满
        return OVER_FLOW;
    else {  // 操作成功
        elems[++top] = e;   // 将元素e追加到栈顶 
        return SUCCESS;
    }
}

template<class ElemType>
Status SeqStack<ElemType>::Top(ElemType& e) const
// 操作结果:如栈非空,用e返回栈顶元素,函数返回SUCCESS,否则函数返回UNDER_FLOW
{
    if (IsEmpty())   // 栈空
        return UNDER_FLOW;
    else { // 栈非空,操作成功
        e = elems[top]; // 用e返回栈顶元素
        return SUCCESS;
    }
}

template<class ElemType>
Status SeqStack<ElemType>::Pop(ElemType& e)
// 操作结果:如栈非空,删除栈顶元素,并用e返回栈顶元素,函数返回SUCCESS,否则
// 函数返回UNDER_FLOW
{
    if (IsEmpty())   // 栈空
        return UNDER_FLOW;
    else { // 操作成功
        e = elems[top--]; // 用e返回栈顶元素
        return SUCCESS;
    }
}

template<class ElemType>
SeqStack<ElemType>::SeqStack(const SeqStack<ElemType>& s)
// 操作结果:由栈s构造新栈--复制构造函数
{
    maxSize = s.maxSize;                // 栈的最大容量 
    if (elems != NULL) delete[]elems;   // 释放已有存储空间
    elems = new ElemType[maxSize];      // 分配存储空间
    top = s.top;                        // 复制栈顶指针 
    for (int i = 0; i <= top; i++)      // 从栈底到栈顶对栈s的每个元素进行复制
        elems[i] = s.elems[i];
}

template<class ElemType>
SeqStack<ElemType>& SeqStack<ElemType>::operator = (const SeqStack<ElemType>& s)
// 操作结果:将栈s赋值给当前栈--赋值语句重载
{
    if (&s != this) {
        maxSize = s.maxSize;                // 栈的最大容量 
        if (elems != NULL) delete[]elems;   // 释放已有存储空间
        elems = new ElemType[maxSize];      // 分配存储空间
        top = s.top;                        // 复制栈顶指针 
        for (int i = 0; i <= top; i++)      // 从栈底到栈顶对栈s的每个元素进行复制
            elems[i] = s.elems[i];
    }
    return *this;
}

这段代码是一个模板类 `SeqStack` 的实现,表示一个基于数组的顺序栈。

1. 构造函数:

  •  - 初始化栈的最大容量为给定的 `size`。
  •  - 释放已有的存储空间,然后分配新的存储空间。

2. 析构函数:

  •  - 释放动态分配的存储空间。

3. 获取栈长度:

  •  - 返回栈中元素的个数。

4. 判断栈是否为空:

  •  - 如果栈为空,返回 `true`;否则返回 `false`。

5. 清空栈:

  •  - 将栈清空,即将栈顶指针置为 -1。

6. 遍历栈:

  •  - 从栈顶到栈底依次对栈的每个元素调用传入的函数进行访问。

7. 入栈操作:

  •  - 将元素追加到栈顶,如果栈已满,则返回 `OVER_FLOW`。

8. 取栈顶元素:

  •  - 如果栈非空,将栈顶元素用参数返回,返回 `SUCCESS`;否则返回 `UNDER_FLOW`。

9. 出栈操作:

  •  - 如果栈非空,删除栈顶元素,将栈顶元素用参数返回,返回 `SUCCESS`;否则返回 `UNDER_FLOW`。

10. 复制构造函数:

  •  - 根据已有的栈 `s` 创建一个新的栈。

11. 赋值语句重载:

  •  - 将栈 `s` 的内容赋值给当前栈。

这个实现允许栈存储不同类型的元素,提供了基本的栈操作,并且支持复制构造和赋值语句的重载。

源程序代码 RailwayCarriage.cpp

#include "SeqStack.h"   // 循环队列类
#include<vector>
#include<iostream>

int main(void)
{
    int command;
    while (1)
    {
        cout << "请选输入功能对应序号" << endl;
        cout << "1.手动输入待调度的车厢初始序列(编号之间用空格或换行分开),输出是否能调度成功,以及调度每一步的调度过程;并输出栈的总入栈(出栈)次数;" << endl;
        cout << "2.随机生成100个初始序列,测试这些初始序列能否被成功调度,并输出测试结果(包括100个初始序列以及其是否能被成功调度),并统计调度的成功率;" << endl;
        cin >> command;
        if (command == 1)
        {
            SeqStack<int> qa;
            int n, x, d, No, num1, num2;
            num1 = 0;//入栈次数
            num2 = 0;//出栈次数
            cout << "输入车厢数:";
            cin >> n;
            No = 1;
            cout << "输入 " << n << " 节车厢的初始顺序:";
            for (int i = 1; i <= n; i++) {
                cin >> d;
                if (d == No)
                {
                    cout << "第 " << d << " 号车厢从主轨道左边进入主轨道右边." << endl;
                    No++;
                }
                else
                {
                    qa.Push(d);
                    cout << "第 " << d << " 号车厢从主轨道左边进入辅轨道." << endl;
                    num1++;
                }
                while (qa.IsEmpty() == false)
                {
                    if (qa.Top(x) == SUCCESS && x == No)
                    {
                        qa.Pop(x);
                        cout << "第 " << x << " 号车厢从辅轨道进入主轨道右边." << endl;
                        No++;
                        num2++;
                    }
                    else
                        break;
                }
            }
            if (qa.IsEmpty())
                cout << "调度完成." << endl;
            else
                cout << "调度无法完成." << endl;
            cout << "总入栈次数" << num1 << endl;
            cout << "总出栈次数" << num2 << endl;
        }
        if (command == 2)
        {
            SeqStack<int> qa;
            int n, x, No, num1, num2, success_num = 0;//调度成功的次数
            num1 = 0;//入栈次数
            num2 = 0;//出栈次数
            cout << "请输入单组数据的长度" << endl;
            cin >> n;
            int times = 100;
            srand((int)time(0));
            while (times--)
            {
                No = 1;
                vector<int>temp;
                randperm(temp, n);
                for (int i = 0; i < n; i++)
                {
                    if (temp[i] == No)
                    {
                        No++;
                    }
                    else
                    {
                        qa.Push(temp[i]); 
                        num1++;
                    }
                    while (qa.IsEmpty() == false)
                    {
                        if (qa.Top(x) == SUCCESS && x == No)
                        {
                            qa.Pop(x);      
                            No++;
                            num2++;
                        }
                        else
                            break;
                    }
                }
                if (qa.IsEmpty())
                {
                    cout << "调度完成." << endl;
                    success_num++;
                }
                else
                    cout << "调度无法完成." << endl;
                qa.Clear();
                
            } 
            cout << success_num << "%" << endl;
        }
        system("pause");
        system("cls");
    }
    system("pause");
    return 0;
}

这是一个火车调度系统的主程序。

1. 包含头文件和库:

  •  - `Assistance.h` 和 `SeqStack.h` 是预定义的辅助软件包和循环队列类的头文件。
  •  - 使用了 `<vector>` 和 `<iostream>` 标准库。

2. 主函数:

  •  - 进入一个无限循环,允许用户选择功能。
  •  - 用户可以选择手动输入待调度的车厢初始序列,或者随机生成100个初始序列并测试调度结果。
  •  - 调用相应的函数完成用户选择的功能。

3. 手动输入待调度的车厢初始序列:

  •  - 使用 `SeqStack<int>` 类来模拟火车调度系统。
  •  - 用户输入车厢数 `n` 和初始顺序。
  •  - 车厢按照指定顺序进入主轨道和辅轨道,模拟调度过程。
  •  - 输出是否能够成功调度,以及每一步的调度过程。
  •  - 统计总入栈次数和总出栈次数。

4. 随机生成100个初始序列:

  •  - 使用 `randperm` 函数生成随机序列,该函数的实现未提供。
  •  - 循环进行100次测试,每次测试都会生成一个随机序列并尝试调度。
  •  - 输出每个初始序列以及是否能够成功调度。
  •  - 统计成功调度的次数,并输出成功率。

5. 总结:

  •  - 通过循环、条件判断和函数调用模拟了火车调度的过程。
  •  - 使用 `SeqStack` 类实现栈的功能,记录了总入栈次数和总出栈次数。
  •  - 通过手动输入和随机生成的方式测试了不同情况下的调度结果。

算法复杂度分析:

1. 手动输入待调度的车厢初始序列部分:

  • - 用户输入操作:O(1)(假设输入操作的时间复杂度是常数时间)。
  • - 遍历车厢序列:O(n)(n 为车厢的数量,需要遍历所有车厢)。
  • - 主轨道和辅轨道的模拟操作:O(n)(与车厢数量成正比,每个车厢都需要相应的操作)。
  • - 辅轨道的入栈和出栈操作:O(1)(由于使用了顺序栈 `SeqStack`,入栈和出栈的时间复杂度为常数时间)。
  • 总体复杂度:O(n)

2. 随机生成100个初始序列并测试调度结果部分:

  • - 循环测试操作:O(1)(假设循环测试的次数是常数)。
  • - 生成随机序列:O(n)(假设 `randperm` 的复杂度与车厢数量成正比,需要遍历所有车厢)。
  • - 主轨道和辅轨道的模拟操作:O(n)(与车厢数量成正比,每个车厢都需要相应的操作)。
  • - 辅轨道的入栈和出栈操作:O(1)(由于使用了顺序栈 `SeqStack`,入栈和出栈的时间复杂度为常数时间)。
  • 总体复杂度:O(n)

3. SeqStack 类的实现部分:

  • - 栈的入栈和出栈操作:O(1)(由于采用顺序栈,入栈和出栈的时间复杂度为常数时间)。
  • - 遍历栈中元素:O(n)(n 为栈中元素的数量,需要遍历所有元素)。
  • 总体复杂度:O(n)

综合总结:

  • - 整个程序的时间复杂度主要受到遍历车厢序列和栈操作的影响。
  • - 在输入车厢序列和模拟调度过程时,时间复杂度主要与车厢数量成正比,为 O(n)。
  • - 使用的顺序栈 `SeqStack` 在入栈和出栈操作上具有常数时间复杂度,因此对整体时间复杂度的影响较小。

实验结果示例

功能1.1:手动输入车厢初始序列(失败)

功能1.2:手动输入车厢初始序列(成功)

功能2.1:自动生成车厢初始序列(失败)

功能3:动画演示 暂无 提供思路:Qt、ppt制作动画

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值