目录
一、原题链接:
二、有效的括号:
编辑代码实现:
三、用队列实现栈:
四、用栈实现队列:
五、设计循环队列:
六、读书分享:
一、原题链接:
20. 有效的括号
225. 用队列实现栈
232. 用栈实现队列
622. 设计循环队列
二、有效的括号:
这种题大家应该在上数据结构课时应该就听过吧(括号匹配)。对于这个题,我们可以使用栈实现。
当栈顶元素与将要入栈的元素形成一个完整的括号时,就让栈顶元素出栈。直到把所有括号遍历完,栈里没有剩余元素时,就是有效的括号。
代码实现:
//手动实现一个栈
typedef char StackDataType;
typedef struct Stack
{
StackDataType* _a;
int _top;
int _capacity;
}Stack;
void StackInit(Stack* pst);
void StackDestory(Stack* pst);
void StackPush(Stack* pst, StackDataType x);
void StackPop(Stack* pst);
int StackSize(Stack* pst);
bool StackEmpty(Stack* pst);
StackDataType StackTop(Stack* pst);
void StackInit(Stack* pst)
{
assert(pst);
pst->_a = (StackDataType*)malloc(sizeof(StackDataType) * 4);
pst->_top = 0;
pst->_capacity = 4;
}
void StackDestory(Stack* pst)
{
assert(pst);
free(pst->_a);
pst->_a = NULL;
pst->_top = 0;
pst->_capacity = 0;
}
void StackPush(Stack* pst, StackDataType x)
{
assert(pst);
if (pst->_top == pst->_capacity)
{
pst->_capacity *= 2;
StackDataType* tmp = (StackDataType*)realloc(pst->_a, sizeof(StackDataType) * pst->_capacity);
if (tmp == NULL)
{
printf("内存不足\n");
exit(-1);
}
else
{
pst->_a = tmp;
}
}
pst->_a[pst->_top] = x;
pst->_top++;
}
void StackPop(Stack* pst)
{
assert(pst);
assert(pst->_top > 0);
--pst->_top;
}
int StackSize(Stack* pst)
{
assert(pst);
return pst->_top;
}
bool StackEmpty(Stack* pst)
{
assert(pst);
return pst->_top == 0 ? 1 : 0;
}
StackDataType StackTop(Stack* pst)
{
assert(pst);
assert(pst->_top > 0);
return pst->_a[pst->_top - 1];
}
//有效的括号
bool isValid(char* s) {
Stack st;
StackInit(&st);
bool ret=false;//用于返回最终结果
while(*s!='\0')
{
if(*s=='('||*s=='['||*s=='{')//前括号入栈
{
StackPush(&st,*s);
++s;
}
if(*s==')'||*s==']'||*s=='}')//后括号出栈
{
if(StackEmpty(&st))//当栈为空,入栈的是后括号,无法匹配
break; //使用break不使用return是为了防止内存泄漏,因为中途返回不会调用销毁栈的函数
StackDataType Top=StackTop(&st);//取栈顶元素出来匹配
//匹配不成功
if(Top=='('&&*s!=')')
break;
if(Top=='['&&*s!=']')
break;
if(Top=='{'&&*s!='}')
break;
//匹配成功
StackPop(&st);
++s;
}
}
if(*s=='\0')//字符串遍历完
{
ret=StackEmpty(&st);//查看栈内是否还存在元素
}
StackDestory(&st);
return ret;
}
三、用队列实现栈:
要用队列实现栈,我们要用两个队列来实现。
入栈时,把元素放入非空队列(最开始都为空时,随便放一个);
出栈时,把除队尾的所有元素都放入另一个队列里,然后让队尾元素出栈。
每次保证一个队列有数据,一个队列为空。
//手动实现一个队列
typedef int QueueDataType;
typedef struct QueueNode
{
struct QueueNode* _next;
QueueDataType _data;
}QueueNode;
typedef struct Queue
{
QueueNode* _head;
QueueNode* _tail;
}Queue;
void QueueInit(Queue* pq);
void QueueDestory(Queue* pq);
void QueuePush(Queue* pq,QueueDataType x);
void QueuePop(Queue* pq);
QueueDataType QueueFront(Queue* pq);
QueueDataType QueueBack(Queue* pq);
bool QueueEmpty(Queue* pq);
int QueueSize(Queue* pq);
void QueueInit(Queue* pq)
{
assert(pq);
pq->_head = NULL;
pq->_tail = NULL;
}
void QueueDestory(Queue* pq)
{
assert(pq);
QueueDataType* tmp = NULL;
while (pq->_head != NULL)
{
tmp = pq->_head;
pq->_head = pq->_head->_next;
free(tmp);
}
pq->_tail = NULL;
}
void QueuePush(Queue* pq, QueueDataType x)
{
assert(pq);
QueueNode* newNode = (QueueNode*)malloc(sizeof(QueueNode));
if (newNode == NULL)
{
printf("内存不足\n");
exit(-1);
}
newNode->_data = x;
newNode->_next = NULL;
if (pq->_head == NULL)
{
pq->_head = pq->_tail = newNode;
}
else
{
pq->_tail->_next = newNode;
pq->_tail = newNode;
}
}
void QueuePop(Queue* pq)
{
assert(pq);
assert(pq->_head);
QueueNode* next = pq->_head->_next;
free(pq->_head);
pq->_head = next;
if (pq->_head == NULL)
{
pq->_tail = NULL;
}
}
QueueDataType QueueFront(Queue* pq)
{
assert(pq);
assert(pq->_head);
return pq->_head->_data;
}
QueueDataType QueueBack(Queue* pq)
{
assert(pq);
assert(pq->_tail);
return pq->_tail->_data;
}
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->_head == NULL ? 1 : 0;
}
int QueueSize(Queue* pq)
{
assert(pq);
QueueNode* cur = pq->_head;
int size = 0;
while (cur != NULL)
{
size++;
cur = cur->_next;
}
return size;
}
//用两个队列实现栈
typedef struct {
Queue _q1;
Queue _q2;
} MyStack;
MyStack* myStackCreate() {
MyStack* st=(MyStack*)malloc(sizeof(MyStack));//为结构体分配内存
QueueInit(&st->_q1);
QueueInit(&st->_q2);
return st;//后续可以通过st对栈进行操作
}
void myStackPush(MyStack* obj, int x) {
//将数据放入非空队列,适用于两个队列都没有数据时
if(!QueueEmpty(&obj->_q1))
{
QueuePush(&obj->_q1,x);
}
else
{
QueuePush(&obj->_q2,x);
}
}
int myStackPop(MyStack* obj) {
//保证一个队列为空,将除最后一个数据外的其它数据都移到空队列里
Queue* empty=&obj->_q1;
Queue* nonEmpty=&obj->_q2;
if(!QueueEmpty(&obj->_q1))
{
empty=&obj->_q2;
nonEmpty=&obj->_q1;
}
int ret=QueueBack(nonEmpty);//获取非空队列的队尾作为栈顶元素
while(QueueSize(nonEmpty)>1)//将非栈顶元素放入空队列,将非空队列的元素依次出栈,除栈顶元素
{
QueuePush(empty,QueueFront(nonEmpty));
QueuePop(nonEmpty);
}
QueuePop(nonEmpty);//栈顶元素出栈
return ret;
}
int myStackTop(MyStack* obj) {
//非空队列的最后一个元素就是栈顶元素
//题目说了:每次调用 pop 和 top 都保证栈不为空
if(!QueueEmpty(&obj->_q1))
{
return QueueBack(&obj->_q1);
}
else //if(!QueueEmpty(obj->_q2))
{
return QueueBack(&obj->_q2);
}
}
bool myStackEmpty(MyStack* obj) {
if(QueueEmpty(&obj->_q1)&&QueueEmpty(&obj->_q2))
{
return true;
}
return false;
}
void myStackFree(MyStack* obj) {
QueueDestory(&obj->_q1);
QueueDestory(&obj->_q1);
free(obj);
}
栈的相关函数的外部调用:
int main()
{
MyStack* obj = myStackCreate();
myStackPush(obj, 1);
myStackPush(obj, 2);
return 0;
}
四、用栈实现队列:
类比于用队列实现栈,这个题也可以用两个栈实现队列。
我们创建一个插入栈(_pushSt),一个删除栈(_PopSt),入队列就将元素放入_pushSt,出队列就将 _pushSt 里的所有元素都放入 _popSt 后再出栈。
//手动实现一个栈
typedef int StackDataType;
typedef struct Stack
{
StackDataType* _a;
int _top;
int _capacity;
}Stack;
void StackInit(Stack* pst);
void StackDestory(Stack* pst);
void StackPush(Stack* pst, StackDataType x);
void StackPop(Stack* pst);
int StackSize(Stack* pst);
bool StackEmpty(Stack* pst);
StackDataType StackTop(Stack* pst);
void StackInit(Stack* pst)
{
assert(pst);
pst->_a = (StackDataType*)malloc(sizeof(StackDataType) * 4);
pst->_top = 0;
pst->_capacity = 4;
}
void StackDestory(Stack* pst)
{
assert(pst);
free(pst->_a);
pst->_a = NULL;
pst->_top = 0;
pst->_capacity = 0;
}
void StackPush(Stack* pst, StackDataType x)
{
assert(pst);
if (pst->_top == pst->_capacity)
{
pst->_capacity *= 2;
StackDataType* tmp = (StackDataType*)realloc(pst->_a, sizeof(StackDataType) * pst->_capacity);
if (tmp == NULL)
{
printf("内存不足\n");
exit(-1);
}
else
{
pst->_a = tmp;
}
}
pst->_a[pst->_top] = x;
pst->_top++;
}
void StackPop(Stack* pst)
{
assert(pst);
assert(pst->_top > 0);
--pst->_top;
}
int StackSize(Stack* pst)
{
assert(pst);
return pst->_top;
}
bool StackEmpty(Stack* pst)
{
assert(pst);
return pst->_top == 0 ? 1 : 0;
}
StackDataType StackTop(Stack* pst)
{
assert(pst);
assert(pst->_top > 0);
return pst->_a[pst->_top - 1];
}
//用两个栈实现队列
typedef struct {
Stack _pushSt;
Stack _popSt;
} MyQueue;
MyQueue* myQueueCreate() {
MyQueue* q=(MyQueue*)malloc(sizeof(MyQueue));//为队列分配空间
StackInit(&q->_pushSt);
StackInit(&q->_popSt);
return q;
}
void myQueuePush(MyQueue* obj, int x) {
StackPush(&obj->_pushSt,x);//直接将元素压入_pshSt
}
int myQueuePop(MyQueue* obj) {
int ret=myQueuePeek(obj);//由于该操作和获取队头元素操作类似,所以直接调用获取队头元素
StackPop(&obj->_popSt);
return ret;
}
//获取队头元素
int myQueuePeek(MyQueue* obj) {
//直接在_popSt里面拿
if(StackEmpty(&obj->_popSt))//当_popSt里面没有元素
{
while(!StackEmpty(&obj->_pushSt))//当_pushSt里面有元素
{
StackPush(&obj->_popSt,StackTop(&obj->_pushSt));
StackPop(&obj->_pushSt);
}
}
return StackTop(&obj->_popSt);
}
bool myQueueEmpty(MyQueue* obj) {
return StackEmpty(&obj->_pushSt) && StackEmpty(&obj->_popSt);
}
void myQueueFree(MyQueue* obj) {
StackDestory(&obj->_pushSt);
StackDestory(&obj->_popSt);
free(obj);
}
五、设计循环队列:
对于静态循环链表的设计,由于会存在队空与队满情况的冲突(队空与队满时,头指针与尾指针都指向一个位置)
如果没有多余空间:
当多余一个空间:
队满情况:
执行出队操作时的特殊情况:
返回队尾元素,且_rear指向数组头时的情况 :
//静态循环队列
typedef struct {
int* _a;
int _front;
int _rear;
int _k;
} MyCircularQueue;
MyCircularQueue* myCircularQueueCreate(int k) {
MyCircularQueue* q=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
q->_a=(int*)malloc(sizeof(int)*(k+1));//多分配一个空间,方便区别空与满
q->_front=0;
q->_rear=0;
q->_k=k;//记录循环链表的容量
return q;
}
//先声明,方便函数调用
bool myCircularQueueIsEmpty(MyCircularQueue* obj);
bool myCircularQueueIsFull(MyCircularQueue* obj);
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
if(myCircularQueueIsFull(obj))
{
return false;
}
else
{
obj->_a[obj->_rear]=value;
if(obj->_rear==obj->_k)
{
obj->_rear%=obj->_k;//_rear在最后一个位置需要向后移动时,回到数组头部
}
else
{
obj->_rear++;
}
}
return true;
}
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
{
return false;
}
else
{
obj->_front++;
if(obj->_front==obj->_k+1)//如果_front走到了数组最后一个位置的下一个位置时,回到数组头部
{
obj->_front%=obj->_k+1;
}
}
return true;
}
int myCircularQueueFront(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
return -1;
return obj->_a[obj->_front];
}
int myCircularQueueRear(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))//队列内没有元素
return -1;
if(obj->_rear==0)//队尾指针指向在数组的第0号位置,队尾元素应该在数组的最后
return obj->_a[obj->_k];
return obj->_a[obj->_rear-1];
}
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
return obj->_front==obj->_rear;
}
bool myCircularQueueIsFull(MyCircularQueue* obj) {
return (obj->_rear+1)%(obj->_k+1) == obj->_front;
}
void myCircularQueueFree(MyCircularQueue* obj) {
free(obj->_a);
free(obj);
}
六、读书分享:
《道德经·第五十章》:
出生入死。生之徒,十有三;死之徒”,十有三;人之生,动之于死地”,亦十有三。夫何故?以其生生之厚。
盖闻善摄生者", 陆行不遇兕虎,人军不被甲兵"。兕无所投其角”,虎无所用其爪”,兵无所容其刃。夫何故?以其无死地。
解释:
人出世为生,入地为死。属于长寿的,占十分之三;属于短命的,占十分之三;人的过分地奉养生命,妄为而走向死路的,也占了十分之三。为什么呢?因为奉养太过度了。
听说善于养护生命的人,在陆地上行走不会遇到犀牛和老虎,在战争中不会受到杀伤。犀牛用不上它的角,老虎用不上它的爪,兵器用不上它的刃。为什么呢?因为他没有进入死亡的范围。