記事ディレクトリ
序文
スタック: 一方の端でのみデータの挿入と削除を許可する特別な線形構造。データの操作が可能な端はスタックの最上位と呼ばれ、もう一方の端はスタックの最下位と呼ばれます。
このブログで実装するのは配列スタックです。
1.スタックの実装アイデア
スタックの特殊性については、配列 (配列の最後にデータを挿入して削除) とリンク リスト (先頭にあるデータを挿入して削除) を使用してスタックを実装する場合の時間計算量は O(1) です。難しくない。
配列スタックの
長所と短所
- 配列はランダム アクセス (添え字を使用してデータにアクセスする) をサポートしており、多くのアルゴリズムでは二分法などのランダム アクセスのサポートが必要です。
- キャッシュ使用率が高い
劣った
- スペースが足りない場合は容量を拡張してください
- 時々スペースを無駄にする
1. 構造の定義
スタックの構造は非常に単純です
。動的に開かれた空間へのポインタ、実際の空間サイズを記録する変数、スタックの最上位要素を記録する添字です。
typedef int STDataType;
typedef struct Stack
{
STDataType* data;
int top;//栈顶下标
int capacity;//空间大小
}Stack;
2. スタックの初期化(StackInit)
データ ポインタは動的に開かれたスペースを指し、capacity はこの時点のスペースのサイズを記録し、top は 0 に設定されます。
- top は 0 に設定され、スタックの先頭データが挿入される位置を示します。
- top は -1 に設定され、現時点でのスタック上の先頭データの位置を示します。
ここでは、top を 0 に設定します。
//初始化栈
#define SIZE 4
void StackInit(Stack* ps)
{
assert(ps);
ps->data = (STDataType*)malloc(sizeof(STDataType) * SIZE);
if (ps->data == NULL)
{
perror("malloc");
exit(-1);
}
ps->top = 0;
ps->capacity = SIZE;
}
3. スタックにプッシュします (StackPush)
top は 0 に初期化されているため、top の添え字に直接データを入力できます。ただし、データを入力する前に容量を確認してください。top == 容量の場合は、容量を拡張する必要があります。
- ここで容量を確認する操作は関数にカプセル化することもできますが、スタックの操作はスタックをプッシュするときに容量を確認するだけでよく、他の操作では容量を確認する必要がないため、その必要はありません。関数に組み込むと効率が低下します (関数呼び出しでは関数スタック フレームを形成する必要があります)。
//入栈
void StackPush(Stack* ps, STDataType x)
{
assert(ps);
if (ps->top == ps->capacity)
{
STDataType* tmp = (STDataType*)realloc(ps->data, sizeof(STDataType) * (ps->capacity * 2));
if (tmp == NULL)
{
perror("realloc");
exit(-1);
}
ps->data = tmp;
ps->capacity *= 2;
}
ps->data[ps->top] = x;
ps->top++;
}
4.ポップ(スタックポップ)
top は、スタックの先頭のデータがスタックにプッシュされる位置を示すため、スタックをポップするには、先頭から 1 を減算するだけで済みます。(スタックにプッシュしたデータは次回から直接上書きされます)
ただし、top = 0の場合はスタックにデータが無いことを意味し、スタック操作はできませんので注意してください。
- ポップ操作でデータを取得できません
//出栈
void StackPop(Stack* ps)
{
assert(ps);
assert(ps->top != 0);
ps->top--;
}
5. スタックの最上位要素を取得します (StackTop)
top は、データがスタックにプッシュされる場所、つまりスタック上の最上位データの次の場所を指します。
次に、スタックの最上位にあるデータを取得するには、top - 1 を読み取るだけで済みます。ただし、top = 0 の場合、top - 1 = -1 は範囲外にアクセスされるため、top = 0 の場合、スタックの最上位の要素を取得できないことに注意してください。
//获取栈顶元素
STDataType StackTop(Stack* ps)
{
assert(ps);
assert(ps->top > 0);
return ps->data[ps->top - 1];
}
6. スタックが空かどうかを確認します (StackEmpty)
top はデータがスタックにプッシュされる場所を指し、その値はスタック内のデータの数も示します。
したがって、スタックが空かどうかを知るには、top == 0 の判定を行うだけで済みます。
//检查栈是否为空
bool StackEmpty(Stack* ps)
{
assert(ps);
return ps->top == 0;
}
7. スタックを破棄します (StackDestroy)
動的に開かれたスペースを解放し、capacity を 0 に、top を 0 に設定します。
//销毁栈
void StackDestroy(Stack* ps)
{
assert(ps);
free(ps->data);
ps->top = 0;
ps->capacity = 0;
}
2. コードの実装
Stack.h ファイルには関数の宣言、ヘッダー ファイルの参照、および構造体の定義が格納され、
Stack.c ファイルには関数の実装が格納されます。
//Stack.h 文件
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
#define SIZE 4
typedef int STDataType;
typedef struct Stack
{
STDataType* data;
int top;
int capacity;
}Stack;
//初始化栈
void StackInit(Stack* ps);
//入栈
void StackPush(Stack* ps, STDataType x);
//出栈
void StackPop(Stack* ps);
//获取栈顶元素
STDataType StackTop(Stack* ps);
//检查栈是否为空
bool StackEmpty(Stack* ps);
//销毁栈
void StackDestroy(Stack* ps);
//Stack.c 文件
#include "Stack.h"
//初始化栈
void StackInit(Stack* ps)
{
assert(ps);
ps->data = (STDataType*)malloc(sizeof(STDataType) * SIZE);
if (ps->data == NULL)
{
perror("malloc");
exit(-1);
}
ps->top = 0;
ps->capacity = SIZE;
}
//入栈
void StackPush(Stack* ps, STDataType x)
{
assert(ps);
if (ps->top == ps->capacity)
{
STDataType* tmp = (STDataType*)realloc(ps->data, sizeof(STDataType) * (ps->capacity * 2));
if (tmp == NULL)
{
perror("realloc");
exit(-1);
}
ps->data = tmp;
ps->capacity *= 2;
}
ps->data[ps->top] = x;
ps->top++;
}
//出栈
void StackPop(Stack* ps)
{
assert(ps);
assert(ps->top != 0);
ps->top--;
}
//获取栈顶元素
STDataType StackTop(Stack* ps)
{
assert(ps);
assert(ps->top > 0);
return ps->data[ps->top - 1];
}
//检查栈是否为空
bool StackEmpty(Stack* ps)
{
assert(ps);
return ps->top == 0;
}
//销毁栈
void StackDestroy(Stack* ps)
{
assert(ps);
free(ps->data);
ps->top = 0;
ps->capacity = 0;
}
要約する
上記は私のスタックの実装です。