【数据结构】栈与队列 - 笔记&习题
其实是老师布置的作业。稍微写了些注释,然后直接把代码扔上来,希望能帮到有需要的同学。
拒绝抄作业,写那么多注释就是让你来读懂代码的。
栈
顺序栈 - 使用C++类封装
// 使用C++类 实现栈的数据结构
template <typename T>
class Stack
{
private:
T *dat; // 用于访问存储空间的指针
int ptr; // 存储栈顶元素下标的变量,相当于指向栈顶的指针
const int MAX_SIZE; // 栈的最大容量
public:
Stack(int size) : MAX_SIZE(size)
{
// 构造函数 在实例化对象时决定栈的大小并申请内存
// ptr初值为-1,表示栈为空
dat = new T[MAX_SIZE];
ptr = -1;
}
bool empty() // 栈是否为空
{
return ptr == -1 ? 1 : 0;
}
int count() // 栈中元素计数
{
return ptr+1;
}
int push(T now) // now元素入栈
{
if (ptr + 1 == MAX_SIZE)
return -1;
dat[++ptr] = now;
return 0;
}
int pop() // 删除栈顶元素
{
if (ptr == -1)
return -1;
ptr--;
return 0;
}
T top() // 返回栈顶元素
{
return dat[ptr];
}
~Stack()
{
delete[] dat;
}
};
以下题目中对于类的声明部分均省略。
进制转换问题
【问题描述】根据课堂讲授,请用“顺序栈”解决进制转换问题,不采用顺序栈,不给分。
【输入形式】十进制数据和待转换的进制
【输出形式】转换后的数据
【样例输入1】1348 8
【样例输出1】2504
【样例输入2】2608 16
【样例输出2】A30
// 使用顺序栈解决进制转换问题
void convert(Stack<int> &s, int dat, int t) // 进制转换 t表示t进制
{
while (dat) // 当dat非0重复操作
{
s.push(dat % t); //把余数放入栈中
dat /= t;
}
return;
}
char dict[] = "0123456789ABCDEF"; // 字典,把栈中存储的元素转换为对应的字符(主要用于16进制)
int main()
{
int dat, t;
// 定义一个长度为32的栈
Stack<int> res(32);
scanf("%d%d", &dat, &t);
convert(res, dat, t);
//输出栈中元素
while (!res.empty())
{
printf("%c", dict[res.top()]);
res.pop();
}
putchar('\n');
return 0;
}
字符串镜像
【问题描述】试写一个算法,识别依次读入的一个以“@”为结束符的字符序列是否为形如 “序列1&序列2” 模式的字符序列。其中序列1和序列2都不含字符 “&”,且序列2是序列1的逆序列。例如,“ a+b&b+a ”是属该模式的字符序列,而 “1+3&3-1”则不是。
【输入形式】 以@为结尾的一串字符
【输出形式】若符合模式则输出字符串长度,否则输出no
【样例输入】a+b&b+a@
【样例输出】3
int main()
{
Stack<char> s(1024); // 亲测有长度为2000的字符串数据。开一半就够了
int cnt=0,flag=1; //cnt计数,flag标识是否为镜像串
char now;
// 前半部分 边读入边入栈
do
{
now=getchar();
s.push(now);
} while (now!='&');
s.pop(); // 把栈顶的'&'删掉
cnt=s.count();
// 后半部分 边读入边比较
do
{
now=getchar();
if(now!=s.top() && now!='@')
flag=0;
s.pop();
} while (now!='@');
if(flag)
printf("%d\n",cnt);
else
printf("no\n");
return 0;
}
表达式求值
很重要的算法,一定要掌握!
【问题描述】栈的应用,给定一个以“#”作为结束符的算式,求出算式的结果
【输入形式】以“#”结尾的表达式,运算数为正整数。每个表达式占一行。
【输出形式】输出表达式运算的结果。
【样例输入1】4+2.53*3-10/5#
【样例输出1】9.59
【样例输入2】3*(7.91-2)#
【样例输出2】17.73
// 全部函数采用指针now来读取字符串,并且指针now为引用传参,便于标记读到哪一位了。
// 从当前位置开始,读入字符,将其转化为浮点数并存入到引用变量ans中。(指针now移到这个数后一位(应当是符号))
// 返回值表示是否有读到一个数字。0为没有读入。
inline bool readnum(char *&now,float &ans)
{
if(*now<'0' || *now>'9') return 0;
do
{
ans *= 10;
ans += *now-'0';
now++;
} while (*now >= '0' && *now <= '9'); // 转换整数部分
if (*now == '.') // 如果有小数部分,则转换
{
now++; //跳过小数点
float flag=0.1;
do
{
ans += (*now-'0')*flag;
flag *= 0.1;
now++;
} while (*now>='0'&&*now<='9'); // 转换小数部分
}
return 1;
}
// 运算优先级比较函数,只考虑了四则运算
inline bool cmp(char a,char b)
{
int a1 = (a=='*' || a=='/')?1:0;
int b1 = (b=='*' || b=='/')?1:0;
return a1<=b1;
}
// 运算函数,直接从栈中取出运算符与操作数
// 因为该函数调用前,算法可以保证栈里面有元素,没有判断栈是否为空。
// 同样通过引用传参的方式使函数内可以访问栈中元素
inline float calc(Stack<float> &ovs,Stack<char> &optr)
{
float b=ovs.top(); ovs.pop();
float a=ovs.top(); ovs.pop();
char opt=optr.top();optr.pop();
switch (opt)
{
case '+':
return a+b;
case '-':
return a-b;
case '*':
return a*b;
case '/':
return a/b;
default:
return -1;
}
}
float deal(char* &now)
{
// 分别为运算数栈与运算符栈
Stack<float> ovs(64);
Stack<char> optr(64);
while(1)
{
if (*now == '(') // 若读到括号,则进入递归,单独处理括号中的内容。
{
now++;
ovs.push(deal(now)); // 处理完后返回,压入运算数栈。
}
float num = 0; // 若接下来是一个数,就放进这个变量里,然后再进栈
if (readnum(now, num))
ovs.push(num);
else // 否则说明读到了一个运算符,开始处理表达式
{
// 运算符栈不为空,这时候需要比较当前运算符和栈中运算符优先级
// 栈里面运算符优先级高,就先把它算掉再管当前的运算符
// 否则先搁置当前运算符,放入运算符栈里面,下一次遇到运算符的时候再管。(以防后面有优先级更高的运算符)
while (!optr.empty())
{
if (cmp(*now, optr.top()))
ovs.push(calc(ovs, optr));
else
{
optr.push(*(now++)); // 这里入栈之后就now++了,表示这个符号处理完了
break;
}
}
if (*now == '#' || *now == ')') // 读到这两个说明这一段表达式结束了,退出外层循环。
break;
// 如果当前还是个符号,只有可能是因为opt栈是空的,没进入while循环处理掉
// 说明它是第一个进栈的运算符,先留着
if ((*now < '0' || *now > '9'))
optr.push(*(now++));
}
}
// 最后ovs栈里面只可能剩下一个元素,就是运算结果。
return ovs.top();
}
int main()
{
char raw[128],*p=raw;
cin.getline(raw,127);
float ans=deal(p);
printf("%.2f\n",ans);
return 0;
}
队列
链式队列 - 使用C++类封装
template <typename T>
class LinkQueue // 链式队列 - 在链表基础上实现
{
private:
struct Node
{
T dat;
Node* next;
}*head,*rear;
// head指向队列的前端(不是第一个有效元素)
// rear指向队列最后一个元素
public:
LinkQueue()
{
head = new Node; // 创建队列,建立头结点
rear = head;
rear->next = NULL;
}
bool empty()
{
return head==rear; // 头尾指针重合表示队列为空
}
void push(T dat) // 在队列最后添加元素
{
rear->next = new Node; // 新建节点
rear = rear->next; // 移动指针
rear->dat = dat; // 修改节点数据
rear->next = NULL;
}
// 删除与查找操作没有保证操作合法性,使用前需判断队列是否为空
T front() // 返回队列最前端元素
{
return head->next->dat;
}
void pop() // 删除队列最前端元素
{
Node *tmp = head->next; // 取出待删除元素
head->next = tmp->next; // 在链表中移去该元素
if(tmp == rear) // 如果是最后一个元素,就把尾指针归位
rear = head;
delete tmp; // 释放内存,完全删除
}
~LinkQueue()
{
while(!empty())
pop();
delete head;
}
};
循环队列 - 使用C++类封装
template <typename T>
class CycleQueue
{
private:
int head,tail;
// 顺序结构可以直接使用整数下标
// head指向第一个元素,tail指向最后一个元素的后一位
T* dat;
const int MAX_SIZE;
public:
CycleQueue(int size):MAX_SIZE(size) // 在创建对象时决定队列大小
{
dat = new T[MAX_SIZE];
head=0,tail=0;
}
inline bool empty() // 判断队列是否为空
{
return head == tail;
}
inline bool full() // 判断队列是否为满
{
return (tail+1)%MAX_SIZE == head;
}
inline T front() // 取出最前面的元素
{
if(!empty())
return dat[head];
return 0;
}
int push(T now) // 在末尾放入元素
{
if(full())
return -1;
dat[tail++] = now;
return 0;
}
void pop()
{
if(!empty())
head++;
return;
}
~CycleQueue()
{
delete[] dat;
}
};
队列元素逆置
【问题描述】已知Q是一个非空队列,S是一个空栈。仅使用少量工作变量以及对队列和栈的基本操作,编写一个算法,将队列Q中的所有元素逆置。需采用链式队列与栈(顺序或链式),否则不能得分。
【输入形式】输入的第一行为队列元素个数,第二行为队列从首至尾的元素
【输出形式】输出队列的逆置
【样例输入】
3
1 2 3
【样例输出】3 2 1
// 代码很简单 懒得写注释了
int main()
{
LinkQueue<int> q;
Stack<int> s(1024);
int n,tmp;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&tmp);
q.push(tmp);
}
while (!q.empty())
{
s.push(q.front());
q.pop();
}
while(!s.empty())
{
printf("%d ",s.top());
s.pop();
}
putchar('\n');
return 0;
}
杨辉三角
【问题描述】杨辉三角形的打印,请用循环队列实现。不采用“循环队列”,不给分。
void YangHui(int line)
{
CycleQueue<int> que(1024);
que.push(1);
printf("1\n");
for(int i=2;i<=line;i++,putchar('\n'))
{
que.push(1);
for(int j=2;j<=i;j++)
{
printf("%d ",que.front());
int now = que.front();
que.pop();
now+= que.front();
que.push(now);
}
printf("1 ");
}
while (que.empty())
{
printf("%d ",que.front());
que.pop();
}
putchar('\n');
return;
}
int main()
{
int n;
scanf("%d",&n);
YangHui(n);
return 0;
}