数据结构与算法 汉诺塔问题和列车车厢重排问题

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个缓冲轨道才能完成排序:打印过程

 

 

posted @ 2019-03-03 11:48  Alpha205  阅读(216)  评论(0编辑  收藏  举报