用的是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: