C语言实现栈和队列及经典题目(附详细源码)-创新互联
- [数据结构] C语言实现栈和队列及经典题目(附详细源码)
- 1. 栈
- 1.1 栈的概念及结构
- 1.2 栈的实现
- 1.3 经典题目
- 1.3.1 有效的括号(括号匹配)
- 1.3.2 用栈实现队列
- 2. 队列
- 2.1 队列的概念及结构
- 2.2 队列的实现
- 2.3 经典题目
- 2.3.1 用队列实现栈
- 2.3.2 设计循环队列
1.2 栈的实现栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端 称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出(后进先出是一个相对概念指的是同时在栈中的数据后进先出)LIFO(Last In First Out)的原则。
压栈:栈的插入操作叫做进栈/压栈/入栈入数据在栈顶。
出栈:栈的删除操作叫做出栈。出数据也在栈顶。
栈的存储是线性结构,因此栈的实现一般可以使用数组或者链表实现,相对而言数组的结构实现更优一些。因为数组在尾上插入数据的代价比较小。如果要用链表实现头插效率的更高更适合。
代码实现:
#include#include#include#include
typedef int STDataType;
typedef struct Stack//数据结构类型定义
{STDataType* a;
int capacity;
int top;
}ST;
void StackInit(ST* ps)//初始化
{assert(ps);
ps->a = (STDataType*)malloc(sizeof(STDataType) * 4);
if (ps->a == NULL)
{perror("malloc fail");
exit(-1);
}
ps->top = 0;
ps->capacity = 4;
}
void StackDestroy(ST* ps)//销毁
{assert(ps);
ps->a = NULL;
ps->top = ps->capacity = 0;
}
void StackPush(ST* ps,STDataType x)//入栈
{assert(ps);
if (ps->top == ps->capacity)
{STDataType* tmp = (STDataType*)realloc(ps->a, sizeof(STDataType) * ps->capacity * 2);
if (tmp == NULL)
{ perror("realloc fail");
exit(-1);
}
ps->a = tmp;
ps->capacity *= 2;
}
ps->a[ps->top] = x;
ps->top++;
}
void StackPop(ST* ps)//出栈
{assert(ps);
//assert(ps->top >0);
assert(!StackEmpty(ps));
ps->top--;
}
STDataType StackTop(ST* ps)//获取栈顶元素
{assert(ps);
//assert(ps->top >0);
assert(!StackEmpty(ps));
return ps->a[ps->top - 1];
}
bool StackEmpty(ST* ps)//检测栈是否为空
{assert(ps);
return ps->top == 0;
}
int StackSize(ST* ps)// 获取栈中有效元素个数
{assert(ps);
return ps->top;
}
1.3 经典题目
1.3.1 有效的括号(括号匹配)**题目链接:**https://leetcode-cn.com/problems/valid-parentheses/
题目描述:
题目分析:
遇到左括号入栈,遇到右括号拿出栈顶数据去进行匹配,十分符合栈后进先出的特性。
代码实现:
// 下面是定长的静态栈的结构,实际中一般不实用,所以我们主要实现下面的支持动态增长的栈
typedef char STDataType;
typedef struct Stack
{STDataType* a;
int top;
int capacity;
}ST;
void StackInit(ST* ps)//初始化
{assert(ps);
ps->a = (STDataType*)malloc(sizeof(STDataType)*4);
if (ps->a == NULL)
{perror("malloc fail\n");
exit(-1);
}
ps->top = 0;
ps->capacity = 4;
}
void StackDestroy(ST* ps)//销毁
{assert(ps);
free(ps->a);
ps->a = NULL;
ps->top = ps->capacity = 0;
}
void StackPush(ST* ps, STDataType x)//入栈
{assert(ps);
if (ps->top == ps->capacity)//栈满
{STDataType* tmp = (STDataType*)realloc(ps->a, sizeof(STDataType)*ps->capacity*2);
if (tmp == NULL)
{perror("realloc fail");
exit(-1);
}
ps->a = tmp;
ps->capacity *= 2;
}
ps->a[ps->top] = x;
ps->top++;
}
bool StackEmpty(ST* ps)//检测栈是否为空
{assert(ps);
return ps->top == 0;
}
void StackPop(ST* ps)//出栈
{assert(ps);
assert(!StackEmpty(ps));
ps->top--;
}
char StackTop(ST*ps)//获取栈顶元素
{assert(ps);
assert(!StackEmpty(ps));
return ps->a[ps->top - 1];
}
bool isValid(char * s){ST st;
StackInit(&st);
while(*s)
{if ((*s == '(') || (*s == '[') || (*s == '{'))//左括号入栈
{StackPush(&st, *s);
s++;
}
else//右括号进行匹配
{if (StackEmpty(&st))//有右括号没有左括号
{StackDestroy(&st);
return false;
}
STDataType tmp = StackTop(&st);
if ((tmp == '(' && *s != ')')
||(tmp == '[' && *s != ']')
||(tmp == '{' && *s != '}'))//匹配失败
{StackDestroy(&st);
return false;
}
else
{StackPop(&st);
s++;
}
}
}
if (!StackEmpty(&st))//剩余左括号未匹配
return false;
StackDestroy(&st);
return true;
}
1.3.2 用栈实现队列**题目链接:**https://leetcode-cn.com/problems/implement-queue-using-stacks/
题目描述:
题目分析:
栈的特性是后进先出,队列的特性是先进先出,用两个栈实现先进先出,需要指定一个栈用来入数据,另一个栈用来出数据,只在入数据的栈入数据,只在出数据的栈出数据,实现出队时如果出数据的栈有数据先将出数据的栈的数据出栈,如果实现出队时,出数据的栈为空,需要先将入数据的栈的数据挪动到出数据的栈的数据,再进行出队列,如果在实现出队时出数据的栈有数据,将入数据的栈的数据挪到出数据的栈会造成数据顺序的错误。
代码实现:
typedef int STDataType;
typedef struct Stack
{STDataType* a;
int top;
int capacity;
}ST;
void StackInit(ST* ps)//栈的初始化
{assert(ps);
ps->a = (STDataType*)malloc(sizeof(STDataType)*4);
ps->top = 0;
ps->capacity = 4;
}
void StackDestroy(ST* ps)//销毁栈
{assert(ps);
free(ps->a);
ps->a = NULL;
ps->top = 0;
ps->capacity = 0;
}
void StackPush(ST* ps, STDataType x)//入栈
{assert(ps);
if (ps->top == ps->capacity)
{ps->a = (STDataType*)realloc(ps->a, sizeof(STDataType)*ps->capacity*2);
ps->capacity *= 2;
}
ps->a[ps->top] = x;
ps->top++;
}
bool StackEmpty(ST* ps)//检查栈是否为空
{assert(ps);
return ps->top == 0;
}
void StackPop(ST* ps)//出栈
{assert(ps);
assert(!StackEmpty(ps));
ps->top--;
}
STDataType StackTop(ST* ps)//取栈顶元素
{assert(ps);
assert(!StackEmpty(ps));
return ps->a[ps->top - 1];
}
typedef struct {ST popst;
ST pushst;
} MyQueue;
MyQueue* myQueueCreate() {MyQueue* obj = (MyQueue*)malloc(sizeof(MyQueue));
StackInit(&obj->popst);
StackInit(&obj->pushst);
return obj;
}
void myQueuePush(MyQueue* obj, int x) {StackPush(&obj->pushst, x);
}
int myQueuePeek(MyQueue* obj);//函数声明便于复用
int myQueuePop(MyQueue* obj) {int ret = myQueuePeek(obj);
StackPop(&obj->popst);
return ret;
}
int myQueuePeek(MyQueue* obj) {if (StackEmpty(&obj->popst))
{while(!StackEmpty(&obj->pushst))
{StackPush(&obj->popst,StackTop(&obj->pushst));
StackPop(&obj->pushst);
}
}
return StackTop(&obj->popst);
}
bool myQueueEmpty(MyQueue* obj) {return StackEmpty(&obj->popst) && StackEmpty(&obj->pushst);
}
void myQueueFree(MyQueue* obj) {StackDestroy(&obj->pushst);//注意申请空间的释放
StackDestroy(&obj->popst);
free(obj);
}
2. 队列
2.1 队列的概念及结构2.2 队列的实现队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出 FIFO(First In First Out) 入队列:进行插入操作的一端称为队尾 ,出队列:进行删除操作的一端称为队头。
2.3 经典题目 2.3.1 用队列实现栈队列也可以数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数 组头上出数据,效率会比较低。
#include
#include #include #include #include typedef int QDataType; typedef struct QueueNode//结点类型定义 {QDataType data; struct QueueNode* next; }QNode; typedef struct Queue//队列结构定义 {QNode* head; QNode* tail; }Queue; void QueueInit(Queue* pq)//初始化 {assert(pq); pq->head = pq->tail = NULL; } void QueueDestroy(Queue* pq)//销毁队列 {assert(pq); QNode* cur = pq->head; while (cur) {QNode* next = cur->next; free(cur); cur = next; } pq->head = pq->tail = NULL; } void QueuePush(Queue* pq, QDataType x)//入队 {assert(pq); QNode* newnode = (QNode*)malloc(sizeof(QNode)); if (newnode == NULL) {perror("malloc fail"); exit(-1); } newnode->data = x; newnode->next = NULL; if (pq->tail == NULL) {pq->head = pq->tail = newnode; } else {pq->tail->next = newnode; pq->tail = newnode; } } void QueuePop(Queue* pq)//出队 {assert(pq); assert(!QueueEmpty(pq)); if (pq->head->next == NULL) {free(pq->head); pq->head = pq->tail = NULL; } else {QNode* del = pq->head; pq->head = pq->head->next; free(del); } } QDataType QueueFront(Queue* pq)//获取队头元素 {assert(pq); assert(!QueueEmpty(pq)); return pq->head->data; } QDataType QueueBack(Queue* pq)//获取队尾元素 {assert(pq); assert(!QueueEmpty(pq)); return pq->tail->data; } bool QueueEmpty(Queue* pq)//查看队列是否为空 {assert(pq); return pq->head == NULL && pq->tail == NULL; } int QueueSize(Queue* pq)//获取队列数据个数 {assert(pq); int size = 0; QNode* cur = pq->head; while (cur) {size++; cur = cur->next; } return size; } 另外扩展了解一下,实际中我们有时还会使用一种队列叫循环队列。如操作系统课程讲解生产者消费者模型 时可以就会使用循环队列。环形队列可以使用数组实现,也可以使用循环链表实现。
**题目链接:**https://leetcode-cn.com/problems/implement-stack-using-queues/
题目描述:
题目分析:
用队列实现栈,首先要了解栈和队列的特性,栈是后进先出,队列是先进后出,因此我们需要用两个队列来实现一个栈的功能,为了实现后进先出的功能,在出数据时,我们需要把有数据的队列的除了最后一个数据全部挪到空队列里,剩下的那一个数据就成为了队头,可以出队,由此后进先出的功能就能实现
实现入栈和出栈及取栈顶元素:无论是实现入栈或出栈或取栈顶元素,都要保证操作完成后一个队列有数据,一个队列为空,永远保持有一个空队列用于数据的挪动,如果是实现入栈,把数据入队至有数据的队列,如果是实现出栈,把有数据的队列的数据挪到空队列直到剩下一个数据时,将剩下的数据出队实现出栈,如果是实现获取栈顶元素,把有数据的队列的数据挪到空队列直到剩下一个数据时,将剩下的数据记录,然后将其在挪动到另一个队列。
代码实现:
typedef int QDataType;//队列存储的数据类型的定义
typedef struct QNode//队列结点结构定义
{QDataType data;
struct QNode* next;
}QNode;
typedef struct Queue//队列定义
{QNode* head;//指向队头
QNode* tail;//指向队尾
int size;//记录队列存储数据个数
}Queue;
void QueueInit(Queue* pq)//队列初始化
{assert(pq);
pq->head = pq->tail = NULL;
pq->size = 0;
}
void QueueDestroy(Queue* pq)//销毁队列
{assert(pq);
QNode* del = pq->head;
while(del)
{QNode* next = del->next;
free(del);
del = next;
}
pq->head = pq->tail = NULL;
pq->size = 0;
}
bool QueueEmpty(Queue* pq)//判断队列是否为空
{assert(pq);
return pq->size == 0;
}
void QueuePush(Queue* pq, QDataType x)//入队
{assert(pq);
QNode* newnode = (QNode*)malloc(sizeof(QNode));
newnode->data = x;
newnode->next = NULL;
if (pq->tail == NULL)
{pq->head = pq->tail = newnode;
}
else
{pq->tail->next = newnode;
pq->tail = pq->tail->next;
}
pq->size++;
}
void QueuePop(Queue* pq)//出队
{assert(pq);
assert(!QueueEmpty(pq));
if (pq->head->next != NULL)
{QNode* del = pq->head;
pq->head = pq->head->next;
free(del);
}
else
{free(pq->head);
pq->head = pq->tail = NULL;
}
pq->size--;
}
QDataType QueueFront(Queue* pq)//获取队头数据
{assert(pq);
assert(!QueueEmpty(pq));
return pq->head->data;
}
QDataType QueueBack(Queue* pq)//获取队尾数据
{assert(pq);
assert(!QueueEmpty(pq));
return pq->tail->data;
}
int QueueSize(Queue* pq)//获取队列数据个数
{assert(pq);
return pq->size;
}
typedef struct {//用两个队列定义的栈
Queue q1;
Queue q2;
} MyStack;
MyStack* myStackCreate() {MyStack* obj = (MyStack*)malloc(sizeof(MyStack));
QueueInit(&obj->q1);
QueueInit(&obj->q2);
return obj;
}
void myStackPush(MyStack* obj, int x) {Queue* empty = &obj->q1;//假设一个为空另一个为非空
Queue* nonempty = &obj->q2;
if (QueueEmpty(&obj->q2))//若假设错误,修改假设
{empty = &obj->q2;
nonempty = &obj->q1;
}
QueuePush(nonempty, x);
}
int myStackPop(MyStack* obj) {Queue* empty = &obj->q1;
Queue* nonempty = &obj->q2;
if (QueueEmpty(&obj->q2))
{empty = &obj->q2;
nonempty = &obj->q1;
}
while(QueueSize(nonempty) >1)
{QueuePush(empty,QueueFront(nonempty));
QueuePop(nonempty);
}
int top = QueueFront(nonempty);
QueuePop(nonempty);
return top;
}
int myStackTop(MyStack* obj) {Queue* empty = &obj->q1;
Queue* nonempty = &obj->q2;
if (QueueEmpty(&obj->q2))
{empty = &obj->q2;
nonempty = &obj->q1;
}
while(QueueSize(nonempty) >1)
{QueuePush(empty,QueueFront(nonempty));
QueuePop(nonempty);
}
int top = QueueFront(nonempty);
QueuePush(empty,QueueFront(nonempty));
QueuePop(nonempty);
return top;
}
bool myStackEmpty(MyStack* obj) {return QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2);
}
void myStackFree(MyStack* obj) {QueueDestroy(&obj->q1);//注意队列结点的释放
QueueDestroy(&obj->q2);
free(obj);
}
2.3.2 设计循环队列**题目链接:**https://leetcode-cn.com/problems/design-circular-queue/
题目描述:
题目分析:
首先,我们要考虑是用数组还是链表实现。
首先分析用链表实现,假设k(队列长度)为4,用链表实现首先要解决的问题是判空和判满的条件
判空和判满无法很好的进行区分,为了解决这个问题,我们可以:
1.改良队列数据结构增加一个变量记录队列长度
2.增加一个空余结点
增加一个空余结点解决了判空判满的区分,造成了新的问题:取队尾数据变得十分困难,当然也可以解决,但是可以看出用链表实现是比较麻烦的。
再看看用数组实现,
同样是增加一个空余结点,数组不仅解决了找尾部数据的问题,而且从具体实现上也比链表简单许多,因此选择数组实现是更为优的方式。
用数组实现还要注意几个细节实现:
判空判满:只要队头等于队尾队列为空,判断为满需要考虑队尾在数组末尾的情况,如果队尾不在末尾,队尾加一取模还是它本身,如果队尾在末尾,队尾加一取模就回到数组开头。
**入数据:**需要注意队尾在数组末尾再入数据时。
**出数据:**需要注意队头在数组末尾再出数据。
**取队尾数据:**注意队尾在数组头部。
入数据注意队尾回到数组头部,出数据注意队头回到数组头部,取队尾数据注意队尾在数组头部取队尾,也就是加一减一时能否回到正确位置。
代码实现:
typedef struct {int* a;
int front;
int rear;
int k;
} MyCircularQueue;
MyCircularQueue* myCircularQueueCreate(int k) {MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
//多开一个解决判空判满问题
obj->a = (int*)malloc(sizeof(int) * (k + 1));
obj->front = 0;
obj->rear = 0;
//队列满时可以存储数据的个数,空间为k+1
obj->k = k;
return obj;
}
bool myCircularQueueIsFull(MyCircularQueue* obj);
bool myCircularQueueIsEmpty(MyCircularQueue* obj);
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {assert(obj);
if (myCircularQueueIsFull(obj)==true)
return false;
obj->a[obj->rear] = value;
obj->rear = (obj->rear + 1) % (obj->k + 1);
return true;
}
bool myCircularQueueDeQueue(MyCircularQueue* obj) {assert(obj);
if (myCircularQueueIsEmpty(obj))
return false;
obj->front = (obj->front + 1) % (obj->k + 1);
return true;
}
int myCircularQueueFront(MyCircularQueue* obj) {assert(obj);
if (myCircularQueueIsEmpty(obj))
return -1;
return obj->a[obj->front];
}
int myCircularQueueRear(MyCircularQueue* obj) {assert(obj);
if (myCircularQueueIsEmpty(obj))
return -1;
return obj->a[(obj->rear + obj->k) % (obj->k + 1)];
}
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {assert(obj);
return obj->front == obj->rear;
}
bool myCircularQueueIsFull(MyCircularQueue* obj) {assert(obj);
return ((obj->rear + 1) % (obj->k + 1)) == obj->front;
}
void myCircularQueueFree(MyCircularQueue* obj) {assert(obj);
free(obj->a);
free(obj);
}
你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧
分享文章:C语言实现栈和队列及经典题目(附详细源码)-创新互联
标题URL:http://hbruida.cn/article/cdgphh.html