数据结构:队列 和 栈 的详解
Posted 正义的伙伴啊
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构:队列 和 栈 的详解相关的知识,希望对你有一定的参考价值。
栈
栈的结构和概念
栈 :一种特殊的线性表,其中只允许在固定的一端进行插入和删除元素。进行数据插入和删除操作的一段称为栈顶,另一端称为栈底 。栈中的元素遵循先进后出的原则。
压栈:栈的插入操作叫做 进栈/入栈/压栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈,出数据也在栈顶。
栈的实现
栈的实现一般可以使用数组或者链表,但是数组的结构要更优一点。因为数组在尾上插入数据的代价比较小。
其实两种数据结构都可以实现链表,但是为什么选择数组呢
因为由于栈的特性,使得如果使用链表,那么栈顶也就是出入元素的位置在链表的末尾,每次入栈、出栈都要从头节点遍历整个链表。
栈的实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int Datatype;
typedef struct Stack
{
Datatype* a;
int top;
int Capacity;
}Stack;
void StackInit(Stack* p); //初始化栈
void StackPush(Stack* p, Datatype x); //入栈操作
void StackPop(Stack* p); //出栈操作
Datatype StackTop(Stack* p); //返回栈顶元素的值
int StackEmpty(Stack* p); //检查栈是否为空
void StackDestroy(Stack* p); //销毁栈
void StackPrint(Stack* p); //打印栈的元素
以上时栈的结构和具体的基本操作
下面是这些具体操作的实现
#define _CRT_SECURE_NO_WARNINGS 1
#include"stack.h"
void StackInit(Stack* p)
{
assert(p);
p->a = (Datatype *)malloc(4*sizeof(Datatype));
p->top = 0;
p->Capacity = 4;
}
void StackPush(Stack* p, Datatype x)
{
assert(p);
if (p->top == p->Capacity) //检查是否要增容
{
p->a = (Datatype *)realloc(p->a,2 * p->Capacity*sizeof(Datatype));
p->Capacity = p->Capacity * 2;
}
p->a[p->top] = x;
p->top++;
}
void StackPop(Stack* p)
{
assert(p);
if(p->top)
p->top = p -> top - 1;
}
void StackPrint(Stack* p)
{
assert(p);
for (int i = 0; i < p->top; i++)
{
printf(" %d ", p->a[i]);
}
printf("\\n");
}
Datatype StackTop(Stack* p)
{
return p->a[p->top - 1];
}
int StackEmpty(Stack* p)
{
return p->top == 0;
}
void StackDestroy(Stack* p)
{
free(p->a);
p->a = NULL;
p->top = 0;
p->Capacity = 0;
}
栈的一些应用
括号匹配问题
//上面要引用栈的实现和相应实现的函数,为了能更清晰的看判断函数,我就不在前面复制了
bool isValid(char * s){
Stack ps;
StackInit(&ps);
while(*s)
{
if(*s=='(' || *s=='{' || *s=='[')
{
StackPush(&ps,*s);
s++;
}
else
{
if(StackEmpty(&ps))
return false;
char top=StackTop(&ps);
StackPop(&ps);
if((top=='[' && *s!=']')||(top=='{' && *s!='}')||(top=='(' && *s!=')'))
return false;
s++;
}
}
if(StackEmpty(&ps))
return true;
return false;
}
这题的思路是:
- 循环遍历字符串s,如果*s是 ‘(’ 、’{’、 ‘[’ 中的任意一个,就将该字符入栈
- 如果*s不是以上字符也就是’)’ 、’}’、 ‘]’ ,那就那他和栈顶的元素进行匹配,如果匹配了就将栈顶的元素出栈,不匹配就直接return false结束判断。循环完成返回return true
但是还是要注意一些特殊情况:
- 当字符串为 “(” 时,这时就要在末尾的return true前面判断栈是否为空,如果栈不为空,说明栈里面还有未匹配的字符,所以要返回false
- 当字符串为")"时,我们要在第二部也就时取栈顶元素之前判断栈是否为空,如果为空的话,就说明栈里面没有与右括号所匹配的左括号,返回false
用栈实现队列
leetcode-用栈实现队列
用队列实现栈主要的难点是在 出栈 和 返回栈顶元素 两个操作上
但是两个函数思路是一摸一样的
以 出栈函数 myQueuePop函数为例
这里为了实现队列,我们要创建两个栈来相互倒来将队首元素拿出,具体操作如下:
-
首先将s1栈的栈顶元素入到s2栈中,然后将栈顶元素出栈。重复上述操作直到s1栈为空。
-
这时整个栈的元素相当于逆置了,队首元素现在在栈顶的位置,下面的操作在 myQueuePop和myQueuePeek操作略有不同
- myQueuePop 要把s2栈顶的值出栈 - myQueuePeek 不用把s2的栈顶的值
-
最后首先将s2栈的栈顶元素入到s1栈中,然后将栈顶元素出栈。重复上述操作直到s2栈为空
代码:
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int Datatype;
typedef struct Stack
{
Datatype* a;
int top;
int Capacity;
}Stack;
void StackInit(Stack* p)
{
assert(p);
p->a = (Datatype*)malloc(4 * sizeof(Datatype));
p->top = 0;
p->Capacity = 4;
}
void StackPush(Stack* p, Datatype x)
{
assert(p);
if (p->top == p->Capacity) //检查是否要增容
{
p->a = (Datatype*)realloc(p->a, 2 * p->Capacity * sizeof(Datatype));
p->Capacity = p->Capacity * 2;
}
p->a[p->top] = x;
p->top++;
}
void StackPop(Stack* p)
{
assert(p);
if (p->top)
p->top = p->top - 1;
}
void StackPrint(Stack* p)
{
assert(p);
for (int i = 0; i < p->top; i++)
{
printf(" %d ", p->a[i]);
}
printf("\\n");
}
Datatype StackTop(Stack* p)
{
return p->a[p->top - 1];
}
int StackEmpty(Stack* p)
{
return p->top == 0;
}
void StackDestroy(Stack* p)
{
free(p->a);
p->a = NULL;
p->top = 0;
p->Capacity = 0;
}
//以上都是栈的基本操作,下面才是队列的实现
typedef struct {
Stack s1;
Stack s2;
} MyQueue;
/** Initialize your data structure here. */
MyQueue* myQueueCreate() {
MyQueue* q=(MyQueue*)malloc(sizeof(MyQueue));
StackInit(&(q->s1));
StackInit(&(q->s2));
return q;
}
/** Push element x to the back of queue. */
void myQueuePush(MyQueue* obj, int x) {
int a = 0;
StackPush(&(obj->s1), x);
}
/** Removes the element from in front of queue and returns that element. */
int myQueuePop(MyQueue* obj) {
while (StackEmpty(&(obj->s1))==0)
{
StackPush(&(obj->s2), StackTop(&(obj->s1)));
StackPop(&(obj->s1));
}
Datatype a = StackTop(&(obj->s2));
StackPop(&(obj->s2));
while (StackEmpty(&(obj->s2))==0)
{
StackPush(&(obj->s1), StackTop(&(obj->s2)));
StackPop(&(obj->s2));
}
return a;
}
/** Get the front element. */
int myQueuePeek(MyQueue* obj) {
while (StackEmpty(&(obj->s1)) == 0)
{
StackPush(&(obj->s2), StackTop(&(obj->s1)));
StackPop(&(obj->s1));
}
Datatype a = StackTop(&(obj->s2));
while (StackEmpty(&(obj->s2)) == 0)
{
StackPush(&(obj->s1), StackTop(&(obj->s2)));
StackPop(&(obj->s2));
}
return a;
}
/** Returns whether the queue is empty. */
bool myQueueEmpty(MyQueue* obj) {
return StackEmpty(&(obj->s1));
}
void myQueueFree(MyQueue* obj) {
StackDestroy(&(obj->s1));
StackDestroy(&(obj->s2));
obj = NULL;
}
队列
队列的概念和结构
队列: 只允许在一段进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(first in first out) 入队列、进行插入操作的一端称为队尾 出队列、进行删除的一段称为队头
队列的实现
队列的实现同样可以使用数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
typedef int DataType;
typedef struct BinaryTree
{
DataType x;
struct BinaryTree* _left;
struct BinaryTree* _right;
}BT;
typedef BT* Datatype;
typedef struct ListNode
{
Datatype x;
struct ListNode* next;
}ListNode;
typedef struct Queue
{
ListNode* head;
ListNode* tail;
}Queue;
void QueueInit(Queue* q); //初始化队列
void QueuePush(Queue* q, Datatype x); // 入队
void QueuePop(Queue* q); // 出队
Datatype QueueFront(Queue* q); //输出队列队头的元素
Datatype QueueBack(Queue* q); // 输出队列队尾的元素
int QueueSize(Queue* q); //输出队列的大小
int QueueEmpty(Queue* q); //判断队列是否为空
void QueueDestroy(Queue* q); //队列销毁
以上时栈的结构和具体的基本操作
下面是这些具体操作的实现
#define _CRT_SECURE_NO_WARNINGS 1
#include"Queue.h"
void QueueInit(Queue* q)
{
ListNode* First = (ListNode*)malloc(sizeof(ListNode));
q->head = q->tail = First;
q->tail->next = NULL;
}
void QueuePush(Queue* q, Datatype x)
{
assert(q);
q->tail->x = x;
ListNode* NewNode = (ListNode*)malloc(sizeof(ListNode));
q->tail->next = NewNode;
q->tail = NewNode;
q->tail->next = NULL;
}
void QueuePop(Queue* q)
{
assert(q);
assert(!QueueEmpty(q));
if (!QueueEmpty(q))
{
ListNode* temp = q->head->next;
free(q->head);
q->head = temp;
}
}
Datatype QueueFront(Queue* q)
{
assert(q);
assert(!QueueEmpty(q));
return q->head->x;
}
Datatype QueueBack(Queue* q)
{
assert(q);
assert(!QueueEmpty(q));
ListNode* cur = q->head;
while (cur->next != q->tail)
{
cur = cur->next;
}
return cur->x;
}
int QueueSize(Queue* q)
{
if (QueueEmpty(q))
{
return 0;
}
ListNode* cur = q->head;
int k = 0;
while (cur != q->tail)
{
cur = cur->next;
k++;
}
return k;
}
int QueueEmpty(Queue* q)
{
return q->head == q->tail;
}
void QueueDestroy(Queue* q)
{
ListNode* cur = q->head;
while (cur != q->tail)
{
ListNode* temp = cur->next;
free(cur);
cur = temp;
}
free(cur);
cur = NULL;
q->head = NULL;
q->tail = NULL;
}
队列的应用
用队列实现栈
leetcode——原题
实现思路: 用两个队列来实现栈的功能,栈用队列的实现不同主要在myStackPop这个函数上,该如何得到队尾的元素是本题的难点:
开卷数据结构~栈和队列详解