【数据结构篇03】栈和队列

目录

基本概念

队列

​​​​代码演示:

多说几句


 

基本概念

  • 栈:只能在一端进行插入(入栈)或删除(出栈)的操作的线性表,允许操作的一段称为栈(Top)。
    • 特点:先进后出(FILO)
    • 存储结构:顺序栈和链式栈
    • 数学性质:当n个元素以某种顺序进栈,并且可在任意时刻出栈时(满足FILO),所获得的元素排列的数目N恰好满足 N=\frac{1}{n+1}C_{2n}^{n}
  • 队列:它也是一种受限的线性表,其限制为仅允许在表的一端(队尾-Rear)进行插入(入队),在表的另一端(队头-Front)进行删除(出队)。
    • 特点:先进先出(FIFO)
    • 存储结构:顺序队和链队

  • 顺序栈:---,几个状态,两个操作,st表示一个顺序栈 
    • 栈空:st.top==-1(也有的书表示为st.top==0,具体情况视规定)
    • 栈满:设maxSize为最大 元素个数,st.top==maxSize-1(注意是st.top==-1和st.top==0为栈空条件时候,第一个元素存储的位置也有区别)
    • 非法状态:上溢(栈满时继续入栈)和下溢(栈空时继续出栈)
    • 两个操作:入栈:++(st.top);st.data[st.top]=x;   出栈:x=st.data[st.top];--(st.top);  注:对于独立的自增操作,++a总比a++效率要高,原因在于前置++不会产生临时对象,有兴趣的自行去了解。

  • Q:编写算法,判断表达式中的括号是否正确,表达式已经存入exp[]中,字符个数为n
    #include<iostream>
    using namespace std;
    
    /*---------------栈-------------------*/
    
    //用顺序栈来实现括号匹配
    #define maxSize 20
    void math(char exp[], int n) {
    	//为了简便直接定义,也可以自己定义一个结构体
    	char st[maxSize] = {0};
    	int top = -1;
    	
    	for (int i=0; i < n; ++i) {
    		if (exp[i] == '(') {
    			st[++top] == '(';
    		}
    		if (exp[i] == ')') {
    			if (top == -1) {
    				cout << exp << ":括号匹配失败!\n";
    				return;
    			}
    			else
    			{
    				--top;  //相当于遇到一个')'就将栈中的'('pop出来一个
    			}
    		}
    	}
    	//只有当恰好栈空的时候为匹配成功
    	if (top == -1) {
    		cout << exp << ":括号匹配成功!\n";
    		return;
    	}
    	else {
    		cout << exp << ":括号匹配失败!\n";
    		return;
    	}
    }
    
    int main() {
    	char exp[] = "(haha))(";
    	char exp1[] = "(haha)((";
    	char exp2[] = "(haha)()";
    	math(exp, 8);
    	math(exp1, 8);
    	math(exp2, 8);
    	return 0;
    }
    

  • 链栈:---,两个状态,两个操作,lst(它的next指向top)表示一个链栈
    • 栈空:lst->next=NULL;
    • 栈满:链式栈可以动态分配,只要在内存允许的范围内即可。
    • 两个操作:入栈:p->next=lst->nxet;lst->next=p;头插法建立链表中的插入操作 。   出栈:p=lst->next;x=p->data;lst->next=p->next;free(p); 就是单链表的删除操作。

实现链栈的基本操作,初始化,判断空,进栈,出栈

#include<iostream>
using namespace std;

//链栈
//定义结点
typedef struct LNode {
	int data;
	struct LNode* next;
}LNode;

//初始化栈,其实就是一个指向top元素的LNode指针
void initLStack(LNode*& lst) {
	lst = (LNode*)malloc(sizeof(LNode)); //和书二有些区别,因为lst书中默认认为已经分配了空间
	lst->next = NULL;
}
//判断栈是否为空
int isEmpty(LNode* lst) {
	if (lst->next == NULL) {
		return 1;
	}
	else
	{
		return 0;
	}
}
//入栈
void push(LNode*& lst,int x) {
	LNode* newNode = (LNode*)malloc(sizeof(LNode));
	newNode->data = x;
	newNode->next = lst->next;
	lst->next = newNode;
}
//出栈
int pop(LNode*& lst,int &x) {
	LNode* p;
	if (lst->next == NULL) {
		return 0; //栈空
	}
	else
	{
		p = lst->next;
		x = p->data;
		lst->next = p->next;
		free(p);
		return 1;
	}
}

//通过出栈操作打印链栈全部元素
void printLStack(LNode*& lst) {
	int flg = 1;
	cout << "依次pop为:";
	while (flg)
	{	
		int temp;
		flg=pop(lst,temp);
		if (flg == 1) {
			cout << temp << " ";
		}
	}
	cout << endl;
}

int main() {
	int temp = 0;
	cin >> temp;
	LNode* lst;
	initLStack(lst);
	while (temp!=-1) //-1为结束符
	{
		push(lst, temp);
		cin >> temp;
	}
	if (isEmpty(lst) == 0) {
		cout << "链栈非空!"<<endl;
	}
	else {
		cout << "链栈为空!"<<endl;
	}

	printLStack(lst);
	free(lst); //养成好习惯,记得释放空间
	return 0;
}

队列

  • 顺序队
    • 循环队列:为什么要有循环队列,我们首先要了解“假溢出”,定义两个指针rear,front,分别指向队尾,队首。maxSize为最大元素个数,元素进队的时候rear后移,元素出队的时候front也要后移,当经历一系列的操作后,两个指针最终都会到达数组末端maxSize-1,队中没有元素了,但是没办法让新元素进队,这就是“假溢出”。所以就产生了循环队列来解决这个问题。

    • 循环队列的要素
      • 两个状态:队空:qu.rear=qu.front,队满:(qu.rear+1)%maxSize=qu.front
      • 两个操作:
        • 进队:移动队尾指针 qu.rear=(qu.rear+1)%maxSize;qu.data[qu.rear]=x;
        • 出队:移动队首指针 qu.front=(qu.front+1)%maxSize;x=qu.data[qu.front];
  • 链队:采用单链表,lqu表示链队
    • 两个状态:链队空:lqu->rear==NULL或lqu->front==NULL; 队满:不存在队满的操作(内存允许),若想判断队的长度可以维护个length变量。
    • 两个操作:
      • 进队:lqu->raer->next=p;lqu->rear=p;
      • 出队:p=lqu->front;lqu->front=p->next;x=p->data;free(p);

​​​​代码演示:

 前面实现了栈的一些定义和操作,来实现一下链队的基本操作,例如链队的初始化,是否空,入队,出队操作,但是也注意,仅仅利用了一些思想并非真正的编程的时候(要封装)就是这样,简化代码。

#include<iostream>
using namespace std;

/*--------------队列------------------*/
//单链表结点
typedef struct LNode {
	int data;
	struct LNode* next;
}LNode;
//链队
typedef struct Lqu {
	LNode* rear;
	LNode* front;
	int length; //与书中写法不一样,我们来维护一个length
}Lqu;

//初始化链队
void initLqu(Lqu*& lqu) {
	lqu = (Lqu*)malloc(sizeof(Lqu));
	lqu->front = lqu->rear = NULL;
	lqu->length = 0;
}

//判断链队是否空
int isEmpty(Lqu* lqu) {
	if (lqu->length == 0) {
		return 1;
	}
	else
	{
		return 0;
	}
}

//入队
void enQueue(Lqu*& lqu,int x) {
	LNode* newNode = (LNode*)malloc(sizeof(LNode));
	newNode->data = x;
	newNode->next = NULL;
	//空队和非空队要做不同的处理
	if (lqu->length == 0) {
		lqu->front = lqu->rear = newNode;
	}
	else
	{
		lqu->rear->next = newNode;
		lqu->rear = newNode;
	}
	++(lqu->length);
}
//出队
int deQueue(Lqu*& lqu,int &x) {
	LNode* p = lqu->front;
	//队为空则没有元素出队
	if (lqu->length == 0) {
		return 0;
	}
	x = p->data;  //取出该值
	//只有一个结点的时候要特殊处理
	if (lqu->length == 1) {
		lqu->front = lqu->rear=NULL;
	}
	else {
		lqu->front = lqu->front->next;
	}
	--(lqu->length);
	free(p);//出队后删除释放以前的结点
	return 1;
}


//顺序打印队,通过出队操作
void printLqueue(Lqu*& lqu) {
	int flg = 1;
	cout << "依次出队为:";
	while (flg)
	{	
		int temp;
		flg = deQueue(lqu, temp);
		if (flg == 1) {
			cout << temp << " ";
		}
	}
	cout << endl;
}

int main() {
	int temp = 0;
	cin >> temp;
	Lqu *lqu;
	initLqu(lqu);
	while (temp!=-1) //-1为结束符
	{
		enQueue(lqu, temp);
		cin >> temp;
	}
	if (isEmpty(lqu) == 0) {
		cout << "链队非空!"<<endl;
	}
	else {
		cout << "链队为空!"<<endl;
	}
	
	printLqueue(lqu);
	free(lqu); //养成好习惯,记得释放空间
	return 0;
}

 

多说几句

ADT:Abstract Data type,抽象数据类型,可以看做一些数据对象以及附加在这些数据对象上的操作集合。

  • 数据对象集:存储在数据结构中的数据元素
  • 数据关系及:指数据对象的组织方式,如线性表的一对一,树的一对多以及图的多对多关系。
  • 操作集:例如栈的入栈出栈操作,判断是否为空等操作。

ADT注意事项:

  • 它的格式,类似于C语言结构体的写法,一般关系集会出现4中关系:没关系、顺序关系、树形关系和图关系。
  • 例子:例如图书馆 ADT
    ADT 书
    {
        数据对象集:
            书名;
            书号;
            作者;
    };
    
    ADT 书架
    {
        数据对象集:
            书架号;
            书={书0,书1,···,书n};
        数据关系集:
            书在书架上的排列方式={<书0,书1>,<书1,书2>,······};
    };
    
    ADT 图书馆
    {
        数据对象集:
            图书馆名;
            书架={书架0,书架1,······};
        数据关系集:
            书架在图书馆的排列方式={<书架0,书架1>,<书架1,书架2>,······};
        操作集:
            根据书架号查找书架;
            根据书名查找书;
            ·······
    }

     

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值