总结
栈
1.顺序栈
基本操作:
typedef int ElemType;
//定义:顺序栈//
typedef struct{
ElemType data[Maxsize];
int top;//栈顶指针
}SqStack;
//判空//
bool StackEmpty(SqStack S){
if(S.top == -1) return true;//大小都是负的,肯定是空的//
else return false;
}
//进栈//
bool Push(SqStack &S , ElemType x){
if(S.top == Maxsize - 1) return false;
S.data[++S.top] = x; return false;//这个地方是先执行了移动操作,后执行了赋值操作
}
//出栈操作
bool Pop(SqStack &S , ElemType &x){//这个地方的x也是引用操作
if(S.top == Maxsize - 1) return false;
x = S.data[S.top --];//先去赋值,然后执行移动指针的操作//
return true;
}
//读取栈顶元素
bool GetTop(SqStack S , ElemType &x){//x:引用操作
if(S.top == -1) return false;
x = S.data[S.top];
return true;
}
(易混问题)指针问题:
入栈:指针先移动,后赋值进来:先告诉东西往哪里放,再放东西。
出栈:先赋值出去,后移动指针:先取出来东西,再移动指针。
2.共享栈
头尾都是一个入栈的口,最后指针相邻则判满;指针移动和顺序栈的要求一致,用的少。
typedef int ElemType;
//定义:共享栈//
typedef struct{
ElemType data[Maxsize];
int top1;//栈顶指针1
int top2;//栈顶指针2
}SqStack;
//判满//
bool StackFull(SqStack S){
if(S.top1 + 1 == S.top2) return true;
else return false;
}
//进栈//
bool Push(SqStack &S , ElemType x , int stackNum){
if(S.top + 1== S.top2) return false;
if(stackNum == 1) S.data[++S.top1] = x;//执行了++操作,后执行了赋值操作
if(stackNum == 2) S.data[--S.top2] = x;//注意这个操作//
return true;
}
//出栈操作
bool Pop(SqStack &S , ElemType &x , int stackNum){//这个地方的x也是引用操作
if(S.top1 == -1 || S.top2 == Maxsize + 1) return false;
if(stackNum == 1) x = S.data[S.top1 --];//先去赋值,然后执行移动指针的操作//
if(stackNum == 2) x = S.data[S.top2 ++];
return true;
}
//读取栈顶元素
bool GetTop(SqStack S , ElemType &x , int stackNum){//x:引用操作
if(S.top1 == -1 || S.top2 == -1) return false;
if(stackNum == 1) x = S.data[S.top1];
if(stackNum == 2) x = S.data[S.top2];
return true;
}
3.链栈
防上溢。
typedef int ElemType;
//链栈结点的定义
typedef struct SNode{
ElemType data;//栈中存放的元素
struct SNode *next;//下一个结点
}SNode , *SLink;//链栈的结点
//链栈的定义
typedef struct LinkStack{
SLink top;//栈顶指针
int count;//栈的个数
}LinkStack;
//进栈
bool Push(LinkStack *S , ELemType x){
SLink p = (SLink)malloc(sizeof(SNode));//malloc要求返回字节数,并且强制转换为实际的指针类型
p->data = x;//如果是指针访问,用->;如果是对象访问,用.//
p->next = S->top;//next指向当前栈顶
S->top = p;//栈顶指针指向新的元素//
S->count ++;
return true;
}
//出栈操作
bool Pop(LinkStack *S , ElemType x){
if(S->top == -1) return false;
x = S->top->data;
SLink p = S->top; //设置一个指针指向栈顶元素//
S->top = S.top->next;//top指针向后移动//
free(p); //释放指针//
S->count --;
return true;
}
队列
1.基本队列
2.循环队列
typedef int ElemType;
//循环队列的定义
typedef struct{
ElemType data[Maxsize];
int front , rear;//队头指针,队尾指针//
}SqQueue;
//入队操作//
bool EnQueue(SqQueue &Q , ElemType x){
if((Q.rear + 1) % Maxsize == Q.front) return false;
Q.data[Q.rear] = x;
Q.rear = (Q.rear + 1) % Maxsize;//算出队尾的位置而不是移动//
return true;
}
//出队//
bool DeQueue(SqQueue &Q , ElemType &x){
if(Q.rear == Q.front) return false;//循环队列//
x = Q.data[Q.front];
Q.front = (Q.front + 1) % Maxsize;
return true;
}
(常考问题)判满问题:
1.在循环队列中,Q.front指向了第一个元素,Q.rear则指向了最后一个元素的后一位,一旦满了,势必导致指针溢出。同时又不能像栈一样再开辟空间,因为前面的资源没有用完啊(你有什么脸再让人家开新的空间╮(╯▽╰)╭)。为了充分利用好前面的空位,所以出现了循环队列。
2.循环队列的问题在于,如果Q.front == Q.rear话,居然会出现分别不清到底现在是满的的空的这种问题。(这个问题有点像时钟,给你个时钟,你说现在是14:00还是2:00。。分不清的)
所以就有了下面的这几种方法。
方法1: 牺牲一个空间滞空。
1.若Q.rear的下一个(即 Q.rear+1 )指针指向的是Q.front,则认为是满了。
2.空的话依旧是Q.front == Q.rear,即为刚开始的时候。
//队满
//这种方法可以“归0”,满了一个“轮回Maxsize”,Q.rear+1 就回到起点。
(Q.rear + 1) % Maxsize == Q.front
//队空
Q.front == Q.rear
//队中元素个数
(Q.rear - Q.front + Maxsize) % Maxsize
//删除
Status Dequeue(Sequeue &Q , QElemtype &e){
if(Q.front == Q.rear) return ERROR;
e = Q.base[Q.front]
Q.front = (Q.front + 1) % Maxsize; //所有的移动都需要这么移动,防止这个数字溢出
return OK;
}
方法二: 增加一个队满元素数目
//队满
Q.size == Maxsize
//队空
Q.size == 0
//队伍大小
Mazsize
方法三: 增加tag标记
//插入后队满:
if(tag == 0 && Q.front == Q.rear) return true;
//插入后队空:
if(tag == 1 && Q.front == Q.rear) return false;
3.链式队列
1.在删除模块的时候,需要保证链队列的Q.rear 指针不会丢失。
2.该队列带头节点。
typedef int ElemType;
//链式队列结点的定义
typedef struct{
ElemType data;
struct LinkNode *next;//这个也是一个结点指针//
}LinkNode;
//链式队列的定义
typedef struct{
LinkNode *front , *rear;
}LinkQueue;
//入队操作//
void EnQueue(LinkQueue &Q , ElemType x){
LinkNode *s = (LinkNode*)malloc(sizeof(LinkNode));
s->data = x;
s->next = NULL;
Q.rear->next = s;
Q.rear = s;
}
//出队:出队指的是头结点的后继结点出队//
bool DeQueue(LinkQueue &Q , ElemType &x){
if(Q.front == Q.rear) return false;
LinkNode *p = Q.front->next;
x = p->data;
Q.front->next = p->next;
if(Q.rear == p) Q.rear = Q.front;//单独只有一个数据的时候,删了p会把rear报错,往前放//
free(p);
return true;
}
4.双端队列
前开,后开,两边都开。
应用
1.栈的应用
括号匹配
#include<iostream>
#include<stack>
using namespace std;
bool Check(string str){
stack<char> s;
for(int i = 0 ; i < str.length() ; i ++){
switch(str[i]){
case '(':
case '[':{ s.push(str[i]); break; }
case ')':{
if(s.top() == '('){ s.pop(); break;}
else return false;
}
case ']':{
if(s.top() == '['){ s.pop(); break;}
else return false;
}
}
}
if(s.empty() == true){
printf("It is true!");
return true;
}
else{
printf("It is false!");
return false;
}
}
int main(){
string s = "(())";
int a = Check(s);
return 0;
}
后缀表达式 (稍后补上)
递归
阶乘运算
#include<iostream>
using namespace std;
int F(int a){
if(a == 0) return 1;
else return a * F(a - 1);
}
int main(){
int a = 4;
printf("%d" , F(a));
return 0;
}
斐波那契
#include<iostream>
using namespace std;
int Fib(int a){
if(a == 0) return 0;
else if(a == 1) return 1;
else return Fib(a - 1) * Fib(a - 2);
}
int main(){
int a = 4;
printf("%d" , Fib(a));
return 0;
}
2.队列应用
层序遍历: 参考树的操作一章节。
系统应用: 缓存区。 (堆栈问题???保存一下后面找到了补上。)