栈
一.栈的定义
1.栈:是只允许在一边进行插入或删除的顺序表。■特别之处在于只能在一端进行删除、插入。
2.栈顶:进行删除或插入操作的一端。
3.栈底:不能进行删除或插入的一端。
二.栈的算法
栈的实现可以用数组(顺序表),也可以使用链表,这里使用顺序表来实现。栈的常见操作有栈的初始化、销毁、增删等等,以下是栈创建过程中使用的函数。
1.宏定义(初始准备)
不直接定义一个数组,而采用realloc、malloc来动态管理顺序表大小,使栈的大小不再受限。再定义一个top栈顶指针来方便后续的增删操作。
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
typedef int MyDataType;//将int类型重定义叫做MyDataType
typedef struct Stack
{
MyDataType* a;//一个MyDataType类型的指针,可以进行动态内存开辟
int capacity;//栈的最大容量
int top;//用于栈顶指针
}ST;
2.栈的初始化
在初始化中,将top栈顶指针指向0,但此时栈中没有元素(此时栈为空)。当然也可以将top初始化为-1,此时当top为0时栈中含有一个元素。
ST st;//在测试代码中预先定义一个ST类型的变量
void InitStack(ST* ps)
{
assert(ps);
ps->a = NULL;
ps->capacity = 0;
ps->top = 0;//将栈顶指针指向0
}
3.向栈顶插入元素
在插入数据前,需要判断ps指针是否为空,还要判断栈是否已经达到最大容量,如果size=capacity(栈已达到最大容量)就需要使用realloc扩容(注意:使用realloc函数时,若第一个参数为空,则函数返回一个开辟了指定大小的地址)。当top初始化为0时,先赋值,在++top;而当top初始化为-1时,先++top,再赋值。
void PushStack(ST* ps, MyDataType x)
{
assert(ps);
if (ps->top == ps->capacity)
{
int NewSize = ps->top == 0 ? 4 : ps->capacity * 2;//用三目运算符可以避免对top是否为零的两种情况进行讨论
MyDataType* temp = (MyDataType*)realloc(ps->a, sizeof(MyDataType) * NewSize);
if (temp == NULL)
{
perror("realloc");
exit(-1);
}
ps->a = temp;
ps->capacity = NewSize;
}
ps->a[ps->top] = x;
ps->top++;//先赋值再将top加一
}
4.判断栈是否为空
bool StackEmpty(ST* ps)
{
assert(ps);
return ps->top == 0;//ps->top为0,表达式为真返回true
}
5.删除栈顶元素
删除栈顶元素之前首先需要判断栈是否为空,如果为空则不能删除元素。顺序表删除尾部元素十分简单,只需要将栈顶指针指向的位置减一即可。
void PopStack(ST* ps)
{
assert(ps);
assert(ps->top > 0);
--ps->top;
}
6.计算栈中元素个数
由于对top初始化时的值不同,计数的条件不同,所以读取个数的操作也由我们来书写,而不由使用者自己来判断。
int StackSize(ST* ps)
{
assert(ps);
assert(ps->top >= 0);
return ps->top ;
}
7.读取栈顶元素
MyDataType StackTop(ST* ps)
{
assert(ps);
assert(ps->top > 0);//需要判断栈是否为空,为空则不能读取栈顶元素
return ps->a[ps->top-1];
}
8.销毁栈
销毁需要把动态开辟的空间全部释放,并将指针置为空。
void DestroyStack(ST* ps)
{
assert(ps);
free(ps->a);
ps->a = NULL;
ps->capacity = ps->top = 0;
}
三.栈的例题
题目链接:
题目解答:
栈的特点是后入先出,与括号之间匹配判断一致
typedef char MyDataType;
typedef struct Stack
{
MyDataType* a;
int capacity;
int top;
}ST;
void InitStack(ST*ps);
void PushStack(ST* ps,MyDataType x);
void PopStack(ST* ps);
void DestroyStack(ST* ps);
bool StackEmpty(ST* ps);
MyDataType StackTop(ST* ps);
int StackSize(ST* ps);
void InitStack(ST* ps)
{
assert(ps);
ps->a = NULL;
ps->capacity = 0;
ps->top = 0;
}
void PushStack(ST* ps, MyDataType x)
{
assert(ps);
if (ps->top == ps->capacity)
{
int NewSize = ps->top == 0 ? 4 : ps->capacity * 2;
MyDataType* temp = (MyDataType*)realloc(ps->a, sizeof(MyDataType) * NewSize);
if (temp == NULL)
{
perror("realloc");
exit(-1);
}
ps->a = temp;
ps->capacity = NewSize;
}
ps->a[ps->top] = x;
ps->top++;
}
void PopStack(ST* ps)
{
assert(ps);
assert(ps->top > 0);
--ps->top;
}
void DestroyStack(ST* ps)
{
assert(ps);
free(ps->a);
ps->a = NULL;
ps->capacity = ps->top = 0;
}
bool StackEmpty(ST* ps)
{
assert(ps);
return ps->top == 0;
}
MyDataType StackTop(ST* ps)
{
assert(ps);
assert(ps->top > 0);
return ps->a[ps->top-1];
}
int StackSize(ST* ps)
{
assert(ps);
assert(ps->top >= 0);
return ps->top ;
}//手动搭建一个栈
bool isValid(char* s)
{
ST st;
InitStack(&st);
while(*s)
{
if(*s=='('||*s=='['||*s=='{')//当为左括号时入栈
{
PushStack(&st,*s);
}
else//当为右括号的时候
{
if(StackEmpty(&st))
{
DestroyStack(&st);
return false;
}//如果栈为空时就读取到右括号,
//即说明还未有左括号就有右括号,显然不符合规律
int stacktop=StackTop(&st);
PopStack(&st);
if(*s==')'&&stacktop!='('||
*s==']'&&stacktop!='['||
*s=='}'&&stacktop!='{' )//出栈并一一判断
{
DestroyStack(&st);
return false;
}
}
s++;
}
bool ret=StackEmpty(&st);
return ret;
}
队列
一.队列的定义
队列是只允许在一端插入元素,另一端删除元素的线性表,特点是先进先出。
二.队列的算法
队列是从队尾进,队头出,所以采用链表的结构来实现,如果采用数组结构,则会使队头操作繁琐。与栈类似,我们仍然是实现以下函数代码。
1.宏定义(初始准备)
Queue这个结构体中还包含两个QNode结点结构体。包含队列头尾结点,能够帮助从队尾插入和从队头删除。
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
typedef int MyDataType;//将int类型重命名为MyDataType
typedef struct QueueNode
{
MyDataType val;
struct QueueNode* next;
}QNode;//单链表的结点结构
typedef struct Queue//队列中包含队头、队尾、队列大小
{
QNode* front;
QNode* back;
int size;
}QL;
2.队列的初始化
void QueueInit(QL* pq)
{
assert(pq);//判断pq是否为空指针
pq->size = 0;
pq->front = pq->back = NULL;
}
3.向队尾插入元素
void QueuePush(QL* pq, MyDataType x)
{
assert(pq);
QNode* newnode = (QNode*)malloc(sizeof(QNode));
if (newnode == NULL)
{
perror("malloc fail");
exit(-1);
}
newnode->val = x;
newnode->next = NULL;
if (pq->back == NULL)
pq->front = pq->back = newnode;//链表为空,则让front和back都指向newnode
else
{
pq->back->next = newnode;
pq->back = newnode;
}
pq->size++;
}//与单链表搭建类似,由于不含头结点,
//所以需要分链表是否为空的两种情况讨论。
4.删除队头元素
void QueuePop(QL* pq)//与单链表不同,这里不需要传二级指针,
//因为这里只是修改结构体中的内容,而不是改变结构体地址
{
assert(pq);
assert(!QueueEmpty(pq));//断言链表不为空,为空则不能删除数据
if (pq->front->next == NULL)//链表只含有一个数据
{
free(pq->front);
pq->back = pq->front = NULL;
}
else
{
QNode* cur = pq->front->next;
free(pq->front);
pq->front = cur;
}
pq->size--;
}
5.读取队尾和队头元素
MyDataType QueueFront(QL* pq)//队头元素
{
assert(pq);
assert(!QueueEmpty(pq));//不能为空链表
return pq->front->val;
}
MyDataType QueueBack(QL* pq)//队尾元素
{
assert(pq);
assert(!QueueEmpty(pq));//不能为空链表
return pq->back->val;
}
6.判断队列是否为空,读取队列大小
即使是读取队列元素个数这样的操作也靠函数实现,能有效避免使用者不明变量含义而造成读取元素个数错误。
bool QueueEmpty(QL* pq)//判断队列是否为空
{
assert (pq);
return pq->front==NULL;//若头结点为空,则队列不含任何元素,队列为空
}
int QueueSize(QL* pq)//读取队列大小
{
return pq->size;
}
7.队列的销毁
void QueueDestroy(QL* pq)
{
assert(pq);
QNode* cur = pq->front;
while (cur)
{
QNode* next = cur->next;
free(cur);
cur = next;
}//每个结点一一释放空间
pq->back = pq->front = NULL;
pq->size = 0;
}
三.队列的例题
题目链接:
题目解答:
可以使用单链表和数组,但显然使用数组更方便。问题的关键在于数组尾元素的处理,这时要灵活使用取模操作。
typedef struct {
int *a;
int front;
int rear;
int k;
} MyCircularQueue;
MyCircularQueue* myCircularQueueCreate(int k) {
MyCircularQueue* obj=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
obj->k=k;
obj->a=(int *)malloc(sizeof(int)*(k+1));
obj->front=obj->rear=0;
return obj;
}
bool myCircularQueueIsEmpty(MyCircularQueue* obj)
{
return obj->front==obj->rear;
}
bool myCircularQueueIsFull(MyCircularQueue* obj)
{
return (obj->rear+1)% (obj->k+1)==obj->front;
}
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value)
{
if(myCircularQueueIsFull(obj))
return false;
obj->a[obj->rear]=value;
obj->rear=(obj->rear+1)%(obj->k+1);
return true;
}
bool myCircularQueueDeQueue(MyCircularQueue* obj)
{
if(myCircularQueueIsEmpty(obj))
return false;
obj->front=(obj->front+1)%(obj->k+1);
return true;
}
int myCircularQueueFront(MyCircularQueue* obj)
{
if(myCircularQueueIsEmpty(obj))
return -1;
else
return obj->a[obj->front];
}
int myCircularQueueRear(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
return -1;
else
return obj->a[(obj->rear+obj->k)%(obj->k+1)];
}
void myCircularQueueFree(MyCircularQueue* obj) {
free(obj->a);
free(obj);
}
栈与队列的互相实现
题目链接:
题目一:用栈实现队列
题目二:用队列实现栈
题目解答:
题目一:由于C语言没有库函数来搭建栈,所以我们必须手动搭建一个栈。思路很简单:用一个栈专门来进数据;另一个栈专门来出数据。
typedef int MyDataType;
typedef struct Stack
{
MyDataType* a;
int capacity;
int top;
}ST;
void InitStack(ST*ps);
void PushStack(ST* ps,MyDataType x);
void PopStack(ST* ps);
void DestroyStack(ST* ps);
bool StackEmpty(ST* ps);
MyDataType StackTop(ST* ps);
int StackSize(ST* ps);
void InitStack(ST* ps)
{
assert(ps);
ps->a = NULL;
ps->capacity = 0;
ps->top = 0;
}
void PushStack(ST* ps, MyDataType x)
{
assert(ps);
if (ps->top == ps->capacity)
{
int NewSize = ps->top == 0 ? 4 : ps->capacity * 2;
MyDataType* temp = (MyDataType*)realloc(ps->a, sizeof(MyDataType) * NewSize);
if (temp == NULL)
{
perror("realloc");
exit(-1);
}
ps->a = temp;
ps->capacity = NewSize;
}
ps->a[ps->top] = x;
ps->top++;
}
void PopStack(ST* ps)
{
assert(ps);
assert(ps->top > 0);
--ps->top;
}
void DestroyStack(ST* ps)
{
assert(ps);
free(ps->a);
ps->a = NULL;
ps->capacity = ps->top = 0;
}
bool StackEmpty(ST* ps)
{
assert(ps);
return ps->top == 0;
}
MyDataType StackTop(ST* ps)
{
assert(ps);
assert(ps->top > 0);
return ps->a[ps->top-1];
}
int StackSize(ST* ps)
{
assert(ps);
assert(ps->top >= 0);
return ps->top ;
}
typedef struct {
ST pushstack;
ST popstack;
int size;
} MyQueue;
MyQueue* myQueueCreate()
{
MyQueue*queue=(MyQueue*)malloc(sizeof(MyQueue));
InitStack(&queue->pushstack);
InitStack(&queue->popstack);
queue->size=0;
return queue;
}
void myQueuePush(MyQueue* obj, int x)
{
assert(obj);
PushStack(&obj->pushstack,x);
}
int myQueuePop(MyQueue* obj)
{
if(StackEmpty(&obj->popstack))
{
while(!StackEmpty(&obj->pushstack))
{
int push=StackTop(&obj->pushstack);
PopStack(&obj->pushstack);
PushStack(&obj->popstack,push);
}
}
int pop=StackTop(&obj->popstack);
PopStack(&obj->popstack);
return pop;
}
int myQueuePeek(MyQueue* obj)
{
if(StackEmpty(&obj->popstack))
{
while(!StackEmpty(&obj->pushstack))
{
int push=StackTop(&obj->pushstack);
PopStack(&obj->pushstack);
PushStack(&obj->popstack,push);
}
}
int pop=StackTop(&obj->popstack);
return pop;
}
bool myQueueEmpty(MyQueue* obj)
{
return StackEmpty(&obj->pushstack)&&StackEmpty(&obj->popstack);
}
void myQueueFree(MyQueue* obj)
{
DestroyStack(&obj->pushstack);
DestroyStack(&obj->popstack);
free(obj);
}
题目二:C语言仍然需要手动搭建队列。思路也不难:出数据时,现将数据导入另一个队列,只剩一个元素时,再弹出数据,如此反复。
typedef int MyDataType;
typedef struct QueueNode
{
MyDataType val;
struct QueueNode* next;
}QNode;
typedef struct Queue
{
QNode* front;
QNode* back;
int size;
}QL;
void QueueInit(QL* pq);
void QueueDestroy(QL* pq);
void QueuePush(QL* pq, MyDataType x);
void QueuePop(QL* pq);
MyDataType QueueFront(QL* pq);
MyDataType QueueBack(QL* pq);
bool QueueEmpty(QL* pq);
int QueueSize(QL* pq);
void QueueInit(QL* pq)
{
assert(pq);
pq->size = 0;
pq->front = pq->back = NULL;
}
void QueueDestroy(QL* pq)
{
assert(pq);
QNode* cur = pq->front;
while (cur)
{
QNode* next = cur->next;
free(cur);
cur = next;
}
pq->back = pq->front = NULL;
pq->size = 0;
}
void QueuePush(QL* pq, MyDataType x)
{
assert(pq);
QNode* newnode = (QNode*)malloc(sizeof(QNode));
if (newnode == NULL)
{
perror("malloc fail");
exit(-1);
}
newnode->val = x;
newnode->next = NULL;
if (pq->back == NULL)
pq->front = pq->back = newnode;
else
{
pq->back->next = newnode;
pq->back = newnode;
}
pq->size++;
}
void QueuePop(QL* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
if (pq->front->next == NULL)
{
free(pq->front);
pq->back = pq->front = NULL;
}
else
{
QNode* cur = pq->front->next;
free(pq->front);
pq->front = cur;
}
pq->size--;
}
MyDataType QueueFront(QL* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->front->val;
}
MyDataType QueueBack(QL* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->back->val;
}
bool QueueEmpty(QL* pq)
{
assert (pq);
return pq->back==NULL;
}
int QueueSize(QL* pq)
{
return pq->size;
}
typedef struct {
QL queue1;
QL queue2;
} MyStack;
MyStack* myStackCreate() {
MyStack*mystack=(MyStack*)malloc(sizeof(MyStack));
QueueInit(&mystack->queue1);
QueueInit(&mystack->queue2);
return mystack;
}
void myStackPush(MyStack* obj, int x)
{
if(QueueEmpty(&obj->queue1))
{
QueuePush(&obj->queue2,x);
}
else{
QueuePush(&obj->queue1,x);
}
}
int myStackPop(MyStack* obj)
{
QL* empque=&obj->queue1;
QL *noempque=&obj->queue2;
if(QueueEmpty(&obj->queue2))
{
empque=&obj->queue2;
noempque=&obj->queue1;
}
while(QueueSize(noempque)!=1)
{
int push=QueueFront(noempque);
QueuePop(noempque);
QueuePush(empque,push);
}
int pop=QueueFront(noempque);
QueuePop(noempque);
return pop;
}
int myStackTop(MyStack* obj) {
if(!QueueEmpty(&obj->queue1))
return QueueBack(&obj->queue1);
else
return QueueBack(&obj->queue2);
}
bool myStackEmpty(MyStack* obj) {
return QueueEmpty(&obj->queue1)&&QueueEmpty(&obj->queue2);
}
void myStackFree(MyStack* obj) {
free(obj);
}