データ構造のサンプルコードとその解説 - スタックとキュー

スタックとキュー

スタックスタック

最初のうちの最後の

スタックの構造定義と基本操作。

#define MaxSize 50
typedef struct {
    
    
	int data[MaxSize];//栈中存放数据类型为整型
	int top;//栈顶指针
}Stack;

初期化

ここで初期化するとき、スタックの最上位ポインタは -1 を指し、一部は 0 を指すため、後続のプッシュとポップのコードは若干異なります。

void InitStack(Stack& S) {
    
    //初始化栈
	S.top = -1;
}

スタックが空かどうかを確認する

int IsEmpty(Stack S) {
    
    //判断栈是否为空
	if (S.top == -1)
		return 1;
	else
		return 0;
}

押し込み操作

  1. スタックの先頭ポインタは最初は -1 を指しているため、最初にスタックの先頭ポインタを変更してから、それをスタックにプッシュする必要があります。
  2. MaxSize が 50 の場合、データ フィールドは data[0]~data[49] にあります。したがって、ここでは S.top == MaxSize - 1 はスタックがいっぱいであることを意味します。
  3. 4 ~ 5 行目は S.data[++S.top]=x として結合できます。
int Push(Stack& S, int x) {
    
    //压栈操作
	if (S.top == MaxSize - 1)//若栈满则无法压栈
		return 0;
	S.top++;
	S.data[S.top] = x;
	return 1;
}

ポップ操作

  1. ここで、ポップされた要素は x によって受信され、ポップされたときに最初に受信され、その後スタックの最上位ポインタがデクリメントされます。
  2. 4 ~ 5 行目は x=S.data[++S.top] として結合できます。
int Pop(Stack& S, int& x) {
    
    //出栈操作
	if (S.top == -1)//若栈空则无法出栈
		return 0;
	x = S.data[S.top];
	S.top--;
	return 1;
}

スタックの最上位要素を読み取ります

ここで、スタックから飛び出した要素は x で受け取られます

int GetTop(Stack S, int& x) {
    
    //读取栈顶元素
	if (S.top == -1)//若栈空则无栈顶元素
		return 0;
	x = S.data[S.top];
	return 1;
}

もちろん、上記はシーケンシャルスタックですが、単一リンクリストに似たチェーンスタックもあり、先頭のノードがスタックの先頭、反対側がスタックの最下位になります。およびスタッキングはヘッド ノードで実行されるため、操作が便利です。

01 先頭ノードを持つ単連結リスト L があり、ノード構造は data と next の 2 つのフィールドで構成され、そのうちデータフィールドは文字型です。リンクされたリスト内の n 文字すべてが中心対称であるかどうかを判断するアルゴリズムを設計してみてください。たとえば、xyx と xyyx は両方とも中心対称です。
  1. この型が中心対称であるかどうかを判断するには、一般にスタックが使用されます。
  2. この問題では前と後ろの2つの部分に分かれており、前半を順番にスタックにプッシュし、後半に到達した時点でスタックから飛び出してきた要素と比較します。比較すると、スタックの一番上のポインタが移動します。ポインターの移動をトラバースします。
  3. エラーを起こしやすいfor ループがスタックにプッシュされた後、i の位置はスタックの先頭要素の後の位置になり、スタックの先頭位置に戻すには i- が必要です。
  4. ノード数が偶数の場合は通常どおり処理され、ノード数が奇数の場合は中間要素はスタックにプッシュされず、トラバーサル ポインタはさらに 1 ステップ進み、中間要素をバイパスします。
int central_symmetry(LinkList L, int n) {
    
    
	char S[n / 2];//定义字符型数组来作为一个栈
	int i;//定义栈顶指针
	LNode* p = L->next;//定义遍历指针
    //对称轴左边字符依次入栈
	for (i = 0; i < n / 2; i++) {
    
    
		S[i] = p->data;
		p = p->next;
	}
	i--;//变量 i 返回栈顶位置
	if (n % 2 == 1) {
    
    //若字符数为奇数则 p 向后遍历一步,因为最中间字符不需考虑
		p = p->next;
	}
	while (p != NULL) {
    
    //对称轴右边字符依次与栈顶元素比对
		if (p->data == S[i]) {
    
    //与栈顶元素相等则 p 继续遍历与下一出栈元素比较
			i--;//出栈
			p = p->next;//继续遍历
		}
		else//若不等,则说明 n 个字符不对称,直接返回 0
			return 0;//非中心对称
	}
	return 1;//若正常跳出循环,则证明 n 个字符对称,返回 1
}
02 算術式に括弧と角括弧の 2 種類の括弧が含まれていると仮定し、算術式が文字配列に格納され、文字 '\0' が格納されていると仮定して、式内の括弧がペアであるかどうかを判断するアルゴリズムを記述します。算術式の終了文字として使用されます。
  1. この質問では、数値と演算子に対する演算は必要ありません。
  2. switch ステートメントは上から下に順番に実行されるため、飛び出すには Break が必要です。
  3. 右括弧が見つかった場合は、まずスタックが空かどうかを判断する必要があります。空の場合は、不一致があることを意味します。
  4. 配列の走査が完了した後にスタックが空の場合は、すべての左括弧が正常にペアになっていることが証明されます。
int BracketsCheck(char a[]) {
    
    
	Stack S;
	InitStack(S);
	char x;
	for (int i = 0; a[i] != '\0'; i++) {
    
    
		switch (a[i]) {
    
    
			case'('://若数组元素为左括号,则压栈继续遍历
				push(S, a[i]);
				break;
			case'[':
				push(S, a[i]);
				break;
			case')'://若元素为右括号,则需考虑是否有左括号与之配对
				if (IsEmpty(S) {
    
    
					return 0;//若栈空,则说明无左括号与之配对
				}
				else {
    
    
					Pop(S, x);
					if (x != '(') {
    
    //若栈不为空,则判断栈顶元素与当前括号是否配对
						return 0;
					}
                    //配对上了
					break;
				}
			case']':
				if (IsEmpty(S)) {
    
    
					return 0;
				}
				else {
    
    
					Pop(S, x);
					if (x != '[') {
    
    
						return 0;
					}
					break;
				}
			default://若数组元素不是括号则直接继续遍历
				break;
		}
	}
	if (IsEmpty(S)) {
    
    //若数组遍历完成后栈为空,则证明所有左括号全部配对成功
		return 1;
	}
	else {
    
    //若栈不为空,则证明有左括号未配对
		retun 0;
	}
}
03 2 つのスタック S1 と S2 は両方ともシーケンシャル スタック方式を採用し、記憶領域 [0,...,MaxSize-1] を共有します。スペースの使用を最大化し、オーバーフローの可能性を減らすために、スタックの最上部を向かい合って、正面から成長させて使用できます。このスタックの定義と、スタッキングやポッピングに関わるS1、S2の動作アルゴリズムを設計して書き出してみます。

2 つのスタックには 2 つのスタック トップ ポインターが必要です。トップ配列はここで定義されており、1 つは top[0]、もう 1 つは top[1]

#define MaxSize 50
typedef struct{
    
    
	int data[MaxSize];
	int top[2];//一个top[0],一个top[1]指针
}DStack;

初期化

void InitDStack(DStack& S) {
    
    
	S.top[0] = -1;//初始化 S1 栈顶指针
	S.top[1] = MaxSize;//初始化 S2 栈顶指针
}

スタック

  1. ストレージスペースには 2 つのスタックがあるため、どのスタックをスタックにプッシュするかを指定する必要があり、i はどのスタックを区別するために使用されます。
  2. ここで、S[1] は下位スタックを表し、S[2] は上位スタックを表します。
int push(int i, int x) {
    
    
	if (i < 0 || i>1) {
    
    
		return 0;//若 i 的输入不合法,则无法入栈
	}
	if (S.top[1] - S.top[0] == 1) {
    
    //若存储空间已满,则无法进行入栈操作
		return 0;
	}
	switch (i) {
    
    
		case 0:// S1 栈顶指针上移后入栈
			S.top[0]++;
			S.data[S.top[0]] = x;
			//S.data[++S.top[0]] = x;
			break;
		case 1:// S2 栈顶指针下移后入栈
			S.top[1]--;
			S.data[S.top[1]] = x;
			//S.data[--S.top[1]] = x;
			break;
	}
	return 1;
}

飛び出る

int pop(int i, int& x) {
    
    
	if (i < 0 || i>1) {
    
    
		return 0;
	}
	if (S.top[0] == -1 || S.top[1] == MaxSize) {
    
    //空栈
		return 0;
	}
	switch (i) {
    
    
        case 0:
            x = S.data[S.top[0]];
            S.top[0]--;//下面的栈往下移
            //x = S.data[S.top[0]--];
            break;
        case 1:
            x = S.data[S.top[1]];
            S.top[1]++;//上面的栈顶指针往上移
            //x = S.data[S.top[1]++];
            break;
        }
	return 1;
}

キューキュー

先入先出

キューの構造定義とその基本操作。

#define MaxSize 50
typedef struct {
    
    
	int data[MaxSize];//队列中存放数据类型为整型
	int front, rear;//队首指针和队尾指针
}Queue;
void InitQueue(Queue& Q) {
    
    //初始化队列
	Q.front = 0;
	Q.rear = 0;
}
int IsEmpty(Queue Q) {
    
    //判断队列是否为空
	if (Q.front == Q.rear)//若队首指针和队尾指针指向同一位置,则队列为空
		return 1;
	else
		return 0;
}

エンキューとデキュー

ここで、キューの末尾のポインターは要素の次の位置を指します。詳細については、「エンキュー操作とデキュー操作」を参照してください。

チームに入るとき、チームの最後尾にあるポインターが必要です。判断队满Q.リア == MaxSize

//顺序队列的入队和出队:
//这里队尾指针指向元素的后一个位置
int EnQueue(Queue& Q, int x) {
    
    //入队操作
	if (Q.rear == MaxSize)//若队满,则无法入队
		return 0;
	Q.data[Q.rear] = x;//变量 x 入队
	Q.rear++;//队尾指针后移
	return 1;//入队成功
}

それは、チームの外に出て戦う必要があるチームのリーダーの指針です。判断队空Q.フロント == Q.リア

int DeQueue(Queue& Q, int& x) {
    
    //出队操作
	if (Q.front == Q.rear)//若队空,则无法出队
		return 0;
	x = Q.data[Q.front];//变量 x 接收出队元素
	Q.front++;//队首指针后移
	return 1;//出队成功
}

循環キュー

Q.front == Q.rear は以前チームが空であると判断したため、ループする場合は空と満員の両方を意味するため、可能です。そのため、牺牲一个空间Q.front == Q.rear は判断にのみ使用できます。空の。

:Q.フロント == Q.リア

完全な評決:(Q.リア + 1) % MaxSize == Q.フロント(リアを後ろに動かすとフロントが一杯になります)

テールポインタとヘッドポインタの両方を後方に移動する必要があります+1取余

//循环队列的入队和出队:(牺牲一个存储空间)
int EnQueue(Queue& Q, int x) {
    
    //入队操作
	if ((Q.rear + 1) % MaxSize == Q.front)//若队满,则无法入队
		return 0;
	Q.data[Q.rear] = x;//变量 x 入队
	Q.rear = (Q.rear + 1) % MaxSize;//队尾指针后移
	return 1;//入队成功
}
int DeQueue(Queue& Q, int& x) {
    
    //出队操作
	if (Q.front == Q.rear)//若队空,则无法出队
		return 0;
	x = Q.data[Q.front];//变量 x 接收出队元素
	Q.front = (Q.front + 1) % MaxSize;//队首指针后移
	return 1;//出队成功
}
04 循環キュー内のすべての要素を使用したい場合は、フラグ フィールド タグを設定し、タグの値を 0 または 1 にして、キュー ヘッドがキューのステータスが「空」であることを区別する必要があります。ポインタの前部とキュー末尾のポインタの後部は同じです。まだ「いっぱい」です。この構造に対応するエンキューおよびデキューのアルゴリズムを作成してみてください。
  1. 循環キューでは、もともと Q.front == Q.rear でキューが空か満杯かを判断できるため、特別な処理のために上記のスペースが犠牲になります。
  2. この質問は、フラグ フィールドを設定することによってチームが空か満員かを判断します。
  3. ここでは、チームに入るときのみチームが満員であると判断する必要があるため、各エントリー後にタグは 1 になり、チームを離れるときのみチームが空であると判断する必要があるため、各退出後にタグは 0 になりますチーム; チームが空いているので Q.front == Q.rear の判定はチームが満員の場合に判定する必要があるため、キューの先頭と最後尾のポインタが同じ位置にない場合は、判定は入りませんので、タグの初期値は0にしてください。
  4. Q.front == Q.rear && Q.tag == 1 と Q.front == Q.rear && Q.tag == 0 は非常に強力で、両方とも 2 つの条件を同時に満たす必要があります。
typedef struct {
    
    
	int data[MaxSize];
	int front, rear;
	int tag;
}Queue;
void InitQueue(Queue& Q) {
    
    //初始化队列
	Q.front = 0;
	Q.rear = 0;
    Q.tag = 0;
}
int EnQueue(Queue& Q, int x) {
    
    
	if (Q.front == Q.rear && Q.tag == 1) {
    
    //若队满,则无法进行入队操作
		return 0;
	}
	Q.data[Q.rear] = x;
	Q.rear = (Q.rear + 1) % MaxSize;//队尾指针后移
	Q.tag = 1;//入队后可能会出现队满的情况,所以将 tag 标志域置为 1
	return 1;
}
int DeQueue(Queue& Q, int& x) {
    
    
	if (Q.front == Q.rear && Q.tag == 0) {
    
    
		return 0;//队空
	}
	x = Q.data[Q.front];
	Q.front = (Q.front + 1) % MaxSize;
	Q.tag == 0;//出队后可能会出现队空的情况,所以将 tag 标志域置为 0
	return 1;
}
05 Q はキュー、S は空のスタックで、キュー内の要素を反転するアルゴリズムを実現します。
  1. キューは先入れ先出しで、スタックは後入れ先出しです。キューの要素を 1 つずつスタックからデキューできるように、キュー内の要素を逆にする必要があります。キューが空の場合は、ポップアウトしてキューに入り、反転を実現します。
//Q 是一个队列,S 是一个空栈,实现将队列中的元素逆置的算法。
void Reverse(Queue& Q, Stack& S) {
    
    
	int x;
	while (!IsEmpty(Q)) {
    
    //出队列入栈
		DeQueue(Q, x);
		Push(S, x);
	}
	while (!IsEmpty(S)) {
    
    //出栈入队列
		Pop(S, x);
		EnQueue(Q, x);
	}
}

おすすめ

転載: blog.csdn.net/knighthood2001/article/details/132526687