歴史上最強のデータ構造-スタックとキューに関連する筆記試験の面接の質問

1.スタックとキューの面接の質問

1.1ブラケットマッチングの問題

トピック:

画像-20220326144841666

アイデア:

最初に指定された文字列をトラバースします。左括弧の場合は、スタックのLIFO機能に従ってスタックに押し込み、次にスタック内の左括弧を1つずつ取り出し、残りの右括弧のペアをペアで一致させます。 、一致しない場合はfalseを返し、両方が一致する場合はtrueを返します。

例えば:

コード:

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キューを使用したスタックの実装

画像-20220327105221958

上記の例を解析します。

入力の最初の行は、実行されたアクションです。2行目は、操作を実行するためのデータです。

出力は対応する戻り値です。

アイデア:2つのキューでスタックを実装します。

スタックの基本構造:

画像-20220327204730285

1.スタックをプッシュし、空ではないキューにデータをプッシュします。

2.スタックをポップし、空ではないキューの最初のN-1データを別の空のキューにインポートし、最後に残っているキューを削除します。

画像-20220327205910564

コード:

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スタックを使用したキューの実装

画像-20220330222159195

アイデア:

画像-20220330221422700

注:上記のコードでは、デキュー操作が実行されると、popSTスタックにデータがある限り、データダンプ操作は実行されません(つまり、プッシュでデータをpopSTスタックにダンプします)。 popの場合のみデータは空であり、デキューするときにデータが注がれます。

キュー構造:

画像-20220330225340619

コード:

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循環キューの設計

画像-20220327163952436

空と満杯、そして見分けがつかないものとの混同を避けるために、余分なスペースを開いてください。

1.フロント=テールの場合は空

2.尻尾の次の位置が前になると、いっぱいになります

フルの2つのケース:

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

画像-20220327202909920

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

画像-20220327203117250

コード:

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.多肢選択問題の概念

1.スタックの初期状態は空です。次に、要素1、2、3、4、5、A、B、C、D、およびEを順番にスタックに入れ、次にそれらを順番にスタックからポップします。要素の順序はスタックからポップします。は(B)です。

12345ABCDE

B EDCBA54321

CABCDE12345

D 54321EDCBA

2.プッシュシーケンスが1、2、3、4であり、プッシュプロセス中にスタックがポップされる可能性がある場合、次の不可能なポップシーケンスは(C)です。

1,4,3,2

B 2,3,4,1

C 3,1,4,2

D 3,4,2,1

分析:オプションCでは、3が出た後に1を考え出す場合は、最初に2を考え出す必要があります。

3.循環キューの記憶域はQ(1:100)であり、初期状態はfront = Rear=100です。一連の通常のエンキューおよびデキュー操作(front = Rear = 99)の後、循環キュー内の要素の数は(D)です。

A 1

B 2

C 99

D 0

分析:前の循環キューの実装から、この時点での要素の数が0であることが簡単にわかります。

4.次の()はキューの基本的な操作ではありませんか?(B)

キューの最後から新しい要素を挿入します

Bはi番目の要素をキューから削除します

Cキューが空かどうかを確認します

Dヘッド要素の値を読み取ります

5.ヘッドポインタがフロントでキューテールポインタがリアの循環キューがあります。循環キューの長さはNで、実際には最大でN-1個のデータを格納します。チームでの有効な長さは(B)です(チームの長がデータを保存しないと仮定します)

A(リア-フロント+ N)%N + 1

B(リア-フロント+ N)%N

耳-前)%(N + 1)

D(リア-フロント+ N)%(N-1)

分析:この質問では、次の2つの状況を考慮する必要があります。

最初のケース:前部が後部の前にある

画像-20220330230533359

2番目のケース:前部が後部の後ろにある

画像-20220330231943935

注:最後から前に移動することは、実際には最後に境界を越えて追加を続けることと同じです。以下に示すように:

画像-20220330232027215

上記の2つの状況を組み合わせると、最終的な式は次のようになります。

count =(rear --front + N)%N(Nが最初のものに追加されるのはなぜですか?フロントがリアの前にある場合、Nもモジュロ演算であるため、Nを追加しても結果に影響はありません)

3.スタックとキューの目的

スタック:ブラケットのマッチング、逆ポーランド記法、再帰から非再帰などを解決するために使用されます。

キュー:均等化キューイング、幅優先探索など。

おすすめ

転載: blog.csdn.net/m0_57304511/article/details/124094135