数据结构之栈和队列

数据结构之栈、堆、队列

1.栈(stack)[重点]

1.1基本知识

特性:

  • 只能一端即使用top指针插入和删除操作的线性表(可以使用顺序表或者链表实现)
  • 栈顶指针绑定指向最先插入的元素
  • 栈的方向一般是从上往下,即地址从高到低
  • 具有先进后出的特点,能够保存特定生命周期的元素

1.2栈的操作

顺序栈:

const int MAX=100;
int data[MAX];
int top;

//栈空及栈满
	//1.规定top==0时栈空,则top==MAX-1为栈满,缺点在于浪费了top=0时的一个元素空间
					//推荐使用第1种,能够像链式栈有头结点,作为一个栈存在的标识

	//2.规定top==-1为栈空,则top==MAX-1为栈满,即top=0也可以使用
//入栈和出栈
	//入栈时首先应该判断是否栈满,防止上溢出
		data[++top]=e;//top指针先下移一位,再入栈
	//出栈时首先判断是否栈空,防止下溢出
		e=data[top--];//先将元素赋给接受变量,top指针再上移一位

链式栈:

typedef struct LNode{
    int data;
    struct LNode *next;
}LNode;//与带头结点的单链表相差无异
LNode *C;

//栈空及栈满
	//栈空:即单链表头结点next指针为NULL
		   C->next==NULL;
	//栈满,不存在的,内存有多大,我就有多大!

//入栈及出栈:
		//入栈:必须使用头插法,保证头指针指向最新入栈的元素
				p->next=C->next;C->next=p;
		//出栈:就是单链表的删除操作
				LNode *s;
				s=C->next;
				C->next=s->next;
				data=s->data;//接受数据
				delete s;

1.3 栈的简单应用实例

1.3.1判断算术表达式中括号是否匹配的算法
//实现一个判断算术表达式中括号是否匹配的算法,返回一个bool值,表达式已存入字符数组exp[]中,字符个数n

//算法分析:1.使用一个顺序栈s,将exp中字符取出,判断是否为‘(’,则入栈,为‘)’,则消除栈中‘(’,成功则继续循环操作,否则匹配失败!!

//算法实现:
bool match(const char exp[],int n){
	char s[n];
    int top=0;
    
    int i=0;
    while(i<n){
        if(exp[i]=='(')
            s[++top]=exp[i];
        if(exp[i]==')'){
            if(top==0)
                return false;
            else
                --top;
        }
        i++;
    }
    if(top==0)
        return true;
    else 
        return false;
}
1.3.2算术表达式的前缀、中缀、后缀计算及转换

中缀表达式:

​ 指人类识别的算术表达式,如3*2+(1/2-2.3)/2

前缀表达式:

​ 从右往左扫描表达式,遇到数字,压入栈中,遇到运算符,弹出两个数字计算其值,将结果压入栈中,最后得到结果

后缀表达式:

​ 与前缀扫描方向相反

中缀转换为前缀,将前缀计算结果:

//算法分析:1.首先编写将中缀转换为前缀函数transfer(),返回一个前缀表达式字符数组
		//2.编写将前缀表达式计算结果的函数calcu(),返回计算结果

//算法缺点:只能转化0~9的算术表达式,只支持加减乘除运算!!	

//测试样例:1+((2+3)*4)-5

//算法实现:
	//1.实现transfer()
int priOutStack(char c){
    int pri;
    switch(c){
        case ')':pri=8;break;
        case '+':pri=3;break;
        case '-':pri=3;break;
        case '*':pri=6;break;
        case '/':pri=6;break;
    }
    return pri;
}
int priInStack(char c){
    int pri;
    switch(c){
        case ')':pri=0;break;
        case '+':pri=3;break;
        case '-':pri=3;break;
        case '*':pri=6;break;
        case '/':pri=6;break;
    }
    return pri;
}
const char *transfer(const char infix[],int n){
    if(!match(infix,n))
        throw "error!";
    int i=n-2;//除去字符数组的结束字符'\0'
    
 	 static char prefix[50];
    //这里我犯了一个低级错误:返回了一个局部变量的地址,编译器会直接报错,
    			//解决办法:改为static变量;;;或者直接传入一个全局变量的prefix指针;;
    			//;;;;或者可以改为使用new动态分配内存
    			//可以返回一个局部变量的值,但是不能返回一个局部变量的地址,由于局部变量存于栈区
    			//返回一个地址不会消失,但是其指向的栈区有可能就不是自己原来的内容了!!!
    int j=0;
    
    char opStack[30];
    int top=0;
    
    while(i>=0){
        if(isdigit(infix[i])){
            prefix[j]=infix[i];
        	j++;i--;
        }else{
            if(infix[i]=='('){
                while(opStack[top]!=')'){
                    prefix[j++]=opStack[top--];
                }
                i--;top--;
            }else{
                while(priOutStack(infix[i])<priInStack(opStack[top])){
                    prefix[j++]=opStack[top--];
                }
                opStack[++top]=infix[i--];
            }            
        }	
    }
    while(top!=0){
        prefix[j++]=opStack[top--];
    }
    return prefix;
}

	//2.实现calcu()
int OP(int a,char op,int b){
    switch(op){
        case '+':return a+b;break;
        case '-':return a-b;break;
        case '*':return a*b;break;
        case '/':
            if(b==0) throw "error";
            else return a/b;
            break;
    }
}
int calcu(const char prefix[],int n){
	int calcu[n];
    int top=0;
    
    int i=0;
    char op;
    int a,b,c;
    while(prefix[i]!='\0'){
		if(prefix[i]>='0'&&prefix[i]<='9')
            calcu[++top]=prefix[i++]-'0';
        else{
            op=prefix[i++];
            a=calcu[top--];//注意顺序,这里是第1个数
            b=calcu[top--];//这是第2个数
            
            c=OP(a,op,b);
            calcu[++top]=c;            
        }
    }
    return calcu[top];
}

int main(){

	char exp[14]="1+((2+3)*4)-5";

	if(	match(exp,14))
		cout<<"匹配成功!\n";
	else
		cout<<"匹配失败!\n";
	cout<<exp<<"\n";
    
    try{
        const char *p=transfer(exp,14);//返回一个常量指针,必须创建一个常量指针进行接收
        cout<<p<<"\n";
        int res=calcu(p,14);
        cout<<res<<"\n";
    }catch(const char *e){
        cout<<e;
    }
	
}
/*
	输出如下:
		匹配成功!
        1+((2+3)*4)-5
        5432+*1+-
        16
*/

2.队列[重点]

2.1基本知识

特性:

  • 只能在队尾的rear指针一端插入,队顶的front另一端删除,特别适用于串行进行的任务设计

  • 具有先进先出的特点

  • 可以使用顺序表或者链表实现

  • 队顶指针绑定在第一个元素上,队列从rear插入从上往下移动,在front删除也是从上往下

2.2队列的操作

顺序队列:

const int MAX=100;
int data[MAX];
int front;
int rear;
	//初始化为指向0,作为顺序队列存在的标志,插入元素先下移一位
		front=0;
		rear=0;
	//队空和队满状态
		front==rear;//队空
		front==MAX-1 || rear==MAX-1;//队满
	//出队与进队
		data[++rear]=e;//进队
		e=data[++front];//出队,不是减减了,都是先移动指针,在插入或者取出元素
	//缺点,由于两个指针都是从上往下移动,当同时指向MAX-1,会出现假溢出的状态!!

//改进:习惯使用一个循环队列
    //初始化:
    front=rear=0;
    //队空及队满
    front==rear//队空
   (rear+1)%MAX==front||(front+1)%MAX==rear//队满,(rear+1)%MAX表示循环将rear指针下移一位
    //进队和出队
    data[(rear+1)%MAX]=e;//进队
    e=data[(front+1)%MAX];//出队,都是先移动指针,在插入和取出元素

链式队列:

typedef struct LNode{
    int data;
    struct LNode *next;
}LNode;
typedef struct LQueue{
    LNode *front;
    LNode *rear;
}LQueue;
LQueue *C;//创建指向队列链表的头结点指针,但是并没有创建一个LQueue节点内存,必须new一个节点才行
LQueue *C=new LQueue;
	//初始化
		C->front=C->rear=NULL;//这个时候才能初始化成功!!
	//队空及队满
		C->front==NULL ||C->rear==NULL//队空,队满,不存在的,内存有多大,我就有多大!
    //进队与出队
      //进队
        C->rear->next=p;//将rear指向的队尾节点元素的next指向新元素p
		C->rear=p;//将rear指针指向新链接的队尾元素p
	  //出队
		LNode *s;
		s=C->front;//创建一个节点指针,指向front指向的节点
		C->front=C->front->next;
		e=s->data;//接收即将释放的节点数据
		delete s;
//注意点:第一个节点入队时,还需要初始化front的指向,第一个节点进队只需要一句C->rear=p;
	//最后一个节点出队时,还需要初始化rear的指向为NULL 

//简单的实例操作
int main(){	
		LQueue *C=new LQueue;//创建指针并分配一个LQueue内存,才能初始化成功!!
		//初始化
		C->front=C->rear=NULL;
     	//进队,两个元素
     	LNode *p1,*p2;
     	p1->next=p2->next=NULL;
     	p1->data=100;
     	p2->data=300;
    
		C->rear=p1;//第一个节点元素操作,将rear指针指向新链接的队尾元素p
		C->front=p1; 
		
		C->rear->next=p2;//第二个节点元素操作将rear指向的队尾节点元素的next指向新元素p
		C->rear=p2;//将rear指针指向新链接的队尾元素p
    
		cout<<C->front->data<<"\t"<<C->front->next->data<<"\n";
    
	    //出队
		LNode *s1;
		s1=C->front;//创建一个节点指针,指向front指向的节点
		C->front=C->front->next;
		int e1=s1->data;//接收即将释放的节点数据
		cout<<e1<<"\n";
		delete s1;
		
		LNode *s2;//最后一个元素出队
    	s2=C->front;
    	C->front=C->rear=NULL;
    	int e2=s2->data;
   		cout<<e2;
    	delete s2;
    
		delete[] C;
	
}
2.3 队列的简单应用实例

++

3 堆heap[重点]

3.1基本知识

特性:

  • 堆只是程序运行时动态申请的一块内存,并使用一个指针指向这一块内存
  • 如何使用堆,通过栈存储的指向堆内存的指针进行快速访问,也就是说,堆只是一块内存,需要借助指针进行访问,不具有先进先出的特点!

4. 栈和队列的综合应用实例

4.1实现一个共享栈

++


4.2使用两个栈实现一个队列

++


4.3使用顺序表实现一个双端队列

++


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值