《程序员代码面试指南》it名企算法与数据结构题目最优解(第二版)刷题笔记day2

由于之前看了牛客网的数据结构和算法的课程知道了左神,现在顺便就买了他的书做做题吧,虽然书的题解都是java实现的,但好在用c++实现难度不大。

第一章 栈和队列

第一题:用一个栈实现另一个栈的排序
1.如果cur小于或等于help的栈顶元素,则将cur直接压入栈
2.如果cur大于help的栈顶元素,则将help的元素逐一弹出,逐一压入stack,直到cur小于或等于help的栈顶元素,再将cur压入help。

一直执行以上操作,直到stack中的全部元素都压入到help。最后将help中的所有元素逐一压入stack,即完成排序。

//用一个栈实现另一个栈的排序
#include "stdafx.h"
#include<stack>
#include <iostream>
using namespace std;
void UseAStackSortTheOther(stack<int> &TheOther){//又忘记传引用了
	stack<int> stackusing;
	int help;
	while (!TheOther.empty())
	{
		
		help=TheOther.top();
		if (stackusing.empty()||stackusing.top()>=help)
		{
			stackusing.push(help);
			TheOther.pop();
			
		}
		else{
			TheOther.pop();
			while(!stackusing.empty()&&stackusing.top()<help){
				TheOther.push(stackusing.top());
				stackusing.pop();
			}
			stackusing.push(help);
		}
	}
	while (!stackusing.empty())
	{
		TheOther.push(stackusing.top());
		stackusing.pop();
	}

}

第二题:用栈来求解汉诺塔问题
在汉诺塔规则的基础上,限制不能从最左的塔移动到最右的塔上,必须经过中间的塔,移动的跨度只能是一个塔。当塔有N层的时候,打印最优移动过程和最优移动步数。
方法一:使用递归的方法进行移动
方法二:使用栈进行移动

方法一:递归的方法。
首先,如果只剩最上层的塔需要移动,则有如下处理:
1.如果希望从“左”移到“中”,打印“Move 1 from left to mid” 。
2.如果希望从“中”移到“左”,打印“Move 1 from mid to left"。
3.如果希望从“中”移到“右”,打印“Move 1 from mid to right”。
4.如果希望从“右”移到“中”,打印“Move 1 from right to mid”。
5.如果希望从“左”移到“右”,打印“Move 1 from left to mid”和“Move 1 from midtoright”。
6.如果希望从“右”移到“左”,打印“Move 1 from right to mid”和“Move 1 from midtoleft”。
以上过程就是递归的终止条件,也就是只剩上层塔时的打印过程。

接下来,我们分析剩下多层塔的情况。
如果剩下N层塔,从最上到最下依次为1~N,则有如下判断:

  1. 如果剩下的N层塔都在“左”, 希望全部移到“中”, 则有三个步骤。1)将1~N-1层塔先全部从“左”移到“右”,明显交给递归过程。
    2)将第N层塔从“左”移到“中”。
    3)再将1~N-1层塔全部从“右”移到“中”,明显交给递归过程。
    2.如果把剩下的N层塔从“中”移到“左",从“中”移到“右"”,从“右”移到“中”,程与情况1同理,一样是分解为三步, 在此不再详述。
    3.如果剩下的N层塔都在“左",希望全部移到“右",则有五个步骤。
    1)将1~N-1层塔先全部从“左”移到“右", 明显交给递归过程。
    2)将第N层塔从“左”移到“中”。
    3)将1~N-1层塔全部从“右”移到“左”,明显交给递归过程。
    4)将第N层塔从“中”移到“右”。
    5)将1~N-1层塔全部从“左”移到“右”,明显交给递归过程。
    4.如果剩下的N层塔都在“右”,希望全部移到“左”,过程与情况3同理,一 样是分解为五步,在此不再详述。
    以上递归过程经过逻辑化简之后的代码请参看如下代码中的hanoiProblem1方法。
//#include "stdafx.h"
//#include <string>
//#include <iostream>
//using namespace std;

int process(int num,string left,string mid,string right,string from,string to){
	if (num==1)
	{
		if (mid==from||mid==to)
		{
			cout<<"Move 1 from "+from+" to "+to<<endl;
			return 0;
		}
		else{
			cout<<"Move 1 from "+from+" to "+mid<<endl;
			cout<<"Move 1 from "+mid+" to "+to<<endl;
			return 2;
		}
	}
	if (mid==from||mid==to)
	{
		//引入another使函数更简洁
		string another=(from==left||to==left)?right:left;
		int part1=process(num-1,left,mid,right,from,another);
		cout<<"Move "<<num<<" from "+from+" to "+to<<endl;
		int part2=1;
		int part3=process(num-1,left,mid,right,another,to);
		return part1+part2+part3;
	}
	else{
		string another=mid;
		int part1=process(num-1,left,mid,right,from,to);
		cout<<"Move "<<num<<" from "+from+" to "+mid<<endl;
		int part2=1;
		int part3=process(num-1,left,mid,right,to,from);
		cout<<"Move "<<num<<" from "+another+" to "+to<<endl;
		int part4=1;
		int part5=process(num-1,left,mid,right,from,to);
		return part1+part2+part3+part4+part5;
	}
}
int hanoiProblem1(int num,string left,string mid,string right){
	if (num<1)
	{
		return 0;
	}
	cout<<process(num,left,mid,right,left,right);
	return 0;
}
//int main(){
//	hanoiProblem1(3,"left","mid","right");//3^n-1
//	return 0;
//}

方法二:

////用栈来求解汉诺塔问题:修改后的汉诺塔问题不能让任何塔从“左”直接移动到“右”,也不能直接从“右”移动到“左”,而是要经过中间,也就是说,实际的动作只有4个:“左”到“中”、“中”到“左”、“中”到“右”、“右”到“中”。
////
////现在我们把左、中、右三个地点抽象成栈,依次记为LS、MS、RS。最初所有的塔都压在LS上。那么如上四个动作可以看作是:某一个栈(from)把栈顶元素弹出,然后压入另一个栈中(to),作为这一个栈(to)的栈顶。
////
////	例如,如果是7层塔 ,在最初,所有的塔都在LS上,LS从栈顶到栈底依次是1~7,如果现在发生了“左”到“右”的动作,这个动作对应的操作是LS栈将栈顶元素1弹出,然后压入MS栈中,其他元素同理。小压大。
////
////在修改后的汉诺塔游戏中,如果想走出最少步数,那么任何两个相邻的动作都不是互为逆过程的。
////两个原则:1.小压大原则 2.相邻不可逆原则
////有了这两个原则可以推导出两个十分有用的结论——非递归方法的核心结论:
////1.游戏的第一个动作一定是L->M,这是显而易见的
////2.在走出最小步的任何时刻,四个动作中只有一个动作不违反小压大和相邻不可逆原则,另外三个动作一定会违反。
////
////对于结论2,现在进行简单的证明:
////	因为游戏的第一个动作是L->M,,则以后每一步都会有前一步。
////	假设前一步是L->M:
////  1.根据小压大的原则,L->M的动作不会重复发生。
////	2.根据相邻不可逆原则M->L 的动作不该发生。
////	3.根据小压大原则M->R和R->M只有一个会达标。
////其余同理
#include "stdafx.h"
#include <string>
#include <iostream>
#include <stack>
using namespace std;
//枚举类型做常量用来协助判断相邻不可逆原则
enum Action{
	No,LToM,MToL,MToR,RToM
};
int process(Action &a,Action preact,Action nowact,stack<int> &pre,stack<int> &now,string from,string to){//一开始两个栈没传引用,改了之后还是死循环,最后发现a也得传引用,真是因为这个浪费的大把时间
	if (a!=preact&&pre.top()<now.top())
	{
		cout<<"Move "<<pre.top()<<" from "+from+" to "+to<<endl;
		now.push(pre.top());
		pre.pop();
		a=nowact;
		return 1;
	}
	else return 0;
}
int hanoiProblemWithNoRecursion(int num,string left,string mid,string right){
	int c=0;
	stack<int> LEFT;LEFT.push(INT_MAX);//先放个INT_MAX元素防止访问空的stack而报错
	for (int b=num;b>0;b--)
	{
		LEFT.push(b);
	}
	stack<int> MID;MID.push(INT_MAX);
	stack<int> RIGHT;RIGHT.push(INT_MAX);
	Action a=No;
	while ((int)RIGHT.size()<num+1)
	{
		c+=process(a,MToL,LToM,LEFT,MID,left,mid);

		c+=process(a,LToM,MToL,MID,LEFT,mid,left);

		c+=process(a,MToR,RToM,RIGHT,MID,right,mid);

		c+=process(a,RToM,MToR,MID,RIGHT,mid,right);
	}
	return c;
}
//int main(){
//	cout<<hanoiProblemWithNoRecursion(3,"left","mid","right");//3^n-1
//	return 0;
//}


猜你喜欢

转载自blog.csdn.net/qq_43241311/article/details/88675558