1.栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端 称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。 压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。 出栈:栈的删除操作叫做出栈。出数据也在栈顶。
栈一般用数组实现,因为数组在尾上插入数据的代价比较小,top一般指向尾元素的下一个。
2.队列
队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出 FIFO(First In First Out)。
入队列:进行插入操作的一端称为队尾 出队列:进行删除操作的一端称为队头
队列也可以数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数 组头上出数据,效率会比较低。
OJ题目
给定一个只包括 '('
,')'
,'{'
,'}'
,'['
,']'
的字符串 s
,判断字符串是否有效。
有效字符串需满足:
- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。
- 每个右括号都有一个对应的相同类型的左括号。
解法:使用栈来解决,因为每次进去的右括号会与最早进去的左括号去试图匹配。因此左括号全部进栈,而右括号进去,若栈空,返回false,若栈顶不是相匹配的左括号,返回false。若正好匹配,则将该左括号出战即可。
#define INITSIZE 3
#define ADDSIZE 2
// 支持动态增长的栈
typedef char STDataType;
typedef struct Stack
{
STDataType* arr;
int top; // 栈顶
int capacity; // 容量
}Stack;
void StackInit(Stack* ps)
{
ps->arr = (STDataType*)malloc(sizeof(STDataType) * INITSIZE);
if (ps->arr == NULL)
{
perror("malloc");
exit(EXIT_FAILURE);
}
ps->top = 0; //top指向栈顶的下一个元素
ps->capacity = INITSIZE;
}
static void checkFull(Stack* ps)
{
if (ps->top == ps->capacity)
{
STDataType* tmp = (STDataType*)realloc(ps->arr, sizeof(STDataType) * (ps->capacity + ADDSIZE));
if (tmp == NULL)
{
perror("realloc");
exit(EXIT_FAILURE);
}
ps->arr = tmp;
ps->capacity = ps->capacity + ADDSIZE;
}
}
void StackPush(Stack* ps, STDataType data)
{
checkFull(ps);
assert(ps);
ps->arr[ps->top] = data;
ps->top++;
}
void StackPop(Stack* ps)
{
assert(ps);
assert(ps->top >0);
ps->top--;
}
STDataType StackTop(Stack* ps)
{
assert(ps);
assert(ps->top > 0);
return ps->arr[ps->top-1];
}
int StackSize(Stack* ps)
{
assert(ps);
return ps->top;
}
int StackEmpty(Stack* ps)
{
assert(ps);
if (ps->top != 0)
{
return false;
}
return true;
}
void StackDestroy(Stack* ps)
{
assert(ps);
free(ps->arr);
ps->arr = NULL;
ps->capacity = ps->top = 0;
}
bool isValid(char * s){
Stack st;
StackInit(&st);
char topval;
while(*s)
{
switch(*s)
{
case '(':
case '{':
case '[':
StackPush(&st,*s);
break;
case ')':
if(StackEmpty(&st))
{
StackDestroy(&st);
return false;
}
if(StackTop(&st)=='(')
StackPop(&st);
else
{
StackDestroy(&st);
return false;
}
break;
case ']':
if(StackEmpty(&st))
{
StackDestroy(&st); return false;
}
if(StackTop(&st)=='[')
StackPop(&st);
else
{
StackDestroy(&st);
return false;
}
break;
case '}':
if(StackEmpty(&st))
{
StackDestroy(&st);
return false;
}
if(StackTop(&st)=='{')
StackPop(&st);
else
{
StackDestroy(&st);
return false;
}
break;
}
s++;
}
bool ret=StackEmpty(&st);
StackDestroy(&st);
return ret;
}
请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push
、top
、pop
和 empty
)。
解法:关键是push和pop操作,我们可以push时将它全部push到一个队列中,下次push依然push到该队列之中,然后需要pop时,先把前n-1个元素push到另一个队列中,再pop剩余那一个元素,再次pop时依然如此,将这些元素前n-1个元素移动到空队列,再pop剩余那一个。
typedef int QDataType;
// 链式结构:表示队列
typedef struct QListNode
{
struct QListNode* _next;
QDataType _data;
}QNode;
// 队列的结构
typedef struct Queue
{
QNode* front;
QNode* rear;
int size;
}Queue;
void QueueInit(Queue* q)
{
assert(q);
q->front = q->rear = NULL;
q->size = 0;
}
void QueuePush(Queue* q, QDataType data)
{
QNode* newnode = (QNode*)malloc(sizeof(QNode));
if (newnode == NULL)
{
perror("malloc");
exit(EXIT_FAILURE);
}
newnode->_data = data;
newnode->_next = NULL;
if (q->front ==NULL)
{
q->front = q->rear = newnode;
}
else
{
q->rear->_next = newnode;
q->rear = newnode;
}
q->size++;
}
void QueuePop(Queue* q)
{
assert(q && q->front);
QNode* next = q->front->_next;
free(q->front);
q->front = next;
if (q->front == NULL)
q->rear = NULL;
q->size--;
}
QDataType QueueFront(Queue* q)
{
assert(q&&q->front);
return q->front->_data;
}
QDataType QueueBack(Queue* q)
{
assert(q && q->front);
return q->rear->_data;
}
int QueueSize(Queue* q)
{
assert(q);
return q->size;
}
int QueueEmpty(Queue* q)
{
assert(q);
if (q->front == NULL)
{
return 1;
}
return 0;
}
void QueueDestroy(Queue* q)
{
assert(q);
QNode* cur = q->front;
QNode* next;
while (cur)
{
next = cur->_next;
free(cur);
cur = next;
}
q->front = q->rear = NULL;
q->size = 0;
}
typedef struct {
Queue q1;
Queue q2;
} MyStack;
MyStack* myStackCreate() {
MyStack *st=(MyStack*)malloc(sizeof(MyStack));
QueueInit(&(st->q1));
QueueInit(&(st->q2));
return st;
}
void myStackPush(MyStack* obj, int x) {
if(!QueueEmpty(&obj->q1))
{
QueuePush(&obj->q1,x);
}
else
{
QueuePush(&obj->q2,x);
}
}
int myStackPop(MyStack* obj) {
QNode* cur = QueueEmpty(&obj->q1) ? obj->q2.front : obj->q1.front;
QNode* tail = QueueEmpty(&obj->q1) ? obj->q2.rear : obj->q1.rear;
//这里两个队列只有存放 队列地址传入进去才能改变q1,q2
Queue *NullQueue = QueueEmpty(&obj->q1) ? &obj->q1 : &obj->q2;
Queue *dataQueue = QueueEmpty(&obj->q1) ? &obj->q2 : &obj->q1;
int tmp = QueueBack(dataQueue);
while (cur != tail)
{
cur = cur->_next;
QueuePush(NullQueue, QueueFront(dataQueue));
QueuePop(dataQueue);
}
QueuePop(dataQueue);
return tmp;
}
int myStackTop(MyStack* obj) {
Queue *dataQueue = QueueEmpty(&obj->q1) ? &obj->q2 : &obj->q1;
return QueueBack(dataQueue);
}
bool myStackEmpty(MyStack* obj) {
return QueueEmpty(&obj->q1)&&QueueEmpty(&obj->q2);
}
void myStackFree(MyStack* obj) {
QueueDestroy(&obj->q1);
QueueDestroy(&obj->q2);
free(obj);
}
/**
* Your MyStack struct will be instantiated and called as such:
* MyStack* obj = myStackCreate();
* myStackPush(obj, x);
* int param_2 = myStackPop(obj);
* int param_3 = myStackTop(obj);
* bool param_4 = myStackEmpty(obj);
* myStackFree(obj);
*/
我们将两个栈分为一个pop栈,一个push栈,每次加入元素总是加入push栈中,删除元素总是在pop栈之中,当pop栈为空,那么将push栈元素全部给pop栈即可。
#define INITSIZE 3
#define ADDSIZE 2
// 支持动态增长的栈
typedef char STDataType;
typedef struct Stack
{
STDataType* arr;
int top; // 栈顶
int capacity; // 容量
}Stack;
void StackInit(Stack* ps)
{
ps->arr = (STDataType*)malloc(sizeof(STDataType) * INITSIZE);
if (ps->arr == NULL)
{
perror("malloc");
exit(EXIT_FAILURE);
}
ps->top = 0; //top指向栈顶的下一个元素
ps->capacity = INITSIZE;
}
static void checkFull(Stack* ps)
{
if (ps->top == ps->capacity)
{
STDataType* tmp = (STDataType*)realloc(ps->arr, sizeof(STDataType) * (ps->capacity + ADDSIZE));
if (tmp == NULL)
{
perror("realloc");
exit(EXIT_FAILURE);
}
ps->arr = tmp;
ps->capacity = ps->capacity + ADDSIZE;
}
}
void StackPush(Stack* ps, STDataType data)
{
checkFull(ps);
assert(ps);
ps->arr[ps->top] = data;
ps->top++;
}
void StackPop(Stack* ps)
{
assert(ps);
assert(ps->top >0);
ps->top--;
}
STDataType StackTop(Stack* ps)
{
assert(ps);
assert(ps->top > 0);
return ps->arr[ps->top-1];
}
int StackSize(Stack* ps)
{
assert(ps);
return ps->top;
}
int StackEmpty(Stack* ps)
{
assert(ps);
if (ps->top != 0)
{
return false;
}
return true;
}
void StackDestroy(Stack* ps)
{
assert(ps);
free(ps->arr);
ps->arr = NULL;
ps->capacity = ps->top = 0;
}
typedef struct {
Stack popstack;
Stack pushstack;
} MyQueue;
MyQueue* myQueueCreate() {
MyQueue* q=(MyQueue*)malloc(sizeof(MyQueue));
StackInit(&q->popstack);
StackInit(&q->pushstack);
return q;
}
void myQueuePush(MyQueue* obj, int x) {
StackPush(&obj->pushstack,x);
}
int myQueuePop(MyQueue* obj) {
int ret=0;
//pop栈为空,则将push栈数据全部给pop栈
if(StackEmpty(&obj->popstack))
{
while(!StackEmpty(&obj->pushstack))
{
StackPush(&obj->popstack,StackTop(&obj->pushstack));
StackPop(&obj->pushstack);
}
}
ret= StackTop(&obj->popstack);
StackPop(&obj->popstack);
return ret;
}
int myQueuePeek(MyQueue* obj) {
if(StackEmpty(&obj->popstack))
{
while(!StackEmpty(&obj->pushstack))
{
StackPush(&obj->popstack,StackTop(&obj->pushstack));
StackPop(&obj->pushstack);
}
}
return StackTop(&obj->popstack);
}
bool myQueueEmpty(MyQueue* obj) {
return StackEmpty(&obj->popstack)&&StackEmpty(&obj->pushstack);
}
void myQueueFree(MyQueue* obj) {
StackDestroy(&obj->popstack);
StackDestroy(&obj->pushstack);
free(obj);
}
/**
* Your MyQueue struct will be instantiated and called as such:
* MyQueue* obj = myQueueCreate();
* myQueuePush(obj, x);
* int param_2 = myQueuePop(obj);
* int param_3 = myQueuePeek(obj);
* bool param_4 = myQueueEmpty(obj);
* myQueueFree(obj);
*/
设计你的循环队列实现。 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。
解法:采用顺序表结构,因为这些空间是已经开辟好的(固定大小),同时rear指向的应该是当前插入元素的下一个(否则不好判断其是否为空),同时我们加入一个闲置位,便于我们判断循环链表是否已满。
// 循环队列已经开辟好了固定大小的空间,插入一个值,
// rear往后走(类似栈)如果设计成链表,不好取出队列的尾元素 普通队列rear指向当前数据
typedef struct {
int front;
int rear;
int capacity;
int *elements;
} MyCircularQueue;
MyCircularQueue* myCircularQueueCreate(int k) {
MyCircularQueue *obj = (MyCircularQueue *)malloc(sizeof(MyCircularQueue));
obj->capacity = k + 1;
obj->rear = obj->front = 0;
obj->elements = (int *)malloc(sizeof(int) * obj->capacity);
return obj;
}
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
if ((obj->rear + 1) % obj->capacity == obj->front) {
return false;
}
obj->elements[obj->rear] = value;
obj->rear = (obj->rear + 1) % obj->capacity;
return true;
}
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
if (obj->rear == obj->front) {
return false;
}
obj->front = (obj->front + 1) % obj->capacity;
return true;
}
int myCircularQueueFront(MyCircularQueue* obj) {
if (obj->rear == obj->front) {
return -1;
}
return obj->elements[obj->front];
}
int myCircularQueueRear(MyCircularQueue* obj) {
if (obj->rear == obj->front) {
return -1;
}
return obj->elements[(obj->rear - 1 + obj->capacity) % obj->capacity];
}
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
return obj->rear == obj->front;
}
bool myCircularQueueIsFull(MyCircularQueue* obj) {
return (obj->rear + 1) % obj->capacity == obj->front;
}
void myCircularQueueFree(MyCircularQueue* obj) {
free(obj->elements);
free(obj);
}