【手撕数据结构】栈和队列高频面试题

括号匹配问题

给定一个只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

1.左括号必须用相同类型的右括号闭合。
2.左括号必须以正确的顺序闭合。
3.每个右括号都有一个对应的相同类型的左括号。

要求:
 时间复杂度:O(n)
题目解析:

  • 根据第一条要求可知,必须是如(),[],{}这类同类型的闭合
  • 第二条则是告诉我们,只有[()]这种顺顺序闭合,不能出现[(])这种
  • 第三条则是告诉我们,右括号一定有一个相同类型的左括号

示例 1:

输入:s = “()”
输出:true
示例 2:

输入:s = “()[]{}”
输出:true
示例 3:

输入:s = “(]”
输出:false

思路:该题是栈的典型应用,满足后进先出的规则(后入栈的前括号将优先与先出现的后括号相匹配)遍历字符串,遇到前括号直接入栈。遇到后括号,判断该后括号与栈顶的前括号是否匹配(若此时栈为空,该括号没有匹配的前括号入栈,如"]"),若不匹配则字符串无效;若匹配则删除栈顶元素,继续遍历字符串,直到字符串遍历完毕。当字符串遍历完后,检测栈是否为空,若为空,则字符串有效(解决了只有入栈前括号没有后括号匹配出栈问题,如"((" ),若不为空,说明有前括号未匹配,字符串无效。

typedef char STDataType;	//方便以后修改栈的存储数据类型
typedef struct Stack
{
	STDataType* arr;
	int top;
	int capacity;
}ST;
void STDestroy(ST* ps)
{
	assert(ps);
	if (ps->arr)
	{
		free(ps->arr);
		ps->arr = NULL;
		ps->capacity = ps->top = 0;
	}
}
void STInit(ST* ps)
{
	assert(ps);
	ps->arr = NULL;
	ps->capacity = ps->top = 0;
}

bool STEmpty(ST* ps)
{
	assert(ps);
	return ps->top == 0;
}
void STPush(ST* ps, STDataType x)
{
	assert(ps);
	//检查空间是否足够
	if (ps->top == ps->capacity)
	{
		int NewCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
		STDataType* tmp = (STDataType*)realloc(ps->arr, NewCapacity * sizeof(STDataType));
		//有可能开辟空间失败
		if (tmp == NULL)
		{
			perror("realloc failed");
			exit(1);
		}
		else
		{
			ps->arr = tmp;
			ps->capacity = NewCapacity;
		}
	}
	ps->arr[ps->top++] = x;
}
void STPop(ST* ps)
{
	assert(ps);
	assert(!STEmpty(ps));	//不要忘记判空,栈必须得有数据才能出栈
	ps->top--;
}
STDataType STTop(ST* ps)
{
	assert(ps);
	assert(!STEmpty(ps));
	return ps->arr[ps->top - 1];
}
bool isValid(char* s) {
    char* ps = s;
    ST st;
    STInit(&st);
    while (*ps != '\0')
    {
        
        if (*ps == '(' || *ps == '{' || *ps == '[')
        {
            STPush(&st, *ps);
        }
        else
        { if(STEmpty(&st))
            {
                return false;
            }
            char ret = STTop(&st);
           
            if ((ret == '(' && *ps==')') || (ret == '[' && *ps == ']') || (ret == '{' && *ps == '}'))
            {
                STPop(&st);
            }
            else
            {
                STDestroy(&st);
                return false;
            }
                 
            
        }
        ps++;
    }
   bool ret = STEmpty(&st) == true;        //这里必须要栈为空,为空表示每个括号匹配成功,不为空表示没匹配到,比如只有一个[
    STDestroy(&st);
    return ret;
}

用队列实现栈

请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通队列的全部四种操作(push、top、pop 和 empty)。

实现 MyStack 类:
 void push(int x) 将元素 x 压入栈顶。
 int pop() 移除并返回栈顶元素。
 int top() 返回栈顶元素。
 boolean empty() 如果栈是空的,返回 true ;否则,返回 false 。
思路:
使用两个队列来回倒数据,如果需要入栈,随便从其中选一个队列,进行入栈操作。如果需要出栈,则将有数据的队列的size-1个元素移倒另外一个空队列中,将剩下的元素进行出栈。以此类推。注意取栈顶元素很容易与出栈一样的操作,误认为只需要不进行删除就行。但是我们进行入栈和出栈的前提是一定有一个空的队列进行移动所以我们只需取栈尾元素即可

typedef int QDataType;		//方便以后修改存储的数据类型
typedef struct QueueNode		//队列中的节点
{
	QDataType val;
	struct QueueNode* next;
}QueueNode;

typedef struct Queue
{
	QueueNode* phead;
	QueueNode* ptail;
	int size;	//不要忘了保存数列的有效数据个数
}Queue;
void QueueInit(Queue* pq)
{
	assert(pq);
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}
bool QEmpty(Queue* pq)
{
	assert(pq);
	return pq->size == 0;	
}
QueueNode* QBuyNode(QDataType x)
{

	QueueNode* tmp = (QueueNode*)malloc(sizeof(QueueNode));
	if (tmp == NULL)
	{
		perror("malloc failed");
		exit(1);
	}
	tmp->val = x;
	tmp->next = NULL;
	return tmp;
}
void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	QueueNode* newnode = QBuyNode(x);
	//注意考虑队列为空的情况
	if (pq->phead == NULL)	//队列为空
	{
		pq->phead = pq->ptail = newnode;
	}
	else
	{
		pq->ptail->next = newnode;
		pq->ptail = newnode;
	}
	pq->size++;
}
void QueuePop(Queue* pq)
{
	assert(pq);
	assert(!QEmpty(pq));

	//只有一个结点的情况,避免ptail变成野指针
	if (pq->ptail == pq->phead)
	{
		free(pq->phead);
		pq->phead = pq->ptail = NULL;
	}
	else
	{
		//删除队头元素、
		QueueNode* next = pq->phead->next;
		free(pq->phead);
		pq->phead = next;
	}
	--pq->size;
}
QDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(!QEmpty(pq));	//assert为假会触发
	return pq->phead->val;
}
QDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(!QEmpty(pq));//assert为假会触发
	return pq->ptail->val;
}
int QueueSize(Queue* pq)
{
	assert(pq);
	return pq->size;
}
void QueueDestroy(Queue* pq)
{
	assert(pq);
	while (pq->phead != pq->ptail)
	{
		QueueNode* del = pq->phead;
		pq->phead = pq->phead->next;
		free(del);
		del = NULL;
	}
	free(pq->phead);
	pq->phead = NULL;
	pq->ptail = NULL;		//不要忘了把ptail也设置NULL,虽然他们是指向同一块空间,但是指针变量不是同一块空间
	pq->size = 0;//不要忘了把size设置0
}
//用两个队列来实现栈
typedef struct {
    Queue q1;
    Queue q2;
} MyStack;

MyStack* myStackCreate() {
    MyStack* pst = (MyStack*)malloc(sizeof(MyStack));
    QueueInit(&pst->q1);
    QueueInit(&pst->q2);
    return pst;
}

void myStackPush(MyStack* obj, int x) {
    if(!QEmpty(&obj->q1))
    {
        QueuePush(&obj->q1,x);
    }
    else
    {
         QueuePush(&obj->q2,x);
    }

}

int myStackPop(MyStack* obj) {
    //找不为空的队列
    Queue* EmpQ=&obj->q1;   //这里obj->q1是第一个队列的地址,假设q1为空
    Queue* NoneQ=&obj->q2;
    if(!QEmpty(&obj->q1))
    {
        NoneQ=&obj->q1;
        EmpQ=&obj->q2;
    }
    //将不为空队列中size-1个数据倒入到空队列中
    while(QueueSize(NoneQ) > 1)
    {
        int front = QueueFront(NoneQ);
        QueuePush(EmpQ,front);
        QueuePop(NoneQ);
    }
    //非空队列只剩下一个数据
    int pop=QueueFront(NoneQ);
    QueuePop(NoneQ);    //出栈也要删除数据
    return pop;
}
//取栈顶元素一定要保证两个队列其中一个队列为空,这里很容易把出栈复制过来删掉pop就是出栈,但是删掉pop后那么两个队列都不为空,出栈和入栈就不能根据为空进行操作了

//所以这里直接进行取队尾元素进行取栈顶元素
int myStackTop(MyStack* obj) {
    if(!QEmpty(&obj->q1))
    {
        return QueueBack(&obj->q1);
    }
    else
    {
        return QueueBack(&obj->q2);
    }
}

bool myStackEmpty(MyStack* obj) {
    return QEmpty(&obj->q1) && QEmpty(&obj->q2);
}

void myStackFree(MyStack* obj) {
    QueueDestroy(&obj->q1);
    QueueDestroy(&obj->q2);
    free(obj);
    obj=NULL;
}

/**
 * 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);
*/

用栈实现队列

请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty)。

实现 MyQueue 类:
 void push(int x) 将元素 x 推到队列的末尾。
 int pop() 从队列的开头移除并返回元素。
 int peek() 返回队列开头的元素。
 boolean empty() 如果队列为空,返回 true ;否则,返回 false。

思路:
与用队列实现栈思路基本一样,用一个栈来入队列,一个栈来出队列。如果要出队列就把入队列的栈一直取栈顶导入为空的出队列中,这样原本1 2 3 4 的由于先进后出就达到了另外一栈存储的是4 3 2 1 这样的顺序,符合队列的先进先出原则。
如果要取队头元素也只需将出队列的栈取栈顶元素即可。


typedef int STDataType;	//方便以后修改栈的存储数据类型
typedef struct Stack
{
	STDataType* arr;
	int top;
	int capacity;
}ST;


void STInit(ST* ps)
{
	assert(ps);
	ps->arr = NULL;
	ps->capacity = ps->top = 0;
}
void STDestroy(ST* ps)
{
	assert(ps);
	if (ps->arr)
	{
		free(ps->arr);
		ps->arr = NULL;
		ps->capacity = ps->top = 0;
	}
}
bool STEmpty(ST* ps)
{
	assert(ps);
	return ps->top == 0;
}
	void STPush(ST* ps, STDataType x)
	{
		assert(ps);
		//检查空间是否足够
		if (ps->top == ps->capacity)
		{
			int NewCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
			STDataType* tmp = (STDataType*)realloc(ps->arr, NewCapacity * sizeof(STDataType));
			//有可能开辟空间失败
			if (tmp == NULL)
			{
				perror("realloc failed");
				exit(1);
			}
			else
			{
				ps->arr = tmp;
				ps->capacity = NewCapacity;
			}
		}
		ps->arr[ps->top++] = x;
	}
void STPop(ST* ps)
{
	assert(ps);
	assert(!STEmpty(ps));	//不要忘记判空,栈必须得有数据才能出栈
	ps->top--;
}
STDataType STTop(ST* ps)
{
	assert(ps);
	assert(!STEmpty(ps));
	return ps->arr[ps->top - 1];
}
STSize(ST* ps)
{
	assert(ps);
	return ps->top;
}


typedef struct {
    ST PushST;
    ST PopST;
} MyQueue;


MyQueue* myQueueCreate() {
    MyQueue* pst=(MyQueue*)malloc(sizeof(MyQueue));
    STInit(&pst->PushST);
    STInit(&pst->PopST);
    return pst;
}

void myQueuePush(MyQueue* obj, int x) {
    STPush(&obj->PushST,x);
}

int myQueuePop(MyQueue* obj) {
    //1.检查popst是否为空
    //不为空,出数据
    //为空,从pushst倒数据
    if(STEmpty(&obj->PopST))
    {
        while(!STEmpty(&obj->PushST))
        {
            STPush(&obj->PopST,STTop(&obj->PushST));
            STPop(&obj->PushST);
        }
    }
    int top = STTop(&obj->PopST );
    STPop(&obj->PopST);
    return top; 
}

int myQueuePeek(MyQueue* obj) {
     if(STEmpty(&obj->PopST))
    {
        while(!STEmpty(&obj->PushST))
        {
            STPush(&obj->PopST,STTop(&obj->PushST));
            STPop(&obj->PushST);
        }
    }
    return STTop(&obj->PopST);
}

bool myQueueEmpty(MyQueue* obj) {
    return STEmpty(&obj->PushST) && STEmpty(&obj->PopST);
}

void myQueueFree(MyQueue* obj) {
    STDestroy(&obj->PushST);
    STDestroy(&obj->PopST);
    free(obj);
    obj=NULL;
}

/**
 * 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);
*/
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值