题目1.括号匹配问题
思路:
判断括号的有效性可以使用栈这一数据结构来解决。
我们遍历给定的字符串s。当我们遇到一个左括号时,我们会期望在后续的遍历中,有一个相同类型的右括号将其闭合。由于后遇到的左括号要先闭合,因此我们可以将这个左括号放入栈顶。
当我们遇到一个右括号时,我们需要将一个相同类型的左括号闭合。此时,我们可以取出栈顶的左括号并判断它们是否是相同类型的括号。如果不是相同的类型,或者栈中并没有左括号,那么字符串s 无效,返回False。在上述的基础之上,在遍历结束后,如果栈中没有左括号,说明我们将字符串 s中的所有左括号闭合,返回True,否则返回 False。
实现代码:
typedef char STDataType;
typedef struct Stack
{
STDataType* a;
int top;//栈顶
int capacity;
}ST;
void StackInit(ST* ps);
void StackDestory(ST* ps);
void StackPush(ST* ps, STDataType x);
void StackPop(ST* ps);
STDataType StackTop(ST* ps);
bool StackEmpty(ST* ps);
int StackSize(ST* ps);
void StackInit(ST* ps)
{
assert(ps);
ps->a = NULL;
ps->capacity = ps->top = 0;
}
void StackDestory(ST* ps)
{
assert(ps);
free(ps->a);
ps->a = NULL;
ps->capacity = ps->top = 0;
}
void StackPush(ST* ps, STDataType x)
{
assert(ps);
if (ps->capacity==ps->top)
{
int newcapcaity = ps->capacity == 0 ? 4 : ps->capacity * 2;
ps->a = (STDataType*)realloc(ps->a, newcapcaity * sizeof(STDataType));
if (ps->a==NULL)
{
perror("realloc fail");
exit(-1);
}
ps->capacity = newcapcaity;
}
ps->a[ps->top] = x;
ps->top++;
}
void StackPop(ST* ps)
{
assert(ps);
assert(!StackEmpty(ps));
ps->top--;
}
STDataType StackTop(ST* ps)
{
assert(ps);
assert(!StackEmpty(ps));
return ps->a[ps->top - 1];
}
bool StackEmpty(ST* ps)
{
assert(ps);
return ps->top == 0;
}
int StackSize(ST* ps)
{
assert(ps);
return ps->top;
}
bool isValid(char * s){
ST MY_st;
StackInit(&MY_st);
while(*s)
{
if(*s=='('||*s=='['||*s=='{')
{
StackPush(&MY_st, *s);
}
else
{
if(StackEmpty(&MY_st))
{
StackDestory(&MY_st);
return false;
}
char tmp = StackTop(&MY_st);
if((tmp=='('&&*s!=')')||(tmp=='['&&*s!=']')||(tmp=='{'&&*s!='}'))
{
StackDestory(&MY_st);
return false;
}
StackPop(&MY_st);
}
s++;
}
if(StackEmpty(&MY_st))
{
StackDestory(&MY_st);
return true;
}
else
{
StackDestory(&MY_st);
return false;
}
}
题目2.用队列实现栈
思路:
为了满足栈的特性,即最后入栈的元素最先出栈,在使用队列实现栈时,应满足队列前端的元素是最后入栈的元素。可以使用两个队列实现栈的操作,
入栈操作时,首先将元素入队到 q1,然后出栈时先将q1的n-1个元素依次出队并入队到 q2
,此时q1仅剩的那一个元素即为栈顶元素,出队即可。此时q1没有元素了
下一次想出栈的时候就再倒一次,也就是把q2的前n-1个元素出队并入队到q1,然后q2把剩下的那个元素出队即可,此时q2为空下一次再入栈的时候,也就不用管q1还是q2,就直接谁有元素就往谁的里面入队元素即可。
总结:出栈倒一下,入栈入非空。
实现代码:
typedef int QDataType;
typedef struct QueueNode
{
struct Queue* next;
QDataType data;
}QNode;
typedef struct Queue
{
QNode* head;
QNode* tail;
int size;
}Queue;
void QueueInit(Queue* pq);//队列的初始化
void QueueDestroy(Queue* pq);//队列的销毁
void QueuePush(Queue* pq, QDataType x);//入队
void QueuePop(Queue* pq);//出队
QDataType QueueFront(Queue* pq);//队头元素
QDataType QueueBack(Queue* pq);//队尾元素
bool QueueEmpty(Queue* pq);//队列判空
int QueueSize(Queue* pq);//判断有效元素个数
void QueueInit(Queue* pq)//队列的初始化
{
assert(pq);
pq->head = pq->tail = NULL;
pq->size = 0;
}
void QueueDestroy(Queue* pq)//队列的销毁
//head->node->node->node->node->node->node->tail//单链表的结构
{
assert(pq);
QNode* cur = pq->head;
while (cur)
{
QNode* del = cur;
cur = cur->next;
free(del);
}
pq->head = pq->tail = NULL;
}
void QueuePush(Queue* pq, QDataType x)//入队
{
assert(pq);
QNode* newnode = (QNode*)malloc(sizeof(QNode));
if (newnode==NULL)
{
perror("malloc fail");
exit(-1);
}
else
{
newnode->data=x;
newnode->next = NULL;
}
if (pq->tail == NULL)
{
pq->head = pq->tail = newnode;
}
else
{
pq->tail->next = newnode;
pq->tail = pq->tail->next;//pq->tail=newnode;
}
pq->size++;
}
void QueuePop(Queue* pq)//出队
{
assert(pq);
assert(!QueueEmpty(pq));
if (pq->head->next == NULL)
{
free(pq->head);
pq->head = pq->tail = NULL;
}
else
{
QNode* del = pq->head;
pq->head = pq->head->next;
free(del);
del = NULL;
}
pq->size--;
}
QDataType QueueFront(Queue* pq)//队头元素
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->head->data;
}
QDataType QueueBack(Queue* pq)//队尾元素
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->tail->data;
}
bool QueueEmpty(Queue* pq)//队列判空
{
assert(pq);
return pq->head==NULL&&pq->tail == NULL;//
}
int QueueSize(Queue* pq)//判断有效元素个数
{
assert(pq);
return pq->size;
}
typedef struct {
Queue q1;
Queue q2;
} MyStack;
MyStack* myStackCreate() {
MyStack*obj=(MyStack*)malloc(sizeof(MyStack));//
QueueInit(&obj->q1);
QueueInit(&obj->q2);
return obj;
}
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))
{
nonEmpty=&obj->q1;
Empty=&obj->q2;
}
int num=QueueSize(nonEmpty);
if(nonEmpty==NULL)
{
return;
}
while(num>1)
{
QueuePush(Empty,QueueFront(nonEmpty));
QueuePop(nonEmpty);
num--;
}
int tmp=QueueFront(nonEmpty);
QueuePop(nonEmpty);
return tmp;
}
int myStackTop(MyStack* obj) {
Queue* Empty=&obj->q1;
Queue* nonEmpty=&obj->q2;
if(!QueueEmpty(&obj->q1))
{
nonEmpty=&obj->q1;
Empty=&obj->q2;
}
return QueueBack(nonEmpty);
}
bool myStackEmpty(MyStack* obj) {
if(&obj->q1==NULL||&obj->q2==NULL)
{
return true;
}
return QueueEmpty(&obj->q1)&&QueueEmpty(&obj->q2);
}
void myStackFree(MyStack* obj) {
QueueDestroy(&obj->q1);
QueueDestroy(&obj->q1);
free(obj);
}
题目3.用栈实现队列
思路:
一个队列是 FIFO 的,但一个栈是 LIFO 的。
这就意味着最新压入的元素必须得放在栈底。为了实现这个目的,我们创建两个栈一个专门用来进元素(pushST),一个用来出元素(popST),入队时只往pushST里存放元素,要出队时先把pushST中所有的元素移到popST 中,直接从 popST 弹出就可以了,
注意:当popST不为空的情况下是不需要从pushST里往popST中倒元素的。要出队时直接从popST里出栈即可。
这样设计可以避免来回颠倒元素,减少不必要的麻烦。
实现代码:
typedef int STDataType;
typedef struct Stack
{
STDataType* a;
int top;//栈顶
int capacity;
}ST;
void StackInit(ST* ps);
void StackDestory(ST* ps);
void StackPush(ST* ps, STDataType x);
void StackPop(ST* ps);
STDataType StackTop(ST* ps);
bool StackEmpty(ST* ps);
int StackSize(ST* ps);
void StackInit(ST* ps)
{
assert(ps);
ps->a = NULL;
ps->capacity = ps->top = 0;
}
void StackDestory(ST* ps)
{
assert(ps);
free(ps->a);
ps->a = NULL;
ps->capacity = ps->top = 0;
}
void StackPush(ST* ps, STDataType x)
{
assert(ps);
if (ps->capacity==ps->top)
{
int newcapcaity = ps->capacity == 0 ? 4 : ps->capacity * 2;
ps->a = (STDataType*)realloc(ps->a, newcapcaity * sizeof(STDataType));
if (ps->a==NULL)
{
perror("realloc fail");
exit(-1);
}
ps->capacity = newcapcaity;
}
ps->a[ps->top] = x;
ps->top++;
}
void StackPop(ST* ps)
{
assert(ps);
assert(!StackEmpty(ps));
ps->top--;
}
STDataType StackTop(ST* ps)
{
assert(ps);
assert(!StackEmpty(ps));
return ps->a[ps->top - 1];
}
bool StackEmpty(ST* ps)
{
assert(ps);
return ps->top == 0;
}
int StackSize(ST* ps)
{
assert(ps);
return ps->top;
}
typedef struct {
ST pushST;
ST popST;
} MyQueue;
MyQueue* myQueueCreate() {
MyQueue*obj=(MyQueue*)malloc(sizeof(MyQueue));
StackInit(&obj->pushST);
StackInit(&obj->popST);
return obj;
}
void myQueuePush(MyQueue* obj, int x) {
StackPush(&obj->pushST,x);
}
int myQueuePop(MyQueue* obj) {
int tmp=0;
if(!StackEmpty(&obj->popST))
{
tmp=StackTop(&obj->popST);
StackPop(&obj->popST);
}
else
{
while(!StackEmpty(&obj->pushST))/犯错之处!!!!!!!!!!!!
{
StackPush(&obj->popST,StackTop(&obj->pushST));
StackPop(&obj->pushST);
}
tmp=StackTop(&obj->popST);
StackPop(&obj->popST);
}
return tmp;
}
int myQueuePeek(MyQueue* obj) {
if(StackEmpty(&obj->popST)&&!StackEmpty(&obj->pushST))
{
while(!StackEmpty(&obj->pushST))/犯错之处!!!!!!!!!!!!
{
StackPush(&obj->popST,StackTop(&obj->pushST));
StackPop(&obj->pushST);
}
}
return StackTop(&obj->popST);
}
bool myQueueEmpty(MyQueue* obj) {
return StackEmpty(&obj->popST)&&StackEmpty(&obj->pushST);
}
void myQueueFree(MyQueue* obj) {
free(obj);
}
题目4.设计循环队列
题目链接
思路:
循环队列可用链表实现也可以用数组实现,链表要实现“循环”直接让最后结点的那个next域指向头结点即可,但是用数组实现的话怎么实现循环呢?
设有效个数为k个(为了解决判空和判满,多留出了一个空间,有效个数为k,实际数组个数为k+1),数组的队头元素下标为front,队尾元素下一个位置的下标为back,(也有书上写front是对头元素的前一个,队尾元素为back,判空判满的表达式一样。)
每次入队的时候先要判断当先队列是否是满的,不为满,让back当前位置赋值要入队的元素,然后back正常++往后走,关键的来了再进行back=back %(k+1)操作即可实现循环。
那怎么区分什么时候空和什么时候满呢?
解决方法:
1、加一个size来记录当前元素的个数。
2、增加一个空间, 假如说长度4,那么就开5个空间,back位置下一个是front就满了(这样做是为了更好的区分队空和队满),所以说满的时候永远留一个空位置。
我们这里用第二种方法。
判断空:
当front和back相等的时候为空。
判断满:
当back的下一个为front时,即(back+1)%k+1和front相等的时候就是满了。
这个比较难想,可以看下面的动图:
下图的k应该是5,这里画错了。
实现代码:
typedef struct {
int*a;
int front;
int back;
int N;
} MyCircularQueue;
MyCircularQueue* myCircularQueueCreate(int k) {
MyCircularQueue*obj=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
obj->a=(int *)malloc(sizeof(int)*(k+1));///犯错之处!!!!!!!!
obj->front=obj->back=0;
obj->N=k+1;
return obj;
}
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
return obj->front==obj->back;
}
bool myCircularQueueIsFull(MyCircularQueue* obj) {
return ((obj->back)+1)%obj->N==obj->front;
}
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
if(myCircularQueueIsFull(obj))
{
return false;
}
obj->a[obj->back]=value;
obj->back++;
obj->back%=obj->N;
return true;
}
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
{
return false;
}
obj->front++;
obj->front%=obj->N;
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;
}
int tmp=(obj->back-1+obj->N)%obj->N;
return obj->a[tmp];
}
void myCircularQueueFree(MyCircularQueue* obj) {
free(obj->a);
free(obj);
}