赞
踩
栈 :一种特殊的线性表,其中只允许在固定的一端进行插入和删除元素。进行数据插入和删除操作的一段称为栈顶,另一端称为栈底 。栈中的元素遵循先进后出的原则。
压栈:栈的插入操作叫做 进栈/入栈/压栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈,出数据也在栈顶。
栈的实现一般可以使用数组或者链表,但是数组的结构要更优一点。因为数组在尾上插入数据的代价比较小。
其实两种数据结构都可以实现链表,但是为什么选择数组呢
因为由于栈的特性,使得如果使用链表,那么栈顶也就是出入元素的位置在链表的末尾,每次入栈、出栈都要从头节点遍历整个链表。
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<stdlib.h> #include<assert.h> typedef int Datatype; typedef struct Stack { Datatype* a; int top; int Capacity; }Stack; void StackInit(Stack* p); //初始化栈 void StackPush(Stack* p, Datatype x); //入栈操作 void StackPop(Stack* p); //出栈操作 Datatype StackTop(Stack* p); //返回栈顶元素的值 int StackEmpty(Stack* p); //检查栈是否为空 void StackDestroy(Stack* p); //销毁栈 void StackPrint(Stack* p); //打印栈的元素
以上时栈的结构和具体的基本操作
下面是这些具体操作的实现
#define _CRT_SECURE_NO_WARNINGS 1 #include"stack.h" void StackInit(Stack* p) { assert(p); p->a = (Datatype *)malloc(4*sizeof(Datatype)); p->top = 0; p->Capacity = 4; } void StackPush(Stack* p, Datatype x) { assert(p); if (p->top == p->Capacity) //检查是否要增容 { p->a = (Datatype *)realloc(p->a,2 * p->Capacity*sizeof(Datatype)); p->Capacity = p->Capacity * 2; } p->a[p->top] = x; p->top++; } void StackPop(Stack* p) { assert(p); if(p->top) p->top = p -> top - 1; } void StackPrint(Stack* p) { assert(p); for (int i = 0; i < p->top; i++) { printf(" %d ", p->a[i]); } printf("\n"); } Datatype StackTop(Stack* p) { return p->a[p->top - 1]; } int StackEmpty(Stack* p) { return p->top == 0; } void StackDestroy(Stack* p) { free(p->a); p->a = NULL; p->top = 0; p->Capacity = 0; }
//上面要引用栈的实现和相应实现的函数,为了能更清晰的看判断函数,我就不在前面复制了 bool isValid(char * s){ Stack ps; StackInit(&ps); while(*s) { if(*s=='(' || *s=='{' || *s=='[') { StackPush(&ps,*s); s++; } else { if(StackEmpty(&ps)) return false; char top=StackTop(&ps); StackPop(&ps); if((top=='[' && *s!=']')||(top=='{' && *s!='}')||(top=='(' && *s!=')')) return false; s++; } } if(StackEmpty(&ps)) return true; return false; }
这题的思路是:
但是还是要注意一些特殊情况:
leetcode-用栈实现队列
用队列实现栈主要的难点是在 出栈 和 返回栈顶元素 两个操作上
但是两个函数思路是一摸一样的
以 出栈函数 myQueuePop函数为例
这里为了实现队列,我们要创建两个栈来相互倒来将队首元素拿出,具体操作如下:
首先将s1栈的栈顶元素入到s2栈中,然后将栈顶元素出栈。重复上述操作直到s1栈为空。
这时整个栈的元素相当于逆置了,队首元素现在在栈顶的位置,下面的操作在 myQueuePop和myQueuePeek操作略有不同
- myQueuePop 要把s2栈顶的值出栈
- myQueuePeek 不用把s2的栈顶的值
最后首先将s2栈的栈顶元素入到s1栈中,然后将栈顶元素出栈。重复上述操作直到s2栈为空
代码:
#include<stdio.h> #include<stdlib.h> #include<assert.h> typedef int Datatype; typedef struct Stack { Datatype* a; int top; int Capacity; }Stack; void StackInit(Stack* p) { assert(p); p->a = (Datatype*)malloc(4 * sizeof(Datatype)); p->top = 0; p->Capacity = 4; } void StackPush(Stack* p, Datatype x) { assert(p); if (p->top == p->Capacity) //检查是否要增容 { p->a = (Datatype*)realloc(p->a, 2 * p->Capacity * sizeof(Datatype)); p->Capacity = p->Capacity * 2; } p->a[p->top] = x; p->top++; } void StackPop(Stack* p) { assert(p); if (p->top) p->top = p->top - 1; } void StackPrint(Stack* p) { assert(p); for (int i = 0; i < p->top; i++) { printf(" %d ", p->a[i]); } printf("\n"); } Datatype StackTop(Stack* p) { return p->a[p->top - 1]; } int StackEmpty(Stack* p) { return p->top == 0; } void StackDestroy(Stack* p) { free(p->a); p->a = NULL; p->top = 0; p->Capacity = 0; } //以上都是栈的基本操作,下面才是队列的实现 typedef struct { Stack s1; Stack s2; } MyQueue; /** Initialize your data structure here. */ MyQueue* myQueueCreate() { MyQueue* q=(MyQueue*)malloc(sizeof(MyQueue)); StackInit(&(q->s1)); StackInit(&(q->s2)); return q; } /** Push element x to the back of queue. */ void myQueuePush(MyQueue* obj, int x) { int a = 0; StackPush(&(obj->s1), x); } /** Removes the element from in front of queue and returns that element. */ int myQueuePop(MyQueue* obj) { while (StackEmpty(&(obj->s1))==0) { StackPush(&(obj->s2), StackTop(&(obj->s1))); StackPop(&(obj->s1)); } Datatype a = StackTop(&(obj->s2)); StackPop(&(obj->s2)); while (StackEmpty(&(obj->s2))==0) { StackPush(&(obj->s1), StackTop(&(obj->s2))); StackPop(&(obj->s2)); } return a; } /** Get the front element. */ int myQueuePeek(MyQueue* obj) { while (StackEmpty(&(obj->s1)) == 0) { StackPush(&(obj->s2), StackTop(&(obj->s1))); StackPop(&(obj->s1)); } Datatype a = StackTop(&(obj->s2)); while (StackEmpty(&(obj->s2)) == 0) { StackPush(&(obj->s1), StackTop(&(obj->s2))); StackPop(&(obj->s2)); } return a; } /** Returns whether the queue is empty. */ bool myQueueEmpty(MyQueue* obj) { return StackEmpty(&(obj->s1)); } void myQueueFree(MyQueue* obj) { StackDestroy(&(obj->s1)); StackDestroy(&(obj->s2)); obj = NULL; }
队列: 只允许在一段进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(first in first out) 入队列、进行插入操作的一端称为队尾 出队列、进行删除的一段称为队头
队列的实现同样可以使用数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<assert.h> #include<stdlib.h> typedef int DataType; typedef struct BinaryTree { DataType x; struct BinaryTree* _left; struct BinaryTree* _right; }BT; typedef BT* Datatype; typedef struct ListNode { Datatype x; struct ListNode* next; }ListNode; typedef struct Queue { ListNode* head; ListNode* tail; }Queue; void QueueInit(Queue* q); //初始化队列 void QueuePush(Queue* q, Datatype x); // 入队 void QueuePop(Queue* q); // 出队 Datatype QueueFront(Queue* q); //输出队列队头的元素 Datatype QueueBack(Queue* q); // 输出队列队尾的元素 int QueueSize(Queue* q); //输出队列的大小 int QueueEmpty(Queue* q); //判断队列是否为空 void QueueDestroy(Queue* q); //队列销毁
以上时栈的结构和具体的基本操作
下面是这些具体操作的实现
#define _CRT_SECURE_NO_WARNINGS 1 #include"Queue.h" void QueueInit(Queue* q) { ListNode* First = (ListNode*)malloc(sizeof(ListNode)); q->head = q->tail = First; q->tail->next = NULL; } void QueuePush(Queue* q, Datatype x) { assert(q); q->tail->x = x; ListNode* NewNode = (ListNode*)malloc(sizeof(ListNode)); q->tail->next = NewNode; q->tail = NewNode; q->tail->next = NULL; } void QueuePop(Queue* q) { assert(q); assert(!QueueEmpty(q)); if (!QueueEmpty(q)) { ListNode* temp = q->head->next; free(q->head); q->head = temp; } } Datatype QueueFront(Queue* q) { assert(q); assert(!QueueEmpty(q)); return q->head->x; } Datatype QueueBack(Queue* q) { assert(q); assert(!QueueEmpty(q)); ListNode* cur = q->head; while (cur->next != q->tail) { cur = cur->next; } return cur->x; } int QueueSize(Queue* q) { if (QueueEmpty(q)) { return 0; } ListNode* cur = q->head; int k = 0; while (cur != q->tail) { cur = cur->next; k++; } return k; } int QueueEmpty(Queue* q) { return q->head == q->tail; } void QueueDestroy(Queue* q) { ListNode* cur = q->head; while (cur != q->tail) { ListNode* temp = cur->next; free(cur); cur = temp; } free(cur); cur = NULL; q->head = NULL; q->tail = NULL; }
leetcode——原题
实现思路: 用两个队列来实现栈的功能,栈用队列的实现不同主要在myStackPop这个函数上,该如何得到队尾的元素是本题的难点:
以上就是我处理的思路:
#include<stdio.h> #include<assert.h> #include<stdlib.h> typedef int Datatype; typedef struct listNode { Datatype x; struct listNode* next; }ListNode; typedef struct { ListNode* head; ListNode* tail; }Queue; void QueueInit(Queue* q) { ListNode* First = (ListNode*)malloc(sizeof(ListNode)); First->next = NULL; q->head = q->tail = First; q->tail->next = NULL; } int QueueEmpty(Queue* q) { return q->head == q->tail; } void QueuePush(Queue* q, Datatype x) { assert(q); q->tail->x = x; ListNode* NewNode = (ListNode*)malloc(sizeof(ListNode)); q->tail->next = NewNode; q->tail = NewNode; q->tail->next = NULL; } void QueuePop(Queue* q) { assert(q); assert(!QueueEmpty(q)); if (!QueueEmpty(q)) { ListNode* temp = q->head->next; free(q->head); q->head = temp; } } Datatype QueueFront(Queue* q) { assert(q); assert(!QueueEmpty(q)); return q->head->x; } Datatype QueueBack(Queue* q) { assert(q); assert(!QueueEmpty(q)); ListNode* cur = q->head; while (cur->next != q->tail) { cur = cur->next; } return cur->x; } int QueueSize(Queue* q) { if (QueueEmpty(q)) { return 0; } ListNode* cur = q->head; int k = 0; while (cur != q->tail) { cur = cur->next; k++; } return k; } void QueueDestroy(Queue* q) { ListNode* cur = q->head; while (cur != q->tail) { ListNode* temp = cur->next; free(cur); cur = temp; } free(cur); cur = NULL; q->head = NULL; q->tail = NULL; } void check(Queue* q1, Queue* q2) { if (QueueEmpty(q1)) { Queue temp = *q1; *q1 = *q2; *q2 = temp; } } typedef struct { Queue q1; Queue q2; } MyStack; /** Initialize your data structure here. */ MyStack* myStackCreate() { MyStack* p = (MyStack*)malloc(sizeof(MyStack)); QueueInit(&(p->q1)); QueueInit(&(p->q2)); return p; } /** Push element x onto stack. */ void myStackPush(MyStack* obj, int x) { check(&(obj->q1), &(obj->q2)); QueuePush(&(obj->q1), x); } /** Removes the element on top of the stack and returns that element. */ int myStackPop(MyStack* obj) { check(&(obj->q1), &(obj->q2)); int b = 0; while (!QueueEmpty(&(obj->q1))) { if (QueueSize(&(obj->q1)) == 1) { b = QueueFront(&(obj->q1)); QueuePop(&(obj->q1)); continue; } int a = QueueFront(&(obj->q1)); QueuePop(&(obj->q1)); QueuePush(&(obj->q2), a); } return b; } /** Get the top element. */ int myStackTop(MyStack* obj) { check(&(obj->q1), &(obj->q2)); return QueueBack(&(obj->q1)); } /** Returns whether the stack is empty. */ bool myStackEmpty(MyStack* obj) { check(&(obj->q1), &(obj->q2)); return QueueEmpty(&(obj->q1)); } void myStackFree(MyStack* obj) { QueueDestroy(&(obj->q1)); QueueDestroy(&(obj->q2)); free(obj); }
循环队列由一个队头(Front)偏移量和一个和一个队尾偏移量(Rear)和一个顺序表组成,在逻辑上是一个环形结构。但在物理存储结构上实际是一个顺序表,靠着一定的条件使得Front和Rear偏移量循环遍历数组。
逻辑结构:
实际上的 物理结构:
如何判断队列空 或 队列的满
如果我们仔细思考一下就会发现 队空 和队满 两种情况都是Rear和Front都是重合的,如果用Rear==Front
就无法判断队空 和队满
于是我们就用添加一个多余的空间(K+1)来解决这个问题
当Rear==Front时,这时我们就认为循环队列是空的
由于Rear始终指向的是队尾元素的下一个空间,所以设置一个空节点使队满的时候,Rear和Front不重合但是,满足条件Rear+1==Front
,但是这里的判断条件实际上在写的过程中要写成((obj->Rear+1)%(obj->capacity))==obj->Front
因为Rear+1可能要越过数组,所以要将他返回数组的起点以达到循环。
typedef struct { int *a; int Front; int Rear; int capacity; } MyCircularQueue; MyCircularQueue* myCircularQueueCreate(int k) { MyCircularQueue*q=(MyCircularQueue*)malloc(sizeof(MyCircularQueue)); q->a=(int *)malloc((k+1)*sizeof(int)); q->Front=0; q->Rear=0; q->capacity=k+1; return q; } bool myCircularQueueIsFull(MyCircularQueue* obj) { return ((obj->Rear+1)%(obj->capacity))==obj->Front; } bool myCircularQueueIsEmpty(MyCircularQueue* obj) { return ((obj->Front)%(obj->capacity))==obj->Rear; } bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) { if(myCircularQueueIsFull(obj)) //检查队列是否满了 return false; obj->a[obj->Rear]=value; obj->Rear++; obj->Rear%=obj->capacity; return true; } bool myCircularQueueDeQueue(MyCircularQueue* obj) { if(myCircularQueueIsEmpty(obj)) return false; obj->Front++; obj->Front%=obj->capacity; 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; return obj->a[(obj->Rear - 1+obj->capacity)%obj->capacity]; } void myCircularQueueFree(MyCircularQueue* obj) { free(obj->a); free(obj); obj=NULL; }
下面来分享一下我在写这个oj的时候遇到的问题:
a[(obj->Rear - 1]
但是在这里要判断Rear-1是否满足循环数组的要求,如果不检查就会一直报heap—overflowCopyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。