The strongest data structure in history - stack and queue related written test interview questions

1. Stacks and queues interview questions

1.1 Bracket matching problem

topic:

image-20220326144841666

Ideas:

First traverse the given string, if it is a left bracket, push it into the stack, according to the LIFO feature of the stack, then take out the left brackets in the stack one by one and match the remaining right brackets pair by pair. , returns false if they do not match, and returns true if both match.

E.g:

Code:

typedef char STDataType;
typedef struct Stack
{
	STDataType* a;
	int top;//栈顶的位置
	int capacity;//容量
}ST;
void StackInit(ST* ps)
{
	assert(ps);
	ps->a = NULL;
	ps->capacity = 0;
	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->top == ps->capacity)//满了进行扩容
	{
		int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
		ps->a = (STDataType*)realloc(ps->a,sizeof(STDataType)*newCapacity);
		if (ps->a == NULL)
		{
			printf("fail malloc\n");
			exit(-1);
		}
		ps->capacity = newCapacity;
	}
	ps->a[ps->top++] = x;
}
void StackPop(ST* ps)
{
	assert(ps);
	assert(ps->top > 0);
	--ps->top;
}
bool StackEmpty(ST* ps)
{
	assert(ps);
	return ps->top == 0;
}
STDataType StackTop(ST* ps)
{
	assert(ps);
	assert(ps->top > 0);

	return ps->a[ps->top - 1];
}
int SizeStack(ST* ps)
{
	assert(ps);
	return ps->top;
}
//前面的所有代码就是定义一个栈以及栈的相关函数
bool isValid(char * s){
    ST st;
    StackInit(&st);
    while(*s)
    {
        if(*s=='['||*s=='('||*s=='{')
        {
            StackPush(&st,*s);
            ++s;
        }
        else
        {
            if(StackEmpty(&st))//当所给的字符串中只有右括号时直接返回false
            {
                StackDestory(&st);
                return false;
            }
            char top = StackTop(&st);//从栈中取一个左括号
            StackPop(&st);//进行出栈操作,让刚才取的字符出栈
            if((*s==']'&&top!='[')
            ||(*s=='}'&&top!='{')
            ||(*s==')'&&top!='('))//两个字符不相匹配的情况下直接返回false
            {
                StackDestory(&st);
                return false;
            }
            else
            {
                ++s;
            }
        }
    }
    //栈为空,说明所有左括号都匹配了
    bool ret =  StackEmpty(&st);//判断是否只有左括号,如果只有左括号此时ret就为false,如果前面都已经匹配完了ret就等于true
    StackDestory(&st);
    return ret;
}

1.2 Implementing a stack with a queue

image-20220327105221958

Parse the above example:

The first line of input is the action performed. The second row is the data to perform the operation.

The output is the corresponding return value.

Idea: Implement stack with two queues.

The basic structure of the stack:

image-20220327204730285

1. Push the stack, push data to the queue that is not empty.

2. Pop the stack, import the first N-1 data of the queue that is not empty into another empty queue, and delete the last remaining one.

image-20220327205910564

Code:

typedef int QDataType;
typedef struct QueueNode
{
	QDataType data;
	struct QueueNode* next;
}QNode;
typedef struct Queue
{
	QNode* head;
	QNode* tail;
}Queue;
void QueueInit(Queue* pq);
void QueueDestory(Queue* pq);
void QueuePush(Queue*pq,QDataType x);
void QueuePop(Queue*pq);
size_t QueueSize(Queue* pq);
QDataType QueueFront(Queue* pq);
QDataType QueueBack(Queue* pq);
bool QueueEmpty(Queue* pq);
void QueueInit(Queue* pq)
{
	assert(pq);
	pq->head =pq->tail = NULL;
}
QNode* BuyQNode(QDataType x)
{
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		printf("fail malloc\n");
		exit(-1);
	}
	newnode->data = x;
	newnode->next = NULL;
	return newnode;
}
void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	QNode* newnode = BuyQNode(x);
	if (pq->tail == NULL)
	{
		assert(pq->head == NULL);
		pq->head= pq->tail = newnode;
	}
	else
	{
		pq->tail->next = newnode;
		pq->tail = newnode;
	}
	
}
void QueuePop(Queue* pq)
{
	assert(pq);
	assert(pq->tail&&pq->head);
	if (pq->head->next==NULL)//处理只有一个节点的时候
	{
		free(pq->tail);
		pq->tail = pq->head = NULL;
	}
	else//有多个节点
	{
		QNode* next = pq->head->next;
		free(pq->head);
		pq->head = next;
	}
}
size_t QueueSize(Queue* pq)
{
	assert(pq);
	size_t size = 0;
	QNode* cur = pq->head;
	while (cur!= pq->tail->next)
	{
		size++;
		cur = cur->next;
	}
	return size;
}
QDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(pq->head);
	return pq->head->data;
}
QDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(pq->tail);
	return pq->tail->data;
}
void QueueDestory(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->head;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	pq->head = pq->tail = NULL;
}
bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->head==NULL&&pq->tail==NULL;
}
typedef struct {
    Queue q1;
    Queue q2;
} MyStack;


MyStack* myStackCreate() {
    MyStack* pst = (MyStack*)malloc(sizeof(MyStack));
    assert(pst);
    QueueInit(&pst->q1);//这一行代码和后面这一行代码等价:QueueInit(&(pst->q1));
    QueueInit(&pst->q2);//这一行代码和后面这一行代码等价:QueueInit(&(pst->q2));
    return pst;
}

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

int myStackPop(MyStack* obj) {
    assert(obj);
    Queue *emptyQ = &obj->q1;
    Queue*nonEmptyQ = &obj->q2;
    if(!QueueEmpty(&obj->q1))
    {
        emptyQ = &obj->q2;
        nonEmptyQ = &obj->q1;
    }
    //把非空队列的前N个数据,导入空队列,剩下一个删掉
    //就实现了后进先出
    while(QueueSize(nonEmptyQ)>1)
    {
        QueuePush(emptyQ,QueueFront(nonEmptyQ));//将非空队列的队头数据push到非空队列中
        QueuePop(nonEmptyQ);//将非空队列的队头数据出队
    }
    QDataType top = QueueFront(nonEmptyQ);
    QueuePop(nonEmptyQ);
    return top;
}

int myStackTop(MyStack* obj) {
    assert(obj);
    
    if(!QueueEmpty(&obj->q1))
    {
        return QueueBack(&obj->q1);
    }
    else
    {
        return QueueBack(&obj->q2);
    }
   
}
bool myStackEmpty(MyStack* obj) {
    assert(obj);
    return QueueEmpty(&obj->q1)&&QueueEmpty(&obj->q2);
}

void myStackFree(MyStack* obj) {
    assert(obj);
    QueueDestory(&obj->q1);
    QueueDestory(&obj->q2);
    free(obj);
}

1.3 Implementing queues with stacks

image-20220330222159195

Ideas:

image-20220330221422700

Note: In the above code, when the dequeue operation is performed, as long as there is data in the popST stack, then we do not perform the data dump operation (that is, dump the data in the push into the popST stack), only when the pop The data is empty and the data is poured when we want to dequeue.

Queue structure:

image-20220330225340619

Code:

typedef int STDataType;
//数组栈的实现
typedef struct Stack
{
	STDataType* a;
	int top;//栈顶的位置
	int capacity;//容量
}ST;
void StackInit(ST* ps)
{
	assert(ps);
	ps->a = NULL;
	ps->capacity = 0;
	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->top == ps->capacity)//满了进行扩容
	{
		int newCapacity = ps->capacity == 0 ? 2 : 2 * ps->capacity;
		STDataType*new = (STDataType*)realloc(ps->a,sizeof(STDataType)*newCapacity);
		if (new == NULL)
		{
			printf("fail malloc\n");
			exit(-1);
		}
		ps->a = new;
		ps->capacity = newCapacity;
	}
	ps->a[ps->top++] = x;
}
void StackPop(ST* ps)
{
	assert(ps);
	assert(ps->top > 0);
	--ps->top;
}
bool StackEmpty(ST* ps)
{
	assert(ps);
	return ps->top == 0;
}
STDataType StackTop(ST* ps)
{
	assert(ps);
	assert(ps->top > 0);

	return ps->a[ps->top - 1];
}
int SizeStack(ST* ps)
{
	assert(ps);
	return ps->top;
}
void StackInit(ST*ps);
void StackDestory(ST* ps);
void StackPush(ST* ps,STDataType x);
void StackPop(ST* ps);
bool StackEmpty(ST* ps);
int SizeStack(ST* ps);
STDataType StackTop(ST* ps);
typedef struct {
    ST pushST;
    ST popST;
} MyQueue;


MyQueue* myQueueCreate() {
    MyQueue * myQueue = (MyQueue*)malloc(sizeof(MyQueue));
    assert(myQueue);
    StackInit(&myQueue->pushST);
    StackInit(&myQueue->popST);
    return myQueue;
}

void myQueuePush(MyQueue* obj, int x) {
    assert(obj);
    StackPush(&obj->pushST,x);//入队直接向pushST插入即可
}

int myQueuePop(MyQueue* obj){
    assert(obj);
    if(StackEmpty(&obj->popST))//push为空,就进行倒数据,就符合先进先出的顺序了
    {
        while(!StackEmpty(&obj->pushST))
        {
            StackPush(&obj->popST,StackTop(&obj->pushST));
            StackPop(&obj->pushST);
        }
    }
    STDataType ret = StackTop(&obj->popST);//临时保存返回的数据
    StackPop(&obj->popST);
    return ret;
}

int myQueuePeek(MyQueue* obj) {
    assert(obj);
    if(StackEmpty(&obj->popST))
    {
        while(!StackEmpty(&obj->pushST))
        {
            StackPush(&obj->popST,StackTop(&obj->pushST));
            StackPop(&obj->pushST);
        }
    }
    return StackTop(&obj->popST);
}

bool myQueueEmpty(MyQueue* obj) {
    assert(obj);
    return StackEmpty(&obj->pushST)&&StackEmpty(&obj->popST);
}

void myQueueFree(MyQueue* obj) {
    assert(obj);
    StackDestory(&obj->pushST);
    StackDestory(&obj->popST);
    free(obj);
}

1.4 Designing circular queues

image-20220327163952436

In order to avoid confusion between empty and full, and indistinguishable, open an extra space.

1. Empty when front = tail

2. When the next position of tail is front, it is full

Two cases of full:

1、obj->tail = k && obj->head = 0

image-20220327202909920

2、obj->tail+1 = obj->head

image-20220327203117250

Code:

typedef struct {
    int *a;
    int head;//循环队列的头
    int tail;//循环队列的尾
    int capacity;//循环队列元素的最大数目
} MyCircularQueue;

bool myCircularQueueIsEmpty(MyCircularQueue* obj);//判断循环队列是否为空的声明
bool myCircularQueueIsFull(MyCircularQueue* obj);//判断循环队列是否为满的声明
MyCircularQueue* myCircularQueueCreate(int k) //循环队列的初始化
{
    MyCircularQueue*myCircularQ = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    assert(myCircularQ);
    int *tmp = (int*)malloc(sizeof(int)*(k+1));
    assert(tmp);
    myCircularQ->a = tmp;
    myCircularQ->head = 0;
    myCircularQ->tail = 0;
    myCircularQ->capacity = k;
    return myCircularQ;
}

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) //向循环队列插入一个元素,如果成功插入则返回真。
{
    assert(obj);
    if(myCircularQueueIsFull(obj))//满了的情况
    {
        return false;
    }
    obj->a[obj->tail] = value;
    if(obj->tail==obj->capacity)//此时已经到达了开辟数组的最后一个位置,tail再进行++操作后会跳跃到第一个位置
    {
        obj->tail = 0;
    }
    else
    {
        ++obj->tail;
    }
    return true;
}

bool myCircularQueueDeQueue(MyCircularQueue* obj) //从循环队列中删除一个元素,如果成功删除则返回真。
{
    assert(obj);
    if(myCircularQueueIsEmpty(obj))//循环队列中为空的情况
    {
        return false;
    }
    if(obj->head==obj->capacity)//循环队列中的head在开辟的最后的一个空间位置的情况,head要发生跳跃
    {
        obj->head = 0;
    }
    else
    {
        ++obj->head;
    }
    return true;
}

int myCircularQueueFront(MyCircularQueue* obj) //从队首获取元素,如果队列为空,返回 -1 。
{
    assert(obj);
    if(myCircularQueueIsEmpty(obj))//队列元素为空的情况
        return -1;
    return obj->a[obj->head];
}

int myCircularQueueRear(MyCircularQueue* obj)//获取队尾元素,如果队列为空,返回 -1 。
{
    assert(obj);
    if(myCircularQueueIsEmpty(obj))//循环队列元素为空的情况
        return -1;
    if(obj->tail==0)//尾在头的时候,就要返回最后一个空间的位置
    {
        return obj->a[obj->capacity];
    }
    else
    {
        return obj->a[obj->tail-1];//正常情况返回tail的前一个位置
    }
}

bool myCircularQueueIsEmpty(MyCircularQueue* obj) //判断循环队列是否满
{
    assert(obj);
    return obj->tail==obj->head;//尾下标等于头下标的时候就是空
}

bool myCircularQueueIsFull(MyCircularQueue* obj) //判断循环队列是否满
{
    assert(obj);
    if(obj->tail==obj->capacity && obj->head==0)//判断的是尾下标指向最后一块空间,头下标在最靠前的空间
    {
        return true;
    }
    else
    {
        return obj->tail+1 ==obj->head;
    }
}  

void myCircularQueueFree(MyCircularQueue* obj) //循环队列的销毁
{
    assert(obj);
    free(obj->a);
    free(obj);
}

2. Concept multiple choice questions

1. The initial state of a stack is empty. Now put the elements 1, 2, 3, 4, 5, A, B, C, D, and E into the stack in turn, and then pop them out of the stack in turn, the order of elements popping out of the stack is ( B ).

A 12345ABCDE

B EDCBA54321

C ABCDE12345

D 54321EDCBA

2. If the push sequence is 1, 2, 3, 4, and the stack can be popped during the push process, the following impossible pop sequence is ( C )

A 1,4,3,2

B 2,3,4,1

C 3,1,4,2

D 3,4,2,1

Analysis: In option C, if you want to come up with 1 after 3 comes out, you must come out with 2 first.

3. The storage space of the circular queue is Q(1:100), and the initial state is front=rear=100. After a series of normal enqueue and dequeue operations, front=rear=99, the number of elements in the circular queue is (D)

A 1

B 2

C 99

D 0

Analysis: From the implementation of the previous circular queue, it is easy to know that the number of elements at this time is 0.

4. The following ( ) is not the basic operation of the queue? (B)

A inserts a new element from the end of the queue

B removes the ith element from the queue

C Check if a queue is empty

D read the value of the head element

5. There is a circular queue, whose head pointer is front and the queue tail pointer is rear; the length of the circular queue is N, and it actually stores at most N - 1 data. Its effective length in the team is (B) (assuming that the head of the team does not store data)

A (rear - front + N) % N + 1

B (rear - front + N) % N

C ear - front) % (N + 1)

D (rear - front + N) % (N - 1)

Analysis: This question needs to consider two situations:

The first case: front is in front of rear

image-20220330230533359

The second case: front is behind rear

image-20220330231943935

Note: Going from the end to the front is actually equivalent to crossing the boundary at the end and continuing to add! As shown below:

image-20220330232027215

Combining the above two situations: the final formula is:

count = (rear - front + N ) % N (Why is N added to the first one? Because for the case where front is in front of rear, adding N has no effect on the result, because N is also modulo operate)

3. Purpose of stacks and queues

Stack: used to solve bracket matching, reverse Polish expressions, recursion to non-recursion, etc.

Queues: Fair queuing, breadth-first traversal, etc.

Guess you like

Origin blog.csdn.net/m0_57304511/article/details/124094135