⭐️ 往期相关文章
✨链接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);// 空为真,非空返回0return 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->tailfree(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;
}