栈是只允许在一端进行插入或删除操作的线性表,这意味着我们可以使用顺序表或者链表的形式来实现一个栈,遵循后进先出的原则
出栈入栈流程如下
栈是一个线性表,可以使用顺序表实现,也可以使用链表实现
使用顺序表实现栈时,需要提前申请连续的内存空间,且存在栈满、扩容等问题
可以建立一个数组,数组头部作为栈底,数组尾部作为栈顶,同时使用一个变量来记录当前栈顶元素所在的数组下标,所有操作都在栈尾进行,这样可以避免移动元素
#include <stdio.h>
#include <stdlib.h>
#define MAX_SIZE 8
#define ARRAY_STACK_INCRE_MENT 8
typedef enum Status { OK, MEMORY_OVERFLOW, ERROR,EMPTY_STACK } Status;
typedef int ElemType;
typedef struct ArrayStack {
ElemType* data;
int stackTopIndex;// 栈顶元素位置
int length;// 栈长度
} ArrayStack;
Status InitStack(ArrayStack* arrayStack) {
arrayStack->data = (ElemType*)malloc(sizeof(ElemType) * MAX_SIZE);
if (arrayStack->data == NULL) {
printf("内存初始化失败\n");
return MEMORY_OVERFLOW;
}
arrayStack->stackTopIndex = -1;
arrayStack->length = MAX_SIZE;
return OK;
}
Status Push(ArrayStack* arrayStack,ElemType e) {
if (arrayStack->stackTopIndex + 1 >= arrayStack->length) {
ElemType* newData = (ElemType*)realloc(arrayStack->data, (arrayStack->length + ARRAY_STACK_INCRE_MENT) * sizeof(ElemType));
if (newData == NULL) {
printf("扩容失败\n");
free(newData);
return MEMORY_OVERFLOW;
}
arrayStack->data = newData;
arrayStack->length += ARRAY_STACK_INCRE_MENT;
}
arrayStack->data[arrayStack->stackTopIndex + 1] = e;
arrayStack->stackTopIndex++;
return OK;
}
ElemType Pop(ArrayStack* arrayStack) {
if (arrayStack->stackTopIndex == -1) {
printf("空栈");
return -1;
}
// 这里其实没有真的删除数组中的元素,而是将栈顶下标前移,入栈的时候会覆盖原来的值
ElemType e = arrayStack->data[arrayStack->stackTopIndex];
arrayStack->stackTopIndex--;
return e;
}
ElemType GetPop(ArrayStack* arrayStack) {
if (arrayStack->stackTopIndex == -1) {
printf("空栈");
return -1;
}
// 这个方法与出栈的区别仅限于没有移动栈顶下标
return arrayStack->data[arrayStack->stackTopIndex];
}
Status PrintArrayStack(ArrayStack* arrayStack) {
if (arrayStack->stackTopIndex == -1) {
printf("\n");
return EMPTY_STACK;
}
for (int index = 0; index <= arrayStack->stackTopIndex; index++) {
printf("%d ", arrayStack->data[index]);
}
printf("\n");
return OK;
}
int main() {
ArrayStack arrayStack;
InitStack(&arrayStack);
for (int i = 0; i < 10; i++) {
Push(&arrayStack,i+1);
}
PrintArrayStack(&arrayStack);
printf("%d\n", GetPop(&arrayStack));
printf("%d\n", Pop(&arrayStack));
printf("%d\n", GetPop(&arrayStack));
printf("%d\n", Pop(&arrayStack));
printf("%d\n", GetPop(&arrayStack));
printf("%d\n", Pop(&arrayStack));
printf("%d\n", GetPop(&arrayStack));
printf("%d\n", Pop(&arrayStack));
printf("%d\n", GetPop(&arrayStack));
printf("%d\n", Pop(&arrayStack));
printf("%d\n", GetPop(&arrayStack));
printf("%d\n", Pop(&arrayStack));
printf("%d\n", GetPop(&arrayStack));
printf("%d\n", Pop(&arrayStack));
printf("%d\n", GetPop(&arrayStack));
printf("%d\n", Pop(&arrayStack));
printf("%d\n", GetPop(&arrayStack));
printf("%d\n", Pop(&arrayStack));
printf("%d\n", GetPop(&arrayStack));
printf("%d\n", Pop(&arrayStack));
printf("%d\n", GetPop(&arrayStack));
printf("%d\n", Pop(&arrayStack));
}
使用链表实现栈时,不需要考虑栈满扩容等因素,所有操作都在链表头部实现,在链表头部入栈,头部出栈
#include <stdio.h>
#include <stdlib.h>
typedef enum Status { OK, MEMORY_OVERFLOW, ERROR, EMPTY_STACK } Status;
typedef int ElemType;
typedef struct StackNode {
ElemType data;
struct StackNode* next;
}StackNode;
typedef struct LinkedStack {
StackNode* head;
int length;
}LinkedStack;
Status InitLinkedStack(LinkedStack* stack){
stack->head = NULL;
stack->length = 0;
return OK;
}
Status LinkedStackPush(LinkedStack* stack, ElemType e) {
StackNode* newNode = (StackNode*)malloc(sizeof(StackNode));
if (newNode == NULL) {
return MEMORY_OVERFLOW;
}
newNode->data = e;
if (stack->length == 0){
newNode->next = NULL;
stack->head = newNode;
stack->length++;
return OK;
}
StackNode* currentHead = stack->head;
newNode->next = currentHead;
stack->head = newNode;
stack->length++;
return OK;
}
ElemType LinkedStackPop(LinkedStack* stack) {
if (stack->length == 0){
printf("空栈\n");
return -1;
}
StackNode* currentHead = stack->head;
ElemType data = currentHead->data;
stack->head = currentHead->next;
free(currentHead);
stack->length--;
return data;
}
ElemType LinkedStackGetPop(LinkedStack* stack) {
if (stack->length == 0) {
printf("空栈\n");
return -1;
}
StackNode* currentHead = stack->head;
ElemType data = currentHead->data;
return data;
}
Status PrintLinkedStack(LinkedStack* stack) {
if (stack->length == 0) {
printf("空栈\n");
return EMPTY_STACK;
}
StackNode* currentHead = stack->head;
while (currentHead != NULL) {
printf("%d ", currentHead->data);
currentHead = currentHead->next;
}
printf("\n");
return OK;
}
int main() {
LinkedStack stack;
InitLinkedStack(&stack);
for (int i = 0; i < 10; i++) {
LinkedStackPush(&stack, i + 1);
}
PrintLinkedStack(&stack);
printf("%d\n", LinkedStackGetPop(&stack));
printf("%d\n", LinkedStackPop(&stack));
printf("%d\n", LinkedStackGetPop(&stack));
printf("%d\n", LinkedStackPop(&stack));
printf("%d\n", LinkedStackGetPop(&stack));
printf("%d\n", LinkedStackPop(&stack));
printf("%d\n", LinkedStackGetPop(&stack));
printf("%d\n", LinkedStackPop(&stack));
printf("%d\n", LinkedStackGetPop(&stack));
printf("%d\n", LinkedStackPop(&stack));
printf("%d\n", LinkedStackGetPop(&stack));
printf("%d\n", LinkedStackPop(&stack));
printf("%d\n", LinkedStackGetPop(&stack));
printf("%d\n", LinkedStackPop(&stack));
printf("%d\n", LinkedStackGetPop(&stack));
printf("%d\n", LinkedStackPop(&stack));
printf("%d\n", LinkedStackGetPop(&stack));
printf("%d\n", LinkedStackPop(&stack));
printf("%d\n", LinkedStackGetPop(&stack));
printf("%d\n", LinkedStackPop(&stack));
printf("%d\n", LinkedStackGetPop(&stack));
printf("%d\n", LinkedStackPop(&stack));
}
队列是一种受限的线性表,允许在表的一端进行插入(入队),在另一端进行删除(出队)
队列使用顺序存储有两种方法,一种是出队时真的删除那个元素,这样的话就需要对数组进行移动,此时算法的时间复杂度就为变成O(n)
另一种是用front,rear两个变量记录当前队首元素和队尾元素在数组中的下标位置,但是此时带来另一个问题,就是队列满无法判断
如上图队列有以下特点:
把存储元素的线性表从逻辑上视为一个环,称为循环队列
循环队列性质:
循环队列出队入队
空队和队满的区分防范
#include <stdio.h>
#include <stdlib.h>
#define MAX_SIZE 6
#define TRUE 1
#define FALSE 0
typedef int bool;
typedef int ElemType;
typedef struct ArrayQueue {
ElemType data[MAX_SIZE];
int front, rear;
}ArrayQueue;
InitArrayQueue(ArrayQueue* queue) {
queue->front = 0;
queue->rear = 0;
}
bool isEmpty(ArrayQueue* queue) {
if (queue->front == queue->rear) {
return TRUE;
}
return FALSE;
}
void EnArrayQueue(ArrayQueue* queue,ElemType e) {
if ((queue->rear + 1) % MAX_SIZE == queue->front) {
printf("队满!\n");
return;
}
queue->data[queue->rear] = e;
queue->rear = (queue->rear + 1) % MAX_SIZE;
}
ElemType DeArrayQueue(ArrayQueue* queue) {
if (isEmpty(queue) == TRUE) {
printf("队空!\n");
return -1;
}
ElemType e = queue->data[queue->front];
printf("%d出队\n", e);
queue->front = (queue->front + 1) % MAX_SIZE;
return e;
}
int main() {
ArrayQueue queue;
InitArrayQueue(&queue);
for (int i = 0; i < 10; i++) {
EnArrayQueue(&queue, i);
}
for (int i = 0; i < 10; i++) {
DeArrayQueue(&queue);
}
}
队列链表实现不存在队满问题,但是任然需要两个指针,一个指向链表头元素,一个指向链表尾部元素,从链表尾部入队,从链表头部出队,这里建立一个空的头节点,初始化或链表为空都指向这个空节点,方便后续操作
#include <stdio.h>
#include <stdlib.h>
#define TRUE 1
#define FALSE 0
typedef int bool;
typedef int ElemType;
typedef struct LinkedQueueNode {
ElemType data;
struct LinkedQueueNode* next;
}LinkedQueueNode;
typedef struct LinkedQueue {
LinkedQueueNode* front;
LinkedQueueNode* rear;
}LinkedQueue;
void InitLinkedQueue(LinkedQueue* queue) {
LinkedQueueNode* head = (LinkedQueueNode*)malloc(sizeof(LinkedQueueNode));
if (head == NULL) {
printf("内存申请失败\n");
return;
}
head->next = NULL;
queue->front = head;
queue->rear = head;
}
bool IsLinkedQueueEmpty(LinkedQueue* queue) {
if (queue->front == queue->rear) {
return TRUE;
}
return FALSE;
}
void EnLinkedQueue(LinkedQueue* queue,ElemType e) {
LinkedQueueNode* newNode = (LinkedQueueNode*)malloc(sizeof(LinkedQueueNode));
if (newNode == NULL) {
printf("内存申请失败\n");
return;
}
newNode->next = NULL;
newNode->data = e;
queue->rear->next = newNode;
queue->rear = newNode;
printf("%d入队\n", e);
}
void DeLinkedQueue(LinkedQueue* queue) {
if (IsLinkedQueueEmpty(queue) == TRUE) {
printf("队空\n");
return;
}
LinkedQueueNode* deQueueNode = queue->front->next;
printf("%d出队\n", deQueueNode->data);
//最后一个元素出队
if (deQueueNode == queue->rear) {
queue->rear = queue->front;
}
else {
queue->front->next = deQueueNode->next;
}
free(deQueueNode);
}
int main() {
LinkedQueue queue;
InitLinkedQueue(&queue);
for (int i = 0; i < 10; i++) {
EnLinkedQueue(&queue, i);
}
for (int i = 0; i < 10; i++) {
DeLinkedQueue(&queue);
}
}
双端队列再队列的基础上,在对列两端都允许进行出栈和入栈操作
如果在某一端允许出队和入队,但是在另一端只允许入队,则这是一个输出受限的双端队列
如果在某一端允许出队和入队,但是在另一端只允许出队,则这是一个输入受限的双端队列
如果只允许在一端进行入队和出队,那么这就是一个栈
使用数组实现顺序表时,还是要注意队列满的问题,也是使用循环队列的解决
#include <stdio.h>
#include <stdlib.h>
#define MAX_SIZE 6
#define TRUE 1
#define FALSE 0
typedef int bool;
typedef int ElemType;
typedef struct ArrayDEQueue {
ElemType data[MAX_SIZE];
int front, rear;
}ArrayDEQueue;
InitArrayDEQueue(ArrayDEQueue* queue) {
queue->front = 0;
queue->rear = 0;
}
bool isArrayDEQueueEmpty(ArrayDEQueue* queue) {
if (queue->front == queue->rear) {
return TRUE;
}
return FALSE;
}
void ArrayDEQueueInsertFront(ArrayDEQueue* queue, ElemType e) {
if ((queue->rear + 1) % MAX_SIZE == queue->front) {
printf("队满!\n");
return;
}
// front向前移动一位,为了避免负数,先加MAX_SIZE再求余
queue->front = (queue->front - 1 + MAX_SIZE)%MAX_SIZE;
queue->data[queue->front] = e;
printf("%d入队\n", e);
}
void ArrayDEQueueInsertRear(ArrayDEQueue* queue, ElemType e) {
if ((queue->rear + 1) % MAX_SIZE == queue->front) {
printf("队满!\n");
return;
}
// rear向后移动一位
queue->data[queue->rear] = e;
queue->rear = (queue->rear + 1) % MAX_SIZE;
printf("%d入队\n", e);
}
ElemType ArrayDEQueueRemoveFront(ArrayDEQueue* queue) {
if (isArrayDEQueueEmpty(queue) == TRUE) {
printf("队空!\n");
return -1;
}
ElemType e = queue->data[queue->front];
printf("%d出队\n", e);
queue->front = (queue->front + 1) % MAX_SIZE;
return e;
}
ElemType ArrayDEQueueRemoveRear(ArrayDEQueue* queue) {
if (isArrayDEQueueEmpty(queue) == TRUE) {
printf("队空!\n");
return -1;
}
// 从队尾出队,此时需要将rear前移一位
queue->rear = (queue->rear - 1+ MAX_SIZE) % MAX_SIZE;
ElemType e = queue->data[queue->rear];
printf("%d出队\n", e);
return e;
}
int main() {
ArrayDEQueue queue;
InitArrayDEQueue(&queue);
printf("从队尾入队,队尾出队\n");
for (int i = 0; i < 10; i++) {
ArrayDEQueueInsertRear(&queue, i);
}
for (int i = 0; i < 10; i++) {
ArrayDEQueueRemoveRear(&queue);
}
printf("从队头入队,队头出队\n");
for (int i = 0; i < 10; i++) {
ArrayDEQueueInsertFront(&queue, i);
}
for (int i = 0; i < 10; i++) {
ArrayDEQueueRemoveFront(&queue);
}
printf("从队头入队,队尾出队\n");
for (int i = 0; i < 10; i++) {
ArrayDEQueueInsertFront(&queue, i);
}
for (int i = 0; i < 10; i++) {
ArrayDEQueueRemoveRear(&queue);
}
printf("从队尾入队,队头出队\n");
for (int i = 0; i < 10; i++) {
ArrayDEQueueInsertRear(&queue, i);
}
for (int i = 0; i < 10; i++) {
ArrayDEQueueRemoveFront(&queue);
}
}
为了方便查询前后节点,使用双链表,且不需要空的头节点
#include <stdio.h>
#include <stdlib.h>
#define TRUE 1
#define FALSE 0
typedef int bool;
typedef int ElemType;
typedef struct LinkedDEQueueNode {
ElemType data;
struct LinkedDEQueueNode* next, * pre;
}LinkedDEQueueNode;
typedef struct LinkedDEQueue {
LinkedDEQueueNode* front;
LinkedDEQueueNode* rear;
}LinkedDEQueue;
void InitLinkedDEQueue(LinkedDEQueue* queue) {
queue->front = NULL;
queue->rear = NULL;
}
void LinkedDEQueueInsertFront(LinkedDEQueue* queue, ElemType e) {
LinkedDEQueueNode* newNode = (LinkedDEQueueNode*)malloc(sizeof(LinkedDEQueueNode));
if (newNode == NULL) {
printf("内存申请失败\n");
return;
}
newNode->pre = NULL;
newNode->next = queue->front;
newNode->data = e;
// 入队若现在队首不为空,则将新节点设置为当前队首的前驱,若为空说明是空队列,要设置尾指针
if (queue->front != NULL) {
queue->front->pre = newNode;
}
else {
queue->rear = newNode;
}
queue->front = newNode;
printf("%d入队\n", e);
}
void LinkedDEQueueInsertRear(LinkedDEQueue* queue, ElemType e) {
LinkedDEQueueNode* newNode = (LinkedDEQueueNode*)malloc(sizeof(LinkedDEQueueNode));
if (newNode == NULL) {
printf("内存申请失败\n");
return;
}
newNode->pre = queue->rear;
newNode->next = NULL;
newNode->data = e;
// 入队若现在队尾不为空,则将新节点设置为当前队首的后继,若为空说明是空队列,要设置头指针
if (queue->rear != NULL) {
queue->rear->next = newNode;
}
else {
queue->front = newNode;
}
queue->rear = newNode;
printf("%d入队\n", e);
}
void LinkedDEQueueRemoveFront(LinkedDEQueue* queue) {
if (queue->front == NULL) {
printf("空队\n");
return;
}
LinkedDEQueueNode* currentHead = queue->front;
printf("%d出队\n", currentHead->data);
queue->front = currentHead->next;
// 删除队首元素,新的队首元素没有前节点
if (queue->front != NULL) {
queue->front->pre = NULL;
}
// 删除队首元素,队列为空则队尾指针也要重置
else {
queue->rear = NULL;
}
free(currentHead);
}
void LinkedDEQueueRemoveRear(LinkedDEQueue* queue) {
if (queue->front == NULL) {
printf("空队\n");
return;
}
LinkedDEQueueNode* currentHead = queue->rear;
printf("%d出队\n", currentHead->data);
queue->rear = currentHead->pre;
// 删除队首元素,新的队首元素没有前节点
if (queue->rear != NULL) {
queue->rear->next = NULL;
}
// 删除队首元素,队列为空则队尾指针也要重置
else {
queue->front = NULL;
}
free(currentHead);
}
int main() {
LinkedDEQueue queue;
InitLinkedDEQueue(&queue);
printf("从队尾入队,队尾出队\n");
for (int i = 0; i < 10; i++) {
LinkedDEQueueInsertRear(&queue, i);
}
for (int i = 0; i < 10; i++) {
LinkedDEQueueRemoveRear(&queue);
}
printf("从队头入队,队头出队\n");
for (int i = 0; i < 10; i++) {
LinkedDEQueueInsertFront(&queue, i);
}
for (int i = 0; i < 10; i++) {
LinkedDEQueueRemoveFront(&queue);
}
printf("从队头入队,队尾出队\n");
for (int i = 0; i < 10; i++) {
LinkedDEQueueInsertFront(&queue, i);
}
for (int i = 0; i < 10; i++) {
LinkedDEQueueRemoveRear(&queue);
}
printf("从队尾入队,队头出队\n");
for (int i = 0; i < 10; i++) {
LinkedDEQueueInsertRear(&queue, i);
}
for (int i = 0; i < 10; i++) {
LinkedDEQueueRemoveFront(&queue);
}
}