数据结构与算法 汉诺塔问题和列车车厢重排问题
1. 汉诺塔问题:
(a)通过递归的方式解决:https://blog.csdn.net/zj1131190425/article/details/85156570
// 汉诺塔问题: 递归解决方案 void movedisk(int n, char fromTower, char toTower, char auxTower) { if(n==1) { cout << "Move disk " << n << " from " << fromTower << " to " << toTower << endl; } else { movedisk(n-1, fromTower, auxTower, toTower); // 前n-1个盘子从A移到C,B作为辅助 cout << "Move disk " << n << " from " << fromTower << " to " << toTower << endl; // 第n个盘子从A到B movedisk(n-1, auxTower, toTower, fromTower); // n-1个盘子从C到B,A作为辅助 } }
递归法的时间复杂度:
按照递归的方法计算,递归的时间复杂度为
假设每次移动后要显示每个塔的布局:
每座塔移动碟子是按照 last in first out的次序进行的,可以把每座塔表示为一个栈。
复杂度问题:
三座塔上的盘子总数为n,如果使用链表形式的,则需要n个元素空间。如果使用数组形式的栈,则塔A需要n个元素空间,塔B需要n个元素空间,C需要n-1个元素空间,所以总共需要3n-1个元素空间。
虽然所需的空间不同,但是数组形式的栈运行速度更快。
思路依然是使用递归的方式,但是使用的是栈来存储数据:
代码:
#include <iostream> #include "E:\back_up\code\c_plus_code\sparseMatrix\external_file\arraystack.h" #include <string> using namespace std; // 对盘子进行编号 arrayStack<int> tower[4]; // ABC塔 // 显示塔的状态 void showState() { cout << "===============================" << endl; cout << "Stack A: " << endl; tower[1].print_stack(); cout << "Stack B: " << endl; tower[2].print_stack(); cout << "Stack C: " << endl; tower[3].print_stack(); } // 递归的方案 //函数moveAndShow(n, x, y, z) // (x,y,z)=(1,2,3) // 将n个盘子从x移动到y,z作为辅助 void moveAndShow(int n, int x, int y, int z) { if(n>0) { moveAndShow(n-1, x, z, y); int tmp = tower[x].top(); tower[x].pop(); tower[y].push(tmp); showState(); moveAndShow(n-1, z, y, x); } } void Hanio_tower(int n) // 初始化三座塔 { for(int i=n; i>0; i--) { tower[1].push(i); } moveAndShow(n, 1, 2, 3); // 将盘子从塔1移动到塔2,塔3作为辅助 } int main(int argc, char *argv[]) { Hanio_tower(3); }
运行结果:
-------------------------------------------分割线---------------------------------------------------------
2. 列车车厢重排问题
H1,H2,H3缓冲轨道(相当于一个FILO的结构)
问题描述:
将入轨道上车厢的编号,通过借助缓冲轨道,排列成出轨道上的顺序
问题分析:
1, 在入轨道上拿一个车厢,如果车厢编号满足条件,直接放入出轨道,接着找缓冲轨道上是否有满足条件的车厢,直到找完所有满足条件的,接着判断入轨道上的下一个车厢。
2. 否则放入缓冲轨道
3.放入缓冲轨道按照如下原则:
优先向非空的缓冲轨道放入,如果有多个满足条件的非空轨道,则选择编号相距最近的轨道
如果非空的都不满足条件,则放入空的缓冲轨道。
4. 返回 步骤1
代码:
#include <iostream> #include "E:\back_up\code\c_plus_code\sparseMatrix\external_file\arraystack.h" #include <string> using namespace std; // 对盘子进行编号 arrayStack<int> H[3]; // 缓冲轨道 arrayStack<int> in_rail; arrayStack<int> out_rail; void show_rail_state() { cout << "============================" << endl; cout << "In rail state: "; in_rail.print_stack(); cout << "H1 state: "; H[0].print_stack(); cout << "H2 state: "; H[1].print_stack(); cout << "H3 state: "; H[2].print_stack(); cout << "Out rail state: "; out_rail.print_stack(); } // 定义一个递归函数 // 参数cnt_number为引用传递,因为涉及到在函数内部修改它的值且需要保存下来 void cache_rail_process(int& cnt_num, int n1=10) { if(cnt_num<=n1) { for(int j=0; j<3; j++) // 遍历三个缓冲轨道 { if(!H[j].empty()) // 如果缓冲轨道非空,则判断是否为满足条件的车厢 { if(H[j].top()==cnt_num) { out_rail.push(H[j].top()); H[j].pop(); //train_number_cnt++; //cnt_num++; cache_rail_process(++cnt_num); } } } } } void rail(int train_number[], int n) { // train_number是车厢的初始排列顺序 for(int i=0; i<n; i++) { in_rail.push(train_number[i]); // 初始化入轨道 } int train_number_cnt = 1; // 要找的车厢编号 while(!in_rail.empty()) { int tmp_number = in_rail.top(); in_rail.pop(); if(tmp_number==train_number_cnt) // 如果车厢编号满足条件,直接放入出轨道 { out_rail.push(tmp_number); train_number_cnt++; // 将下面的函数修改为递归的形式 // 因为在栈中需要不断的选找满足条件的编号,所以需要递归进行 cache_rail_process(train_number_cnt, n); // 接着再看缓冲轨道里面有没有满足条件的车厢 /* for(int j=0; j<3; j++) // 遍历三个缓冲轨道 { if(!H[j].empty()) // 如果缓冲轨道非空,则判断是否为满足条件的车厢 { if(H[j].top()==train_number_cnt) { out_rail.push(H[j].top()); H[j].pop(); train_number_cnt++; } } }*/ } else { // 优先放到非空的轨道 if(H[0].empty() && H[1].empty() && H[2].empty()) // 三个缓冲轨道均为空 { H[0].push(tmp_number); // 随便放入一个轨道即可 } else // 否则优先放置到非空的缓冲轨道上 { int trace_flag[3]; // 三个缓冲轨道的标志位 for(int j=0; j<3; j++) { //if(!H[j].empty() && (H[j].top()>tmp_number)) if(!H[j].empty()) { trace_flag[j] = H[j].top() - tmp_number; // trace_flag[j]>0,第j个轨道可以放置, trace_flag[j]<0,第j个轨道不可以放置 } else { trace_flag[j] = 0; // 第j个轨道为空 } } // 判断可放置的位置 int place_cnt = 0; for(int j=0; j<3; j++) { if(trace_flag[j] > 0) place_cnt++; } if(place_cnt==0) // 非空的位置都不合适 { for(int k=0; k<3; k++) // 则放入空位置 { //if(H[k].empty()) if(trace_flag[k]==0) { H[k].push(tmp_number); break; } } } else if(place_cnt==1) // 只有一个非空位置能放入,则放入到这个位置 { for(int k=0; k<3; k++) { if(trace_flag[k]>0) { H[k].push(tmp_number); break; } } } else // 两个或者三个非空的位置可以放置,选择最优的位置 { int cmp=100; int cmp_index = 0; for(int k=0; k<3; k++) { if(trace_flag[k]<cmp && trace_flag[k]>0) { cmp = trace_flag[k]; cmp_index = k; // 找到最优的位置 } } H[cmp_index].push(tmp_number); } } } show_rail_state(); } // show_rail_state(); } int main(int argc, char *argv[]) { int number[] = {5,8,1,7,4,2,9,6,3}; int nn = 9; rail(number, nn); }
输出的结果:
复杂度分析:
改进的版本:
现在可以设置缓冲轨道的个数为任意多个:
修改程序的输入参数:
所需缓冲轨道的数量与车厢数有关,与车厢的排列顺序是否有关呢:
#include <iostream> #include "E:\back_up\code\c_plus_code\sparseMatrix\external_file\arraystack.h" #include <string> using namespace std; // 对盘子进行编号 int cache_trace_num = 5; arrayStack<int>* H = new arrayStack<int>[cache_trace_num]; //arrayStack<int> H[3]; // 缓冲轨道 arrayStack<int> in_rail; arrayStack<int> out_rail; void show_rail_state() { cout << "============================" << endl; cout << "In rail state: "; in_rail.print_stack(); /* cout << "H1 state: "; H[0].print_stack(); cout << "H2 state: "; H[1].print_stack(); cout << "H3 state: "; H[2].print_stack(); */ for(int i=0; i<cache_trace_num; i++) { cout << "H" << (i+1) << " state: "; H[i].print_stack(); } cout << "Out rail state: "; out_rail.print_stack(); } // 定义一个递归函数 // 参数cnt_number为引用传递,因为涉及到在函数内部修改它的值且需要保存下来 void cache_rail_process(int& cnt_num, int n1=10) { if(cnt_num<=n1) { for(int j=0; j<cache_trace_num; j++) // 遍历三个缓冲轨道 { if(!H[j].empty()) // 如果缓冲轨道非空,则判断是否为满足条件的车厢 { if(H[j].top()==cnt_num) { out_rail.push(H[j].top()); H[j].pop(); //train_number_cnt++; //cnt_num++; cache_rail_process(++cnt_num); } } } } } void rail(int train_number[], int n) { // train_number是车厢的初始排列顺序 for(int i=0; i<n; i++) { in_rail.push(train_number[i]); // 初始化入轨道 } int train_number_cnt = 1; // 要找的车厢编号 while(!in_rail.empty()) { int tmp_number = in_rail.top(); in_rail.pop(); if(tmp_number==train_number_cnt) // 如果车厢编号满足条件,直接放入出轨道 { out_rail.push(tmp_number); train_number_cnt++; // 将下面的函数修改为递归的形式 // 因为在栈中需要不断的选找满足条件的编号,所以需要递归进行 cache_rail_process(train_number_cnt, n); // 接着再看缓冲轨道里面有没有满足条件的车厢 /* for(int j=0; j<3; j++) // 遍历三个缓冲轨道 { if(!H[j].empty()) // 如果缓冲轨道非空,则判断是否为满足条件的车厢 { if(H[j].top()==train_number_cnt) { out_rail.push(H[j].top()); H[j].pop(); train_number_cnt++; } } }*/ } else { // 优先放到非空的轨道 bool all_empty = true; for(int i=0; i<cache_trace_num; i++) // 判断所有轨道是否为空 { if(!H[i].empty()) { all_empty = false; } } if(all_empty) { H[0].push(tmp_number); } else // 否则优先放置到非空的缓冲轨道上 { int trace_flag[cache_trace_num]; // 三个缓冲轨道的标志位 for(int j=0; j<cache_trace_num; j++) { //if(!H[j].empty() && (H[j].top()>tmp_number)) if(!H[j].empty()) { trace_flag[j] = H[j].top() - tmp_number; // trace_flag[j]>0,第j个轨道可以放置, trace_flag[j]<0,第j个轨道不可以放置 } else { trace_flag[j] = 0; // 第j个轨道为空 } } // 判断可放置的位置 int place_cnt = 0; for(int j=0; j<cache_trace_num; j++) { if(trace_flag[j] > 0) place_cnt++; } if(place_cnt==0) // 非空的位置都不合适 { for(int k=0; k<cache_trace_num; k++) // 则放入空位置 { //if(H[k].empty()) if(trace_flag[k]==0) { H[k].push(tmp_number); break; } } } else if(place_cnt==1) // 只有一个非空位置能放入,则放入到这个位置 { for(int k=0; k<cache_trace_num; k++) { if(trace_flag[k]>0) { H[k].push(tmp_number); break; } } } else // 两个或者多个非空的位置可以放置,选择最优的位置 { int cmp=100; int cmp_index = 0; for(int k=0; k<cache_trace_num; k++) { if(trace_flag[k]<cmp && trace_flag[k]>0) { cmp = trace_flag[k]; cmp_index = k; // 找到最优的位置 } } H[cmp_index].push(tmp_number); } } } show_rail_state(); } // show_rail_state(); } int main(int argc, char *argv[]) { int number[] = {3,8,6,10,14,1,11,15,4,2,9,13,7,12,5}; int nn = 15; //int number[] = {5,8,6,7,4,2,9,1,3,11,10}; //int nn = 11; rail(number, nn); }
车厢数增加到15个,顺序是随机排列
至少需要5个缓冲轨道才能完成排序:打印过程
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)