詳細なスタックとキュー
1スタック
1.1 スタックの概念と構造
スタックは、一方の端の要素の挿入と削除のみを許可する特別な線形リストです。データの挿入および削除操作が実行される端をスタックの最上位と呼び、もう一方の端をスタックの最下位と呼びます。スタック内のデータ要素は、LIFO (Last In First Out) の原則に従います。
スタックには 2 つの操作があります。
挿入操作: スタックの挿入操作はプッシュ/プッシュ/プッシュと呼ばれ、受信データはスタックの先頭にあります。(このブログではスタッキングと呼びます)
削除操作: スタックの削除操作をポッピングと呼びます。出力データもスタックの最上位にあります。(このブログはスタックから呼び出されます)
1.2 スタックの実装
スタックの実装は一般に配列またはリンク リストを使用して実装できますが、比較的に配列の構造の方が優れています。配列の最後にデータを挿入するコストが比較的小さいためです。
配列の実装:
静的配列スタック構造:
typedef int STDataType;
#define N 10
typedef struct Stack
{
STDataType a[N];
int top; // 栈顶
}Stack;
この図では主に静的配列で実装されたスタックを示していますが、静的配列で実装されたスタックは実用的ではなく、当ブログでは動的成長をサポートするスタックを主に実装しています。
1.3 動的な成長をサポートするスタック
1.3.1 構造体宣言
// 支持动态增长的栈
#define DEFAULT_CAPACITY 4 //默认(初始)容量
typedef int STDataType;//栈存储的数据类型(这里为int)
typedef struct Stack
{
STDataType* a;
int top; // 栈顶
int capacity; // 容量
}Stack;
1.3.2 スタックの初期化と破棄
関数宣言:
// 初始化栈
void StackInit(Stack* ps);
// 销毁栈
void StackDestroy(Stack* ps);
関数の実装:
void StackInit(Stack* ps)
{
assert(ps);
//先给数组开辟一个初始容量
ps->a = (STDataType*)malloc(sizeof(STDataType) * DEFAULT_CAPACITY);
if (ps->a == NULL)
{
perror("malloc failed");
exit(-1);
}
ps->capacity = DEFAULT_CAPACITY;
ps->top = -1;
}
void StackDestroy(Stack* ps)
{
assert(ps);
ps->capacity = 0;
ps->top = -1;
free(ps->a);
}
注意すべき点:スタックのトップは top -1 から始まります。これは、スタックのトップ要素の添字がトップと同じであるが、スタック内の要素の数がトップ + 1 であることを保証するために使用されます。
1.3.3 プッシュおよびポップ操作
関数宣言:
// 入栈
void StackPush(Stack* ps, STDataType data);
// 出栈
void StackPop(Stack* ps);
// 获取栈顶元素
STDataType StackTop(Stack* ps);
関数の実装:
void StackPush(Stack* ps, STDataType data)
{
assert(ps);
//入栈之前先检查,栈是否满了,满了就需要扩容后再入栈
if (ps->top + 1 == ps->capacity)
{
STDataType* tmp = realloc(ps->a, sizeof(STDataType) * (ps->capacity) * 2);
if (tmp == NULL)
{
perror("realloc failed");
exit(-1);
}
ps->a = tmp;
ps->capacity *= 2;
}
(ps->top)++;
ps->a[ps->top] = data;
}
void StackPop(Stack* ps)
{
assert(ps);
assert(ps->top != -1);
(ps->top)--;
}
STDataType StackTop(Stack* ps)
{
assert(ps);
return ps->a[ps->top];
}
なお、ここでのpop関数は2つに分けて記述し、1つはスタックの先頭要素の削除のみ、もう1つはスタックの先頭要素の取得のみを行います。ここに関数、つまり、スタックの最上位要素を削除し、スタックの最上位要素を返す Pop 関数を記述することもできます。
1.3.4 スタック空判定と要素数
関数宣言:
// 获取栈中有效元素个数
int StackSize(Stack* ps);
// 检测栈是否为空,如果为空返回true,如果不为空返回false
bool StackEmpty(Stack* ps);
関数の実装:
// 获取栈中有效元素个数
int StackSize(Stack* ps)
{
assert(ps);
return ps->top + 1;
}
// 检测栈是否为空
bool StackEmpty(Stack* ps)
{
assert(ps);
return (ps->top == -1);
}
2つのキュー
2.1 キューの概念と構造
キュー: 一方の端でデータを挿入し、もう一方の端でデータを削除することのみを許可する特殊な線形テーブル。キューには先入れ先出し FIFO (先入れ先出し
) があります。削除操作が実行される端は列の先頭と呼ばれます。
2.2 キューの実装
キューは配列やリンク リストの構造でも実装できますが、配列の構造を使用するとデータ全体を移動する必要があり、効率が比較的低いため、リンク リストの構造を使用することをお勧めします。低い。
2.3 リンクリスト実装キュー
2.3.1 構造体宣言
//队列声明
typedef int QNodeDataType;
typedef struct QNode
{
QNodeDataType x;
struct QNode* next;
}QNode;
typedef struct Queue
{
QNode* head;//头指针
QNode* tail;//尾指针
size_t size;
}Queue;
2.3.2 キューの初期化と破棄
関数宣言
// 初始化队列
void QueueInit(Queue* q);
// 销毁队列
void QueueDestroy(Queue* q);
関数の実装:
// 初始化队列
void QueueInit(Queue* q)
{
assert(q);
q->head = q->tail = NULL;
q->size = 0;
}
// 销毁队列
void QueueDestroy(Queue* q)
{
assert(q);
QNode* cur = q->head;
while (cur)
{
QNode* tmp = cur;
cur = cur->next;
free(tmp);
}
q->head = q->tail = NULL;
q->size = 0;
}
2.3.3 キューの入口と出口
関数宣言:
// 队尾入队列
void QueuePush(Queue* q, QNodeDataType data);
// 队头出队列
void QueuePop(Queue* q);
// 获取队列头部元素
QNodeDataType QueueFront(Queue* q);
// 获取队列队尾元素
QNodeDataType QueueBack(Queue* q);
関数の実装:
// 队尾入队列
void QueuePush(Queue* q, QNodeDataType data)
{
assert(q);
QNode* newNode = (QNode*)malloc(sizeof(QNode));
if (newNode == NULL)
{
perror("malloc failed");
exit(-1);
}
newNode->next = NULL;
newNode->x = data;
if (q->tail == NULL)
{
q->head = q->tail = newNode;
}
else
{
q->tail->next = newNode;
q->tail = q->tail->next;
}
q->size++;
}
// 队头出队列
void QueuePop(Queue* q)
{
assert(q);
assert(!QueueEmpty(q));
if (q->head->next == NULL)
{
free(q->head);
q->head = q->tail = NULL;
}
else
{
QNode* tmp = q->head;
q->head = q->head->next;
free(tmp);
}
q->size--;
}
// 获取队列头部元素
QNodeDataType QueueFront(Queue* q)
{
assert(q);
assert(!QueueEmpty(q));
return q->head->x;
}
// 获取队列队尾元素
QNodeDataType QueueBack(Queue* q)
{
assert(q);
assert(!QueueEmpty(q));
return q->tail->x;
}
同様に、ここの関数も別々に書くこともできますし、組み合わせて書くこともできます。
2.3.4 キュー空き判定と要素数の取得
関数宣言:
// 获取队列中有效元素个数
int QueueSize(Queue* q);
// 检测队列是否为空,如果为空返回true,如果非空返回false
bool QueueEmpty(Queue* q);
関数の実装:
// 获取队列中有效元素个数
int QueueSize(Queue* q)
{
assert(q);
return q->size;
}
// 检测队列是否为空
bool QueueEmpty(Queue* q)
{
assert(q);
return (q->size == 0);
}
要約する
スタックとキューは比較的単純な構造であり、主に他の複雑な構造 (後述) の下部構造として使用されます。それらの例をいくつか次号で紹介します。
このブログとは関係のない内容についてお話します。実は長らく更新を停止していたことがお分かりいただけると思います。主な理由は、期間の終わりが近づいているため、見直しに行ったのと、ブロガーがブログ主ではないことです。コンピュータ専攻ですがオートメーション専攻です 学期の授業数が多いので復習内容も多く、長らく中断していましたが、ご理解いただければ幸いです。ここ数日お休みしてたので近々更新します。