数据结构与算法 汉诺塔问题和列车车厢重排问题
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个缓冲轨道才能完成排序:打印过程