1.栈
1.1 概念
栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行插入和删除的一端为栈顶,另一端为栈底。栈中的元素遵守先进后出的原则。
压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈。出数据也在栈顶
栈的实现:栈的实现一般可以使用数组或者链表实现,相对而言,数组的结构更加优化一些。因为数组在尾上插入数据的代价比较小。
1.2 栈的实现
有三个文件,分别为Stack.h,Stack.c ,test.c
栈的结构:
struct Stack
{
int* arr;
int capacity;
int top;//相当于size,即栈顶
}
1.2.1 栈的初始化
//初始化
void STInit(ST* ps)
{
assert(ps);
ps->arr = NULL;
ps->capacity = ps->top = 0;
}
当栈为空时,栈顶==栈底
1.2.2 栈的销毁
//销毁
void STDestroy(ST* ps)
{
assert(ps);
if (ps->arr)
{
free(ps->arr);
}
ps->arr = NULL;
ps->top = ps->capacity = 0;
}
1.2.3 栈的入数据
无论是入数据还是出数据,都是在栈顶实现的。
入栈是增加了数据,所以首先我们应该判断空间是否足够。
代码实现如下:
//栈顶---入数据、出数据
void StackPush(ST* ps, STDataType x)
{
assert(ps);
//检查空间够不够
if (ps->capacity == ps->top)
{
//2倍增容
int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
STDataType* tmp = (STDataType*)realloc(ps->arr, newCapacity * sizeof(STDataType));
if (tmp == NULL)
{
perror("realloc fail!");
exit(1);
}
ps->arr = tmp;
ps->capacity = newCapacity;
}
//空间足够;
ps->arr[ps->top++] = x;
}
要记得在最后不要忘记将top++,以便来进行下一个入数据的操作。
1.2.4 栈的出数据
当栈为空时,不可以出数据,所以要先判断栈是否为空。
//判断栈是否为空
bool StackEmpty(ST* ps)
{
assert(ps);
return ps->top == 0;
}
如果不为空,才可以进行后续操作。
//出数据
void StackPop(ST* ps)
{
assert(ps);
assert(!StackEmpty(ps));
--ps->top;
}
1.2.5 取栈顶元素
代码实现如下:
//取栈顶元素
STDataType StackTop(ST* ps)
{
assert(ps);
assert(!StackEmpty(ps));
return ps->arr[ps->top - 1];
}
在栈中遵循先进后出的原则,因此,不可以用函数直接实现栈的打印。
注意:栈里的数据不能被遍历,也不能被随机访问。
1.3 栈的典型算法题解析
1.3.1 有效的括号
借助栈的知识来解决这道题
//定义栈的结构
typedef char STDataType;
typedef struct Stack
{
STDataType* arr;
int capacity;//栈的空间大小
int top; //栈顶(有效数)
}ST;
//判断栈是否为空
bool StackEmpty(ST* ps)
{
assert(ps);
return ps->top == 0;
}
//初始化
void STInit(ST* ps)
{
assert(ps);
ps->arr = NULL;
ps->capacity = ps->top = 0;
}
//销毁
void STDestroy(ST* ps)
{
assert(ps);
if (ps->arr)
{
free(ps->arr);
}
ps->arr = NULL;
ps->top = ps->capacity = 0;
}
//栈顶---入数据、出数据
void StackPush(ST* ps, STDataType x)
{
assert(ps);
//检查空间够不够
if (ps->capacity == ps->top)
{
//2倍增容
int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
STDataType* tmp = (STDataType*)realloc(ps->arr, newCapacity * sizeof(STDataType));
if (tmp == NULL)
{
perror("realloc fail!");
exit(1);
}
ps->arr = tmp;
ps->capacity = newCapacity;
}
//空间足够;
ps->arr[ps->top++] = x;
}
//出数据
void StackPop(ST* ps)
{
assert(ps);
assert(!StackEmpty(ps));
--ps->top;
}
//取栈顶元素
STDataType StackTop(ST* ps)
{
assert(ps);
assert(!StackEmpty(ps));
return ps->arr[ps->top - 1];
}
//获取栈中有效元素个数
int STSize(ST* ps)
{
assert(ps);
return ps->top;
}
bool isValid(char* s) {
ST st;
//初始化
STInit(&st);
//遍历字符串s
char* ps = s;
while(*ps != '\0')
{
if(*ps == '(' || *ps == '[' || *ps == '{')
{
StackPush(&st, *ps);
}
else
{
if(StackEmpty(&st))
{
return false;
}
char ch = StackTop(&st);
if((*ps == ')' && ch == '(') || (*ps == ']' && ch == '[') || (*ps == '}' && ch == '{'))
{
StackPop(&st);
}
else{
STDestroy(&st);
return false;
}
}
ps++;
}
bool ret = StackEmpty(&st) == true;
//销毁
STDestroy(&st);
return ret;
}
2.队列
2.1 概念
只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出
入队列:进行插入操作的一端称为队尾;
出队列:进行删除操作的一端称为队头。
2.2 队列的实现
用单链表来进行实现
2.2.1 定义队列结构
//定义队列结构
typedef int QDataType;
typedef struct QueueNode
{
QDataType data;
struct QueueNode* next;
}QueueNode;
typedef struct Queue
{
QueueNode* phead;
QueueNode* ptail;
}Queue;
2.2.2 初始化
//初始化
void QueueInit(Queue* pq)
{
assert(pq);
pq->phead = pq->ptail = NULL;
}
2.2.3 在队尾入队列
//入队列,队尾
void QueuePush(Queue* pq, QDataType x)
{
assert(pq);
//申请新节点
QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
if (newnode == NULL)
{
perror("malloc fail!");
exit(1);
}
newnode->data = x;
newnode->next = NULL;
if (pq->phead == NULL)
{
pq->phead = pq->ptail = newnode;
}
else
{
//队列不为空
pq->ptail->next = newnode;
pq->ptail = newnode;
}
}
2.2.4 队列判空
//队列判空
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->phead == NULL && pq->ptail == NULL;
}
2.2.5 在队头出队列
//出队列,队头
void QueuePop(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
//只有一个结点的情况
if (pq->ptail == pq->phead)
{
free(pq->phead);
pq->phead = pq->ptail = NULL;
}
else
{
//删除队头元素
QueueNode* next = pq->phead->next;
free(pq->phead);
pq->phead = next;
}
}
2.2.6 取队头数据
//取队头数据
QDataType QueueFront(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->phead->data;
}
2.2.7 取队尾数据
//取队尾数据
QDataType QueueBack(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->ptail->data;
}
2.2.8 队列有效数据个数
//队列有效数据个数
int QueueSize(Queue* pq)
{
assert(pq);
int size = 0;
QueueNode* pcur = pq->phead;
while (pcur)
{
size++;
pcur = pcur->next;
}
return size;
}
这里的时间复杂度为o(n),所以我们可以优化代码。在定义结构的时候进行优化。
typedef struct Queue
{
QueueNode* phead;
QueueNode* ptail;
int size;
}Queue;
2.2.8 队列的销毁
//销毁队列
void QueueDestroy(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
QueueNode* pcur = pq->phead;
while (pcur)
{
QueueNode* next = pcur->next;
free(pcur);
pcur = next;
}
pq->phead = pq->ptail = NULL;
pq->size = 0;
}