[TOC]
前言
在之前的数据结构学习中,我们学习了顺序表、链表这两种结构
除了单链表以外,还有一个结构,是双向带头循环链表
。这个链表的形式如下
- 头节点的prev指向尾部节点
- 尾节点的next指向头节点,构成循环
别看它的形式有些复杂,实际代码的实现,比单链表还简单!
因为head->prev
指向了尾节点,所以不需要找尾。尾删的时候也不需要遍历找尾节点的前一位,因为尾节点的prev就存放了前一位的地址。
所以这里就偷懒不写博客了!反正也没啥人看😭
好吧,最后我还是写了一篇水文👉点我
本篇博客讲述的是另外一个特别的线性表,栈
1.什么是栈
数据结构里的栈,和函数栈帧中的“栈”有一定相似,但实际上它们完全不同
栈作为一个特殊的线性表,它只允许在表的一头添加、删除数据。
所有的数据都遵循先进后出,后进先出
的原则
- 压栈:栈的插入操作叫做进栈/压栈/入栈,新的数据存放在栈顶
- 出栈:栈的删除操作,先删除栈顶的数据
2.栈的实现
栈可以用数组或者链表来实现,相对而言,数组的方法更优
因为数组在尾插数据的时候,可以很方便的找到尾。而单链表需要遍历找尾,耗时较长。
是不是有些似曾相识呢?没错,实际上栈区就是一个只能尾插+尾删的顺序表
3.敲代码!
3.1头文件
先用头文件写好咱们的大纲,然后在来一一实现这些代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| #include <stdio.h> #include <stdlib.h> #include <assert.h> #include <stdbool.h>
typedef int STDataType; typedef struct Stack { STDataType* a; int top; int capacity; }Stack;
void StackInit(Stack* ps);
void StackDestroy(Stack* ps);
void StackPush(Stack* ps, STDataType data);
void StackPop(Stack* ps);
STDataType StackTop(Stack* ps);
int StackSize(Stack* ps);
bool StackEmpty(Stack* ps);
void StackPrint(Stack* ps);
|
和顺序表不同的是,我们需要写一个单独的函数来获取栈顶元素,这一点在很多OJ题目中都需要用到。
3.2函数实现
如果你看过了我之前顺序表的博客,这部分对你来说想必不难。
如果有什么问题,欢迎在评论区提出!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
| #include"Stack.h"
void StackInit(Stack* ps) { STDataType* new = (STDataType*)malloc(4*sizeof(STDataType) ); if (new == NULL) exit(-1); else { ps->a = new; ps->top = 0; ps->capacity = 4; } }
void StackDestroy(Stack* ps) { assert(ps);
free(ps->a); ps->a = NULL;
ps->capacity = 0; ps->top = 0; }
void StackPush(Stack* ps, STDataType data) { assert(ps); if (ps->top == ps->capacity) { STDataType* new = (STDataType*)realloc(ps->a, sizeof(STDataType) * (ps->capacity) * 2); if (new == NULL) { exit(-1); } else { ps->a = new; ps->capacity *= 2; } } ps->a[ps->top] = data; ps->top++; }
void StackPop(Stack* ps) { assert(ps); if (ps->top > 0) (ps->top)--; }
STDataType StackTop(Stack* ps) { assert(ps); assert(ps->top > 0); return ps->a[ps->top - 1];
}
int StackSize(Stack* ps) { assert(ps); return ps->top; }
bool StackEmpty(Stack* ps) { assert(ps); if (ps->top == 0) return true; else return false; }
void StackPrint(Stack* ps) { assert(ps); int n = ps->top; for (int i = 0; i < n; i++) { printf("%d ", ps->a[i]); } printf("\n"); }
|
大家在自己编写这种带多个板块的代码的时候,一定要一个板块写完就检查一遍!不要将问题都丢一块解决,那样会非常难受的!
在其他版块都确认无误之后,也要过来瞅一眼Destroy板块,避免出现free错误等情况。
这个板块我们可以通过打断点(VS快捷键F9)并调试的方法来检查该板块是否正确
4.知识巩固,来道OJ!
leetcode 20 有效的括号
leetcode:20. 有效的括号
这道题就是一道非常使用用栈来解决的OJ题
题目要求我们判断给出的字符串中,括号是否一一对应
具体要怎么做呢?思路如下:
- 用一个指针来遍历字符串,如果是左括号
{([
其中一个,就入栈 - 如果是右边括号,说明左括号已经入栈完毕,开始比对
- 栈顶的括号一定是和当前右括号匹配的,如果不是则为false
- 每判断一次,就让栈顶的左括号出栈一次
最后的函数实现如下
需要注意的是,STDataType类型需要更改为char类型,此时存放的是括号字符,并不是数字
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
| typedef char STDataType; typedef struct Stack { STDataType* a; int top; int capacity; }Stack;
void StackInit(Stack* ps) { STDataType* new = (STDataType*)malloc(sizeof(STDataType) * 4); if (new == NULL) { exit(-1); } else { ps->a = new; ps->top = 0; ps->capacity = 4; } }
void StackDestroy(Stack* ps) { assert(ps);
free(ps->a); ps->a = NULL;
ps->capacity = 0; ps->top = 0; }
void StackPush(Stack* ps, STDataType data) { assert(ps); if (ps->top == ps->capacity) { STDataType* new = (STDataType*)realloc(ps->a, sizeof(STDataType) * (ps->capacity) * 2); if (new == NULL) { exit(-1); } else { ps->a = new; ps->capacity *= 2; } } ps->a[ps->top] = data; ps->top++; }
void StackPop(Stack* ps) { assert(ps); if (ps->top > 0) (ps->top)--; }
STDataType StackTop(Stack* ps) { assert(ps); assert(ps->top > 0); return ps->a[ps->top - 1];
}
bool StackEmpty(Stack* ps) { assert(ps); if (ps->top == 0) return true; else return false; }
bool isValid(char * s){ Stack st; StackInit(&st);
while(*s) { if(*s=='{'||*s=='['||*s=='(') { StackPush(&st,*s); s++; } else { if(StackEmpty(&st)) { return false; }
char top=StackTop(&st); StackPop(&st);
if((*s=='}'&&top=='{') ||(*s==')'&&top=='(') ||(*s==']'&&top=='[')) { s++; } else { StackDestroy(&st);
return false; }
} } bool ret=StackEmpty(&st);
StackDestroy(&st); return ret; }
|
这个算法的用时还是非常短的!
结语
数据结构学到这里,其实在了解完这些结构的真正思路后,代码的实现反而并不是那么重要。
在学习这部分知识的时候,一定要多画图!
如果你不适应用鼠标画图,可以直接用纸笔画,一些非常简单的草图,也能极大帮助我们理解当前的代码,找出问题👍