东北大学《算法设计与分析》课设任务二:递归函数仿真系统

用的是QT:

mainwindow.h:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QPushButton>
#include <QLabel>
#include <QLineEdit>
#include <QTextEdit>
#include <QTimer>
#include <vector>
#include <stack>
#include <utility> // for std::pair
static bool readKLogged = false;
class SimulatedStack;

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void startSearch();
    void pauseSearch();
    void resumeSearch();
    void continueSearch();

private:
    void drawStack();
    void drawTape();
    void drawWorkTape();
    void drawOutputTape();
    void drawReadWriteHead();
    void updateScene();
    void updateTapeColors(bool success);
    int binarySearch(std::vector<int>& arr, int left, int right, int x, SimulatedStack& simulatedStack, int& steps);

    QGraphicsView *graphicsView;
    QGraphicsScene *scene;
    QVBoxLayout *mainLayout;
    QHBoxLayout *inputLayout;
    QLineEdit *arrayInput;
    QLineEdit *targetInput;
    QPushButton *startButton;
    QPushButton *pauseButton;
    QPushButton *resumeButton;
    QLabel *resultLabel;
    QLabel *stepsLabel;
    QLabel *stackSpaceLabel;
    QLabel *tapeSpaceLabel;
    QTextEdit *logText;

    std::vector<int> arr;
    std::vector<int> workTape;  // 工作纸带
    std::vector<int> outputTape; // 输出纸带
    int target;
    int left;
    int right;
    int steps;
    int currentMid;
    SimulatedStack *simulatedStack;
    QTimer *timer;
    bool searchCompleted;
    bool isPushing;
    bool isPopping;
    int maxStackSize;
    bool found;

    int readWriteHeadPosition;  // 读写头位置
    int tapeOffset;  // 纸带的偏移量
    int workTapeOffset;  // 工作纸带的偏移量
    int outputTapeOffset;  // 输出纸带的偏移量
    int tapeLength;  // 纸带长度
};

class SimulatedStack
{
public:
    SimulatedStack();
    void push(int left, int right);
    std::pair<int, int> pop();
    int getMaxSize() const;
    bool empty() const;
    void clear();
    std::vector<std::pair<int, int>> getStack() const;

private:
    std::stack<std::pair<int, int>> stack;
    int max_size;
};

#endif // MAINWINDOW_H

mainwindow.cpp:

#include "mainwindow.h"
#include <QGraphicsRectItem>
#include <QGraphicsTextItem>
#include <QFont>
#include <QGraphicsLineItem>
#include <QHBoxLayout>
#include <algorithm>
#include <iostream>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent), left(0), right(0), steps(0), searchCompleted(false), isPushing(true), isPopping(false), maxStackSize(0), found(false), readWriteHeadPosition(0), tapeOffset(0), workTapeOffset(0), outputTapeOffset(0), tapeLength(15) // 设置纸带长度
{
    QWidget *centralWidget = new QWidget(this);
    mainLayout = new QVBoxLayout(centralWidget);
    inputLayout = new QHBoxLayout();

    arrayInput = new QLineEdit(this);
    arrayInput->setPlaceholderText("输入元素集合");
    arrayInput->setFixedHeight(30);
    arrayInput->setMinimumWidth(400);
    targetInput = new QLineEdit(this);
    targetInput->setPlaceholderText("输入要搜索的目标值");
    targetInput->setFixedHeight(30);

    inputLayout->addWidget(arrayInput);
    inputLayout->addWidget(targetInput);

    graphicsView = new QGraphicsView(this);
    scene = new QGraphicsScene(this);
    graphicsView->setScene(scene);

    startButton = new QPushButton("开始查找", this);
    pauseButton = new QPushButton("暂停", this);
    resumeButton = new QPushButton("继续", this);
    resultLabel = new QLabel(this);
    resultLabel->setMinimumHeight(30);
    //stepsLabel = new QLabel("Steps: 0", this);
    //stepsLabel->setFixedWidth(100); // 缩小步骤框的宽度
    stackSpaceLabel = new QLabel("Stack Space: 0", this);
    tapeSpaceLabel = new QLabel("Tape Space: 0", this);
    logText = new QTextEdit(this);
    logText->setReadOnly(true);

    mainLayout->addLayout(inputLayout);
    mainLayout->addWidget(graphicsView);
    mainLayout->addWidget(startButton);
    mainLayout->addWidget(pauseButton);
    mainLayout->addWidget(resumeButton);
    mainLayout->addWidget(stackSpaceLabel);
    mainLayout->addWidget(tapeSpaceLabel);
    mainLayout->addWidget(logText);
    mainLayout->addWidget(resultLabel);

    setCentralWidget(centralWidget);

    connect(startButton, &QPushButton::clicked, this, &MainWindow::startSearch);
    connect(pauseButton, &QPushButton::clicked, this, &MainWindow::pauseSearch);
    connect(resumeButton, &QPushButton::clicked, this, &MainWindow::resumeSearch);

    simulatedStack = new SimulatedStack();
    timer = new QTimer(this);
    connect(timer, &QTimer::timeout, this, &MainWindow::continueSearch);
}

MainWindow::~MainWindow()
{
    delete simulatedStack;
}

void MainWindow::startSearch()
{
    readKLogged =false;
    QString arrayText = arrayInput->text();
    QStringList arrayElements = arrayText.split(",");
    arr.clear();
    for (const QString &element : arrayElements) {
        arr.push_back(element.trimmed().toInt());
    }

    std::sort(arr.begin(), arr.end());

    target = targetInput->text().toInt();

    left = 0;
    right = arr.size() - 1;
    steps = 0;
    searchCompleted = false;
    isPushing = true;
    isPopping = false;
    maxStackSize = 0;
    found = false;

    scene->clear();
    resultLabel->clear();
    logText->clear();

    stackSpaceLabel->setText("栈大小: 0");
    tapeSpaceLabel->setText("纸带空间: 0");

    simulatedStack->clear();
    workTape = std::vector<int>(arr.size(), -1); // 初始化工作纸带
    outputTape = std::vector<int>(arr.size(), -1); // 初始化输出纸带
    readWriteHeadPosition = 0; // 初始化读写头位置
    tapeOffset = 0; // 初始化纸带偏移量
    workTapeOffset = 0; // 初始化工作纸带偏移量
    outputTapeOffset = 0; // 初始化输出纸带偏移量

    // 初始化时压入初始范围
    simulatedStack->push(left, right);
    maxStackSize = std::max(maxStackSize, static_cast<int>(simulatedStack->getMaxSize()));

    drawStack();
    drawTape();
    drawWorkTape();
    drawOutputTape();
    drawReadWriteHead();

    timer->start(1000); // Start the search with a 1 second interval
}

void MainWindow::pauseSearch()
{
    timer->stop();
}

void MainWindow::resumeSearch()
{
    timer->start(1000);
}
void MainWindow::continueSearch()
{
    // 标志变量,用于标记是否已记录过readK步骤

    if (searchCompleted) {
        // 清空栈时绘制空栈状态
        if (!simulatedStack->empty()) {
            simulatedStack->pop();
            logText->append(QString("Step %1: return").arg(++steps)); // 记录每次return操作
            updateScene();
        }
        if (simulatedStack->empty()) {
            timer->stop();
        }
        return;
    }

    if (right >= left) {
        steps++;

        int mid = left + (right - left) / 2;
        currentMid = mid;

        logText->append(QString("Step %1: 调用递归函数").arg(steps));
        logText->append(QString("Step %1: 读取low低位").arg(++steps));
        logText->append(QString("Step %1: 读取high高位").arg(++steps));
        logText->append(QString("Step %1: 写中间值").arg(++steps));

        if (!readKLogged) { // 只在第一次调用readK时记录
            logText->append(QString("Step %1: 读取目标值").arg(++steps));
            readKLogged = true;
        }

        logText->append(QString("Step %1: 读取中间值").arg(++steps));
        logText->append(QString("Step %1: 目标值与中间值相比较").arg(++steps));

        // 模拟图灵机的工作纸带操作
        workTape[workTapeOffset] = arr[mid];
        workTapeOffset++; // 将工作纸带左移一格

        if (arr[mid] == target)
        {
            resultLabel->setText(QString("Element %1 found at index %2").arg(target).arg(mid));
            searchCompleted = true;
            found = true;
            outputTape[0] = arr[mid]; // 设置输出纸带的第一个元素为找到的元素
            outputTapeOffset = 1; // 找到目标元素时,输出纸带使用的空间为1

            // 更新 tapeOffset 确保纸带移动到读写头位置
            tapeOffset = mid;

            // 在找到目标值后立即更新显示
            updateScene();

            isPopping = true; // 开始清空栈的过程
            timer->start(500); // 开始逐个弹栈

            // 计算总纸带空间:原始纸带长度 + 工作纸带已用的空间 + 输出纸带已用的空间
            int originalTapeSpace = arr.size();
            int workTapeUsedSpace = workTapeOffset;
            int outputTapeUsedSpace = outputTapeOffset;
            int totalTapeSpace = originalTapeSpace + workTapeUsedSpace + outputTapeUsedSpace;

            tapeSpaceLabel->setText(QString("Tape Space: %1").arg(totalTapeSpace));

            logText->append(QString("Step %1: 找到该元素").arg(++steps)); // 记录found操作

            return;
        }

        if (arr[mid] > target) {
            right = mid - 1;
            if (right >= left) {
                simulatedStack->push(left, right);
            }
        }
        else
        {
            left = mid + 1;
            if (right >= left)
            {
                simulatedStack->push(left, right);
            }
        }

        maxStackSize = std::max(maxStackSize, static_cast<int>(simulatedStack->getMaxSize()));
        stackSpaceLabel->setText(QString("Stack Space: %1").arg(maxStackSize));

        // 在更新纸带偏移量之前清理旧的纸带
        scene->clear();

        // 更新纸带偏移量
        tapeOffset = mid;

        updateScene();
    }
    else
    {
        resultLabel->setText(QString("Element %1 not found").arg(target));
        searchCompleted = true;
        maxStackSize = std::max(maxStackSize, static_cast<int>(simulatedStack->getMaxSize()));
        isPopping = true; // 开始清空栈的过程
        timer->start(500); // 开始逐个弹栈

        logText->append(QString("Step %1: 未找到目标值").arg(++steps));
    }

    // 计算总纸带空间:原始纸带长度 + 工作纸带已用的空间 + 输出纸带已用的空间
    int originalTapeSpace = arr.size();
    int workTapeUsedSpace = workTapeOffset;
    int outputTapeUsedSpace = outputTapeOffset;
    int totalTapeSpace = originalTapeSpace + workTapeUsedSpace + outputTapeUsedSpace;

    tapeSpaceLabel->setText(QString("Tape Space: %1").arg(totalTapeSpace));
}

SimulatedStack::SimulatedStack()
    : max_size(0)
{
}

void SimulatedStack::push(int left, int right)
{
    stack.push({left, right});
    if (stack.size() > max_size) {
        max_size = stack.size();
    }
}

std::pair<int, int> SimulatedStack::pop()
{
    if (stack.empty()) {
        throw std::runtime_error("Pop from empty stack");
    }
    auto value = stack.top();
    stack.pop();
    return value;
}

int SimulatedStack::getMaxSize() const
{
    return max_size;
}

bool SimulatedStack::empty() const
{
    return stack.empty();
}

void SimulatedStack::clear()
{
    while (!stack.empty()) {
        stack.pop();
    }
    max_size = 0;
}

std::vector<std::pair<int, int>> SimulatedStack::getStack() const
{
    std::vector<std::pair<int, int>> stackData;
    std::stack<std::pair<int, int>> tempStack = stack;
    while (!tempStack.empty()) {
        stackData.push_back(tempStack.top());
        tempStack.pop();
    }
    std::reverse(stackData.begin(), stackData.end());
    return stackData;
}

void MainWindow::drawStack()
{
    std::vector<std::pair<int, int>> stackData = simulatedStack->getStack();
    int y = 0;
    int x = -scene->width() / 2; // 将栈的位置设置在屏幕最左边

    for (const auto& [left, right] : stackData) {
        QGraphicsRectItem *rect = scene->addRect(x, y, 50, 50); // 将栈的位置设置在最左边
        QGraphicsTextItem *text = scene->addText(QString("(%1, %2)").arg(left).arg(right));
        text->setPos(x + 5, y + 10);
        y += 50;
    }

    // 保持最大栈深度的指示
    for (int i = stackData.size(); i < maxStackSize; ++i) {
        QGraphicsRectItem *rect = scene->addRect(x, y, 50, 50); // 将栈的位置设置在最左边
        y += 50;
    }

    // 绘制栈顶部指针
    if (!stackData.empty() || maxStackSize > 0) {
        QGraphicsTextItem *topText = scene->addText("top");
        topText->setPos(x - 30, y - 50);
        QGraphicsLineItem *line = scene->addLine(x - 10, y - 25, x, y - 25);
    }
}

void MainWindow::drawTape()
{
    int x = 100 - tapeOffset * 50;
    int y = 0;
    QGraphicsTextItem *label = scene->addText("输入纸带");
    label->setPos(x - 70, y + 10); // 添加输入纸带的标签

    for (int i = 0; i < tapeLength; ++i) {  // 使用纸带长度
        QGraphicsRectItem *rect = scene->addRect(x + i * 50, y, 50, 50);
        QGraphicsTextItem *text = nullptr;
        if (i < arr.size()) {
            text = scene->addText(QString::number(arr[i]));
        } else {
            text = scene->addText("");
        }
        text->setPos(x + i * 50 + 15, y + 10);

        if (i == currentMid) {
            rect->setBrush(Qt::yellow);
        }
    }
}

void MainWindow::drawWorkTape()
{
    int x = 100 - workTapeOffset * 50;
    int y = 100;
    QGraphicsTextItem *label = scene->addText("工作纸带");
    label->setPos(x - 70, y + 10); // 添加工作纸带的标签

    for (int i = 0; i < tapeLength; ++i) {  // 使用纸带长度
        QGraphicsRectItem *rect = scene->addRect(x + i * 50, y, 50, 50); // 确保工作纸带在写完中间值后左移
        QGraphicsTextItem *text = nullptr;
        if (i < workTapeOffset) {
            text = scene->addText(workTape[i] == -1 ? "" : QString::number(workTape[i]));
        } else {
            text = scene->addText("");
        }
        text->setPos(x + i * 50 + 15, y + 10);
    }
}

void MainWindow::drawOutputTape()
{
    int x = 100 - outputTapeOffset * 50;
    int y = 200;
    QGraphicsTextItem *label = scene->addText("输出纸带");
    label->setPos(x - 70, y + 10); // 添加输出纸带的标签

    for (int i = 0; i < tapeLength; ++i) {  // 使用纸带长度
        QGraphicsRectItem *rect = scene->addRect(x + i * 50, y, 50, 50); // 确保输出纸带在写完目标值后左移
        QGraphicsTextItem *text = nullptr;
        if (i < outputTapeOffset) {
            text = scene->addText(outputTape[i] == -1 ? "" : QString::number(outputTape[i]));
        } else {
            text = scene->addText("");
        }
        text->setPos(x + i * 50 + 15, y + 10);
    }
}

void MainWindow::drawReadWriteHead()
{
    int yStart = 0;
    int yEnd = 300;
    int x = 100;

    QGraphicsRectItem *headRect = scene->addRect(x, yStart, 50, yEnd - yStart);
    headRect->setBrush(Qt::transparent); // 使用透明颜色,不遮挡内容
    QGraphicsTextItem *text = scene->addText("读写头");
    text->setPos(x + 15, yEnd / 2 - 10); // 将“RW”文字放在长方形中间
}

void MainWindow::updateScene()
{
    scene->clear();
    drawStack();
    drawTape();
    drawWorkTape();
    drawOutputTape();
    drawReadWriteHead();
}




// 二分搜索递归函数
int MainWindow::binarySearch(std::vector<int>& arr, int left, int right, int x, SimulatedStack& simulatedStack, int& steps) {
    simulatedStack.push(left, right);

    steps++;
    if (right >= left) {
        int mid = left + (right - left) / 2;
        logText->append(QString("Step %1: Searching in range [%2, %3], mid: %4")
                        .arg(steps).arg(left).arg(right).arg(mid));

        // 如果元素刚好在中间位置
        if (arr[mid] == x) {
            simulatedStack.pop();
            return mid;
        }

        // 如果元素在中间位置的左边
        if (arr[mid] > x) {
            int result = binarySearch(arr, left, mid - 1, x, simulatedStack, steps);
            simulatedStack.pop();
            return result;
        }

        // 如果元素在中间位置的右边
        int result = binarySearch(arr, mid + 1, right, x, simulatedStack, steps);
        simulatedStack.pop();
        return result;
    }

    simulatedStack.pop();
    // 如果元素不存在于数组中
    return -1;
}

这是运行的结果:

我们输入测试案例:A=[0,1,2,3,4,6,7,8,9,10], x=5:

然后查找元素0:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值