栈解题秘籍
- 在顺序栈中,栈顶指针指向的是栈顶元素的上一个位置,即空位置,取栈顶元素时要取*(S.top−1)才可以。入栈时,先把元素放入栈顶位置,然后栈顶指针后移,即*S.top=e;S.top++。出栈时,栈顶指针前移,用变量暂存栈顶元素,即e=−−S.top。
- 出栈只是栈顶指针移动,空间元素仍然存在,但下次入栈时会覆盖
- 本书以动态分配为例,静态分配的情况处理方式不同。静态分配是使用一个固定长度的数组存储数据,然后用一个int型的变量top指向栈顶,top实际上是数组的下标。当栈空时S.top=0。入栈时,先把元素放入栈顶位置,然后栈顶指针后移,即S.data[S.top++]=e。出栈时,栈顶指针前移,用变量暂存栈顶元素,即e=S.data[−−S.top]。
- 可以利用栈将递归程序转换为非递归。递归是利用栈实现的,因此可以利用栈将递归程序转换为非递归程序。例如,第6章二叉树的遍历,都可以用栈将递归遍历转换为非递归遍历。
队列解题秘籍
为了避免假溢出,顺序队列一般采用循环队列。循环队列需要注意4个问题。
- 队空:Q.front=Q.rear //Q.front和Q.rear指向同一个位置
- 队满:(Q.rear+1)%Maxsize==Q.front //Q.rear后移一位正好是Q.front
- Q.base[Q.rear]=e;//新元素插入队尾,Q.rear=(Q.rear+1)%Maxsize;//队尾指针加1
- 出队:e=Q.base[Q.front];//保存队头元素,Q.front=(Q.front+1)%Maxsize;//队头指针加1
- 元素个数:(Q.rear-Q.front+Maxsize)%Maxsize
数制的转换
题目:将一个十进制数转换为二进制数
思路:
十进制数转换为二进制,可以采用辗转相除、取余数的方法得到
先求出的余数是二进制数的低位,后求出的余数是二进制数的高位,将得到的余数逆序输出就是所要的二进制数,即11的二进制数为1011。如何将余数逆序输出呢?逆序输出正好符合栈的先入后出性质,因此可以借助栈来实现
复杂度:
#include <iostream>
using namespace std;
typedef int ElemType;
#define Maxsize 100 //预先分配空间,这个数值根据实际需要预估确定;
typedef struct SqStack{
ElemType *base;
ElemType *top;
}SqStack;
bool InitStack(SqStack &S){
S.base=new int [Maxsize];
if(!S.base)return false;
S.top=S.base;
return true;
}
bool EmptyStack(SqStack &S){
if(S.top==S.base)return true;
return false;
}
bool Push(SqStack &S,ElemType e){
if(S.top-S.base==Maxsize)return false;
*S.top=e;
S.top++;
return true;
}
bool Pop(SqStack &S,ElemType &e){
if(S.top==S.base)return false;
S.top--;
e=*S.top;
return true;
}
int GetTop(SqStack S){
if(S.top!=S.base)return *(S.top-1);
else return -1;
}
void binaryconversion(int num){
int e;
SqStack S;
InitStack(S);
while(num){
Push(S,num%2);
num=num/2;
}
while(!EmptyStack(S))//如果栈不空
{
Pop(S,e);//出栈
cout<<e<<"\t";//输出栈顶元素
}
}
int main()
{
int n;
cout<<"请输入一个大于0的十进制整数:"<<endl;
cin>>n;
binaryconversion(n);
return 0;
}
回文判定
题目:
回文是指正读反读均相同的字符序列,如“abba”和“abcscba”均是回文,也就是说字符串沿中心线对称,但“foot”和“bed”不是回文。试写一个算法判定给定的字符串是否为回文。
解题思路:
回文是中心对称的,可以将字符串前一半入栈,然后,栈中元素和字符串后一半进行比较。即将第一个出栈元素和后一半串中第一个字符比较,若相等,则再将出栈一个元素与后一个字符比较……直到栈空为止,则字符序列是回文。在出栈元素与串中字符比较不等时,则字符序列不是回文。
复杂度:
如果字符串长度为n,将前一半入栈,后一半依次和出栈元素比较,相当于扫描了整个字符串,因此时间复杂度为O(n),使用的栈空间大小是n/2,空间复杂度也为O(n)
#include<iostream>
#include<cstring>
typedef char Elemtype;//先类型定义为char
#include"sqstack.h"//引入自定义头文件,源码目录下名为sqstack.h的文件
using namespace std;
bool palindrome(char *str)//判断字符串是否为回文
{
SqStack S;//定义一个栈S
int len,i;
char e;
len=strlen(str);//返回字符串长度
InitStack(S);//初始化栈
for(i=0;i<len/2;i++)//将字符串前一半依次入栈
Push(S,str[i]);
if(len%2==1)//字符串长度为奇数,跳过中心点
i++;
while(!Empty(S))//如果栈不空
{
Pop(S,e);//出栈
if(e!=str[i])//比较元素是否相等
return false;
else
i++;
}
return true;
}
int main()
{
char str[20];
cout<<"请输入一个长度小于20的字符串:"<<endl;
cin>>str;
if(palindrome(str))
cout<<"该字符串是回文!"<<endl;
else
cout<<"该字符串不是回文!"<<endl;
return 0;
}
双端队列
题目:设计一个数据结构,使其具有栈和队列两种特性。
解题思路:
栈是后进先出,队列是先进先出,如何具有这两种特性呢?
允许两端都可以进行入队和出队的队列,就是双端队列
结构体定义
顺序存储,静态分配空间
注意:在顺序存储中,静态分配空间采用的是一维定长数组存储数据,动态分配空间是在程序运行中使用new动态分配空间
1)前端进队时,先令Q.front前移一位,再将元素放入Q.front的位置
2)后端进队时,先将元素放入Q.rear的位置,再令Q.rear后移一位
3)从后端出队,先令Q.rear前移一位,再将Q.rear位置元素取出
4)从前端出队,先将Q.front位置元素取出,再令Q.front后移一位
特点
1)后端进、前端出或者前端进、后端出体现了先进先出的特点,符合队列的特性。
2)后端进、后端出或者前端进、前端出体现了后进先出的特点,符合栈的特性。
循环队列实现的双端队列,具有栈和队列两种性质。
基本操作
1)初始化时,头指针和尾指针置为零,双端队列为空
#include <iostream>
using namespace std;
#define Maxsize 100
typedef char ElemType;
typedef struct SqQueue{
ElemType base[Maxsize];
int front,rear;
}DuQueue;
void InitQueue(DuQueue &Q){
Q.front=Q.rear=0;//队头和队尾置为0,队列为空
}
bool isFull(DuQueue Q){
if((Q.rear+1)%Maxsize==Q.front)return true;
else return false;
}
bool isEmpty(DuQueue Q){
if(Q.front==Q.rear)
return true;
else
return false;
}
bool push_back(DuQueue &Q,ElemType e){//尾进
if(isFull(Q))return false;
Q.base[Q.rear]=e;//先放入尾部
Q.rear=(Q.rear+1)%Maxsize;//向后移动一位
return true;
}
bool pop_back(DuQueue &Q,ElemType &x){//尾出
if(isEmpty(Q))return false;
Q.rear=(Q.rear-1+Maxsize)%Maxsize;//防止为负,比如rear=0时
x=Q.base[Q.rear];
return true;
}
bool push_front(DuQueue &Q,ElemType e){//头进
if(isFull(Q))return false;
Q.front=(Q.front-1+Maxsize)%Maxsize; //先向前移动一位
Q.base[Q.front]=e; //后放入
return true;
}
bool pop_front(DuQueue &Q,ElemType &e){//头出
if(isEmpty(Q))return false;
e=Q.base[Q.front];
Q.front=(Q.front+1)%Maxsize;
return true;
}
bool get_front(DuQueue Q,ElemType &x){//取队头数据
if(isEmpty(Q))return false;
x=Q.base[Q.front];
return true;
}
bool get_back(DuQueue Q,ElemType &x){//取队尾数据
if(isEmpty(Q))return false;
x=Q.base[(Q.rear-1+Maxsize)%Maxsize];
return true;
}
int length(DuQueue Q){
return (Q.rear-Q.front+Maxsize)%Maxsize;
}
void traverse(DuQueue Q){
int temp=Q.front;
if(isEmpty(Q)) cout<<"队列为空";
while(temp!=Q.rear){
cout<<Q.base[temp]<<" ";
temp=(temp+1)%Maxsize;
}
cout <<"已遍历完";
}
int main()
{
DuQueue DuQ;
ElemType e,x;
cout<<"1. 初始化\n";
cout<<"2. 头进\n";
cout<<"3. 头出\n";
cout<<"4. 尾进\n";
cout<<"5. 尾出\n";
cout<<"6. 取队头\n";
cout<<"7. 取队尾\n";
cout<<"8. 求长度\n";
cout<<"9. 遍历队列\n";
cout<<"0. 退出\n";
int choose=-1;
while (choose!=0)
{
cout<<"请选择:";
cin>>choose;
switch(choose)
{
case 1://初始化
cout << "双端队列初始化..." << endl;
InitQueue(DuQ);
break;
case 2://头进
cout << "从前端进队(头进)..." << endl;
cout << "请输入一个字符:" << endl;
cin>>e;
push_front(DuQ,e);
break;
case 3://头出
cout <<"从前端出队(头出)..."<< endl;
pop_front(DuQ,x);
cout << "出队元素为" <<x<< endl;
break;
case 4://尾进
cout << "从后端进队(尾进)..." << endl;
cout << "请输入一个字符:" << endl;
cin>>e;
push_back(DuQ,e);
break;
case 5://尾出
cout <<"从后端出队(尾出)..."<< endl;
pop_back(DuQ,x);
cout << "出队元素为" <<x<< endl;
break;
case 6://取队头元素
cout <<"取队头元素..."<< endl;
get_front(DuQ,x);
cout << "队头元素为" <<x<< endl;
break;
case 7://取队尾元素
cout <<"取队尾元素..."<< endl;
get_back(DuQ,x);
cout << "队尾元素为" <<x<< endl;
break;
case 8://求队长
cout << "双端队列长度:"<<length(DuQ)<<endl;
break;
case 9://遍历
traverse(DuQ);
break;
}
}
return 0;
}