⭐️ 往期相关文章
✨链接1:数据结构和算法的概念以及时间复杂度空间复杂度详解
✨链接2:【数据结构】手撕顺序表
✨链接3:【数据结构】手撕单链表
✨链接4:【数据结构】双向带头循环链表
⭐️ 栈和队列
🌠 栈
栈是一种特殊的线性表,其只允许在固定的一端进行插入和删除元素的操作,或也称入栈和出栈。进行数据插入和删除操作的一端称为栈顶,另一个端称为栈底。栈中的数据元素遵守后进先出(Last in First Out)的原则。
压栈:栈的插入操作叫做进栈 / 压栈 / 入栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈。出数据也在栈顶。
图:
🌠 栈的实现
栈的实现一般可以使用数组或链表实现,相对而言数组的结构实现更优一些,数组实现可以让下标为 0
的这一端当作栈底,另一端当作栈顶,并使用一个 top
下标记录着栈定的位置,那么当 push
元素的时候只需要
O
(
1
)
O(1)
O(1) 的复杂度实现插入,删除也是一样。如果采用链表实现,每次 push
元素时需要尾插,单链表的话需要每次找尾结点,这样时间复杂度就是
O
(
N
)
O(N)
O(N) 了,或者也可以定义一个 tail
尾结点,每次则不需要找尾,但是出栈的时候又是一个问题,因为删除 tail
尾结点还需要把 tail
往前移动一下,但是此时找不到 tail
的前一个结点,还是需要遍历找 tail
结点的前一个结点。所以如果是用链表实现栈的话,最好是链表的尾部当作栈底,链表头结点当作栈顶,这样的话链表的头插头删时间复杂度都是
O
(
1
)
O(1)
O(1) 。我们本章主要采用数组来实现栈。
栈的接口
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
typedef int StackType;
typedef struct Stack {
StackType* data;
int top; // 栈顶
int capacity; // 容量
}Stack;
// 栈的初始化
void StackInit(Stack * ps);
// 栈的销毁
void StackDestroy(Stack * ps);
// 入栈
void StackPush(Stack* ps , StackType node);
// 出栈
void StackPop(Stack* ps);
// 栈是否为空
bool StackEmpty(Stack* ps);
// 栈的大小
int StackSize(Stack* ps);
// 取栈顶元素
StackType StackTop(Stack * ps);
StackInit
实现
void StackInit(Stack* ps) {
assert(ps);
ps->data = NULL;
ps->top = 0;
ps->capacity = 0;
}
StackDestroy
实现
void StackDestroy(Stack* ps) {
assert(ps);
free(ps->data);
ps->data = NULL;
ps->top = 0;
ps->capacity = 0;
}
StackPush
实现
void StackPush(Stack* ps , StackType node) {
assert(ps);
// 检查容量
if (ps->top == ps->capacity) {
int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
StackType* newData = (StackType*)realloc(ps->data , sizeof(StackType) * newCapacity);
assert(newData);
ps->data = newData;
ps->capacity = newCapacity;
}
ps->data[ps->top] = node;
ps->top++;
}
StackPop
实现
void StackPop(Stack* ps) {
assert(ps);
// 判断栈是否为空
assert(!StackEmpty(ps));
ps->top--;
}
StackTop
实现
StackType StackTop(Stack* ps) {
assert(ps);
assert(!StackEmpty(ps));
return ps->data[ps->top - 1];
}
StackEmpty
实现
bool StackEmpty(Stack* ps) {
assert(ps);
// 空为真,非空返回0
return ps->top == 0;
}
StackSize
实现
int StackSize(Stack* ps) {
assert(ps);
return ps->top;
}
🌠 队列
队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出(First In First Out)。
入队列(Enqueue):进行插入操作的一端称为队尾(rear)。
出队列(Dequeue):进行删除操作的一端称为队头(front)。
图:
🌠 队列的实现
队列和栈一样都可以使用数组和链表的结构来实现,但是如果选择数组实现队列的话,下标为 0
的一端是队头,另一端是队尾,那么插入数据的时间复杂度是
O
(
1
)
O(1)
O(1),但是当从队头删除数据的时候,数组需要从后往前挪动数据,效率较低。而使用链表实现队列,单链表的头删是
O
(
1
)
O(1)
O(1) 的操作,但是如果要从队尾入数据单链表需要找尾,所以我们这里可以定义两个指针 head
和 tail
这样单链表尾插时间复杂度
O
(
1
)
O(1)
O(1) 就不需要每次入队列找尾结点了。所以使用队列使用链表来实现更优。
队列的接口
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
typedef int QueueDataType;
// 单链表实现队列
typedef struct QueueNode {
QueueDataType data;
struct QueueNode* next;
}QueueNode;
typedef struct Queue {
QueueNode* head; // 指向队头
QueueNode* tail; // 指向队尾
}Queue;
// 队列的初始化
void QueueInit(Queue* q);
// 队列的销毁
void QueueDestroy(Queue* q);
// 队尾入数据
void QueuePush(Queue* q , QueueDataType x);
// 队头出数据
void QueuePop(Queue* q);
// 判断队列是否为空
bool QueueIsEmpty(Queue* q);
// 获取队头数据
QueueDataType QueueFront(Queue* q);
// 获取队尾数据
QueueDataType QueueBack(Queue* q);
// 获取队列有效元素的个数
int QueueSize(Queue* q);
QueueInit
实现
void QueueInit(Queue* q) {
assert(q);
q->head = NULL;
q->tail = NULL;
}
QueueDestroy
实现
void QueueDestroy(Queue* q) {
assert(q);
QueueNode* cur = q->head;
while (cur) {
QueueNode* next = cur->next;
free(cur);
cur = next;
}
q->head = q->tail = NULL;
}
QueuePush
实现
void QueuePush(Queue* q , QueueDataType x) {
assert(q);
// 创建新结点
QueueNode* newNode = (QueueNode*)malloc(sizeof(QueueNode));
assert(newNode);
newNode->data = x;
newNode->next = NULL;
// 没有结点的情况
if (q->tail == NULL) {
q->head = q->tail = newNode;
}
else {
// 多个结点的情况
q->tail->next = newNode;
q->tail = q->tail->next;
}
}
QueuePop
实现
void QueuePop(Queue* q) {
assert(q);
// 空队列
assert(!QueueIsEmpty(q));
// 一个结点的情况
if (q->head->next == NULL) { // q->head == q->tail
free(q->head);
q->head = q->tail = NULL;
}
else {
// 多个结点的情况
QueueNode* next = q->head->next;
free(q->head);
q->head = next;
}
}
QueueIsEmpty
实现
bool QueueIsEmpty(Queue* q) {
assert(q);
return q->head == NULL;
}
QueueFront
实现
QueueDataType QueueFront(Queue* q) {
assert(q);
// 空队列
assert(!QueueIsEmpty(q));
return q->head->data; // 返回队头数据
}
QueueBack
实现
QueueDataType QueueBack(Queue* q) {
assert(q);
// 空队列
assert(!QueueIsEmpty(q));
return q->tail->data; // 返回队尾数据
}
QueueSize
实现
int QueueSize(Queue* q) {
assert(q);
int size = 0;
QueueNode* cur = q->head;
while (cur != NULL) {
size++;
cur = cur->next;
}
return size;
}