本文主要是几个OJ题的思路和代码(C实现)
包括:括号匹配问题,用队列实现栈,用栈实现队列,设计循环队列
1.括号匹配问题
题目:给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。有效字符串需满足:左括号必须用相同类型的右括号闭合。左括号必须以正确的顺序闭合。
思路:在栈中存放左括号,遇到右括号,则出栈顶元素,判断栈顶元素是否和当前括号匹配,如果不匹配,则说明不匹配。遍历完所有字符,如果栈为空,则表示完全匹配。
源码:(包括了栈的创建以及操作接口)
typedef char Stackdata;
struct Stack
{
Stackdata* a; //内容
int top; //顶
int capacity; //容量
};
typedef struct Stack Stack;
void StackInit(Stack* ps) // 初始化栈
{
assert(ps);
ps->a = (Stack*)malloc(sizeof(Stack) * 2); //给一个初始的空间方便后续翻倍
ps->capacity = 2; //容量
ps->top = 0; //栈顶
}
void StackDestroy(Stack* ps) // 销毁栈
{
assert(ps);
free(ps->a);
ps->top = ps->capacity = 0;
ps->a = NULL;
}
bool StackEmpty(Stack* ps) // 检测栈是否为空
{
assert(ps);
int a = ps->top;
if(a != 0)
{
return true;
}
else
{
return false;
}
}
void StackPush(Stack* ps, Stackdata data) // 入栈
{
assert(ps);
if (ps->top == ps->capacity)
{
ps->capacity *= 2;
Stack* tmp = (Stack*)realloc(ps->a, sizeof(Stack) * ps->capacity); //用中间变量更安全
if (tmp == NULL)
{
exit(-1);
}
ps->a = tmp;
}
ps->a[ps->top] = data;
ps->top++;
}
void StackPop(Stack* ps) // 出栈
{
assert(ps);
ps->top--;
}
Stackdata StackTop(Stack* ps) // 获取栈顶元素
{
assert(ps);
return ps->a[ps->top - 1]; //-1才是栈顶
}
int StackSize(Stack* ps) // 获取栈中有效元素个数
{
return ps->top == 0;
}
bool isValid(char * s)
{
Stack z;
StackInit(&z);
while(*s)
{
if(*s == '(' || *s == '{' || *s == '[')
{
StackPush(&z, *s);
++s;
}
else
{
if(StackEmpty(&z))
{
char* b = StackTop(&z);
if(b == '('&& *s != ')')
{
StackDestroy(&z);
return false;
}
else if(b == '['&& *s != ']')
{
StackDestroy(&z);
return false;
}
else if(b == '{'&& *s != '}')
{
StackDestroy(&z);
return false;
}
else
{
StackPop(&z);
++s; //访问下一个
}
}
else
{
return false;
}
}
}
int t = z.top;
StackDestroy(&z);
if(t == 0)
return true;
else
return false;
}
2.用队列实现栈
题目:请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通队列的全部四种操作(push、top、pop 和 empty)。实现 MyStack 类:void push(int x) 将元素 x 压入栈顶。int pop() 移除并返回栈顶元素。int top() 返回栈顶元素。boolean empty() 如果栈是空的,返回 true ;否则,返回 false 。
思路:用两个队列去实现一个栈,每次始终保持一个队列为空,入栈操作相当于给非空队列进行入队操作。出栈操作相当于非空队列的队尾元素出队,此时需要把非空队列除最后一个元素之外的其余元素入队到空队列,然后出队最后一个队尾元素。
源码:(包括了队列的创建以及操作接口)
typedef int QueueData;
struct QueueNode //链式结构:表示队列 (被操作的)
{
struct QueueNode* next;
QueueData val;
};
typedef struct QueueNode QueueN;
struct Queue // 队列的结构(头尾指针)
{
struct QueueNode* front; //要指向节点,所以结构是struct QueueNode*
struct QueueNode* back;
};
typedef struct Queue Queue;
QueueN* BuyQueueNode(QueueData x) //创建节点
{
QueueN* ret = (Queue*)malloc(sizeof(QueueN));
if (ret == NULL)
{
exit(-1);
}
ret->val = x;
ret->next = NULL;
return ret;
}
void QueueInit(Queue* q) // 初始化队列
{
assert(q);
q->back = NULL;
q->front = NULL;
}
void QueueDestroy(Queue* q) // 销毁队列
{
assert(q);
Queue* cur = q;
while (cur->front) //需要判断cur->front,cur永远不会为空
{
Queue* Next = cur->front->next;
free(cur->front);
cur->front = Next;
}
q->back = q->front = NULL;
}
void QueuePush(Queue* q, QueueData data) // 队尾入队列
{
assert(q);
QueueN* cur = BuyQueueNode(data);
if (q->front == NULL)
{
q->back = q->front = cur;
}
else //队尾进,对首出
{
q->back->next = cur;
q->back = cur;
}
}
void QueuePop(Queue* q) // 队头出队列
{
assert(q);
if (q->front == NULL)
{
return;
}
QueueN* Next = q->front->next;
free(q->front);
q->front = Next;
}
QueueData QueueFront(Queue* q) // 获取队列头部元素
{
assert(q);
if (q->front == NULL) //不用判断,为空时,返回空
return;
return q->front->val;
}
QueueData QueueBack(Queue* q) // 获取队列队尾元素
{
assert(q);
if (q->back == NULL)
return;
return q->back->val;
}
int QueueSize(Queue* q) // 获取队列中有效元素个数
{
assert(q);
int count = 0;
QueueN* cur = q->front;
while (cur)
{
cur = cur->next;
count++;
}
return count;
}
bool QueueEmpty(Queue* q) // 检测队列是否为空
{
return q->front == NULL;
}
typedef struct { //栈是由两个队列组成,在此定义
Queue q1;
Queue q2;
} MyStack;
/** Initialize your data structure here. */
MyStack* myStackCreate() {
MyStack* ret = (MyStack*)malloc(sizeof(MyStack)); //不能用MyStack ret 因为这样定义的是局部变量,要开辟空间。
QueueInit(&ret->q1); //初始化队列
QueueInit(&ret->q2);
return ret;
}
/** Push element x onto stack. */
void myStackPush(MyStack* obj, int x) { //压入不是空的那个队列
if(!QueueEmpty(&obj->q1))
{
QueuePush(&obj->q1, x);
}
else
{
QueuePush(&obj->q2, x);
}
}
/** Removes the element on top of the stack and returns that element. */
int myStackPop(MyStack* obj) {
Queue* pEmpty = &obj->q1, *pNonEmpty = &obj->q2;
if(!QueueEmpty(&obj->q1))
{
pEmpty = &obj->q2;
pNonEmpty = &obj->q1;
}
while(QueueSize(pNonEmpty) > 1)
{
QueuePush(pEmpty, QueueFront(pNonEmpty));
QueuePop(pNonEmpty);
}
// printf("%d\n",QueueBack(pNonEmpty));
int top = QueueFront(pNonEmpty);
//队尾元素出队
QueuePop(pNonEmpty);
return top;
}
/** Get the top element. */
int myStackTop(MyStack* obj) { //直接返回栈顶
if(!QueueEmpty(&obj->q1))
{
return QueueBack(&obj->q1);
}
else
{
return QueueBack(&obj->q2);
}
}
/** Returns whether the stack is empty. */
bool myStackEmpty(MyStack* obj) {
return QueueEmpty(&obj->q1)&&QueueEmpty(&obj->q2); //直接返回
}
void myStackFree(MyStack* obj) {
QueueDestroy(&obj->q1);
QueueDestroy(&obj->q2);
free(obj);
}
3.用栈实现队列
题目:请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty):实现 MyQueue 类:void push(int x) 将元素 x 推到队列的末尾,int pop() 从队列的开头移除并返回元素,int peek() 返回队列开头的元素,boolean empty() 如果队列为空,返回 true ;否则,返回 false。
思路:一个栈进行入队操作,另一个栈进行出队操作。出队操作: 当出队的栈不为空是,直接进行出栈操作,如果为空,需要把入队的栈元素全部导入到出队的栈,然后再进行出栈操作。
源码:(包括了栈的创建以及操作接口)
#define DEFSTACKSIZE 100
typedef int STDataType;
struct Stack
{
STDataType* array; //内容
int size; //顶
int capacity; //容量
};
typedef struct Stack Stack;
void CheckCapacity(Stack* ps)
{
if (ps->size >= ps->capacity)
{
ps->capacity *= 2;
ps->array = (STDataType *)realloc(ps->array, ps->capacity * sizeof(STDataType));
}
}
void StackInit(Stack* ps)
{
ps->array = (STDataType *)calloc(DEFSTACKSIZE, sizeof(STDataType));
ps->capacity = DEFSTACKSIZE;
ps->size = 0;
}
void StackPush(Stack* ps, STDataType x)
{
CheckCapacity(ps);
ps->array[ps->size] = x;
ps->size++;
}
void StackPop(Stack* ps)
{
if (ps->size == 0)
{
return;
}
ps->size--;
}
STDataType StackTop(Stack* ps)
{
if (ps->size == 0)
{
return (STDataType)0;
}
return ps->array[ps->size - 1];
}
int StackEmpty(Stack* ps)
{
return ps->size == 0;
}
int StackSize(Stack* ps)
{
return ps->size;
}
void StackDestory(Stack* ps)
{
if (ps->array)
{
free(ps->array);
ps->array = NULL;
ps->size = 0;
ps->capacity = 0;
}
}
typedef struct {
Stack PushS;
Stack PopS;
} MyQueue;
/** Initialize your data structure here. */
MyQueue* myQueueCreate() {
MyQueue* ret = (MyQueue*)malloc(sizeof(MyQueue));
StackInit(&ret->PushS);
StackInit(&ret->PopS);
return ret;
}
/** Push element x to the back of queue. */
void myQueuePush(MyQueue* obj, int x) {
StackPush(&obj->PushS, x);
}
/** Removes the element from in front of queue and returns that element. */
int myQueuePop(MyQueue* obj) {
if(StackEmpty(&obj->PopS))
{
while(!StackEmpty(&obj->PushS))
{
StackPush(&obj->PopS, StackTop(&obj->PushS));
StackPop(&obj->PushS);
}
}
int top = StackTop(&obj->PopS);
StackPop(&obj->PopS);
return top;
}
/** Get the front element. */
int myQueuePeek(MyQueue* obj) {
if(StackEmpty(&obj->PopS))
{
while(!StackEmpty(&obj->PushS))
{
StackPush(&obj->PopS, StackTop(&obj->PushS));
StackPop(&obj->PushS);
}
}
return StackTop(&obj->PopS);
}
/** Returns whether the queue is empty. */
bool myQueueEmpty(MyQueue* obj) {
return StackEmpty(&obj->PushS)&&StackEmpty(&obj->PopS);
}
void myQueueFree(MyQueue* obj) {
StackDestory(&obj->PushS);
StackDestory(&obj->PopS);
free(obj);
}
4.设计循环队列
题目:
设计你的循环队列实现。 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。你的实现应该支持如下操作:MyCircularQueue(k): 构造器,设置队列长度为 k 。Front: 从队首获取元素。如果队列为空,返回 -1 。Rear: 获取队尾元素。如果队列为空,返回 -1 。enQueue(value): 向循环队列插入一个元素。如果成功插入则返回真。deQueue(): 从循环队列中删除一个元素。如果成功删除则返回真。isEmpty(): 检查循环队列是否为空。isFull(): 检查循环队列是否已满。
思路:通过一个定长数组实现循环队列。入队:首先要判断队列是否已满,再进行入队的操作,入队操作需要考虑索引循环的问题,当索引越界,需要让它变成最小值。出队:首先要判断队列是否为空,再进行出队操作,出队也需要考虑索引循环的问题。判空: 队头 == 队尾。判满: 队尾 + 1 == 队头
源码:
typedef struct {
int*a;
int k; //存数据的量
int front; //头
int tail; //尾
} MyCircularQueue;
MyCircularQueue* myCircularQueueCreate(int k) {
MyCircularQueue * obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
obj->a = (int*)malloc(sizeof(int)*(k+1)); //开辟数组空间
obj->k = k;
obj->front = 0;
obj->tail = 0;
return obj;
}
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
return obj->front == obj->tail;
}
bool myCircularQueueIsFull(MyCircularQueue* obj) {
int tailnext = obj->tail + 1;
if(tailnext == obj->k + 1) //obj->tail + 1 == obj->k + 1,指向最后一个空间时为满。
{
tailnext = 0; //跳到头
}
return tailnext == obj->front; //等于头则满
}
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
int tailnext = obj->tail + 1;
if(myCircularQueueIsFull(obj))
{
return false;
}
else
{
obj->a[obj->tail] = value;
obj->tail++;
if(obj->tail == obj->k + 1) //存在边界情况,front=tail,然后压入,然后tail=k+1,此时队列没有满,tail需要指向头,形成闭环。
obj->tail = 0; //指向头
return true;
}
}
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
{
return false;
}
else
{
obj->front++;
if(obj->front == obj->k + 1) //转圈
obj->front = 0;
return true;
}
}
int myCircularQueueFront(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
{
return -1;
}
else
{
int ret = obj->a[obj->front];
return ret;
}
}
int myCircularQueueRear(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
{
return -1;
}
else
{
int tailpre = obj->tail - 1;
if(tailpre == -1)
tailpre = obj->k;
return obj->a[tailpre];
}
}
void myCircularQueueFree(MyCircularQueue* obj) {
free(obj->a);
free(obj);
}