目次
2.2.3 キュー内の先頭要素と末尾要素および有効要素数を取得する
1スタック
1.1 スタックの概念と構造
スタックは特別なデータ構造です。これは、一方の固定端でのみ要素の挿入と削除を許可する特殊な線形テーブルです。
スタックにおいて、データの挿入および削除操作が実行される一方の端をスタックの最上部と呼び、もう一方の端をスタックの最下部と呼びます。スタック内のデータ要素は、後入れ先出しLIFO (先入れ後出しとも呼ばれます)の原則に従います。
さらに、スタックに関しては、プッシュとポップという 2 つの専門用語があります。
スタックのプッシュ: スタックの挿入操作はプッシュ/プッシュ/プッシュと呼ばれ、挿入されたデータはスタックの先頭にあります。
ポッピング: スタックの削除操作はポッピングと呼ばれ、削除されたデータはスタックの最上位にあります。
1.2 スタックの実装
スタックを実装するには、配列またはリンク リストを使用できます。相対的に言えば、配列内で添字へのランダムアクセスが可能であり、データを末尾から挿入する方が便利であるため、配列を使用する方が良いでしょう。
私たちが最初に想像した論理構造は次のとおりです。配列を作成し、配列の最初の要素がスタックの最下位、最後の要素がスタックの最上位となり、この配列を操作することでスタックが間接的に操作されます。
具体的な実装では、スタックも静的スタックと動的スタックの 2 つの方法に分割されます。
typedef int STDataType;
#define N 10
typedef struct Stack
{
STDataType _a[N];
int _top; // 栈顶
}Stack;
実際の運用では、データ量を見積もることが一般的に難しいため、静的スタックはあまり使用されません。一般に、動的スタックが使用されます。
typedef int STDataType;
typedef struct Stack
{
STDataType* _a;
int _top; // 栈顶
int _capacity; // 容量
}Stack;
1.2.1 動的スタックの初期化と破棄
void STInit(ST* pst)
{
assert(pst);
pst->a = NULL;
pst->top = 0;//top将会指向栈顶元素的下一个位置
//pst->top=-1 //top将会指向栈顶元素的位置
pst->capacity = 0;
}
void STDestory(ST* pst)
{
assert(pst);
free(pst->a);
pst->a = NULL;
pst->top = 0;
pst->capacity = 0;
}
初期化と破棄は比較的日常的な操作ですが、ここで注意が必要です 初期化の際、top の値を 0 に代入すると、top はデータ挿入後に一度インクリメントする必要があるため、top の値が 1 より大きくなりますスタックの最上位要素が配置される添え字。これは後続の操作に影響するため、後で変更を加える前にこれを明確にする必要があります。
top の値をスタックの最上位要素の添え字に直接したい場合は、top を -1 に設定できます。
top の値が異なると、以降の操作の多くも異なります。これらの違いを避けるために、top の値を一律 0 に設定します。
1.2.2 プッシュアンドポップ
配列を使用してスタックを実装するため、プッシュ操作は論理的には比較的単純で、配列の最後に挿入するだけです。ただし、配列の空間は動的に開かれるため、ここで注意する必要がある問題があります。したがって、挿入する前に、十分なスペースがあるかどうかを判断する必要があります。十分でない場合は拡張する必要があります。
void STPush(ST* pst, STDatatype x)
{
assert(pst);
if (pst->top == pst->capacity) //如果容量已满
{
int newCapacity = (pst->capacity == 0 )? 4 : 2 * pst->capacity;
STDatatype* tmp = (STDatatype*)realloc(pst->a, sizeof(STDatatype) * newCapacity);
if (tmp == NULL)
{
perror("realloc fail");
return;
}
pst->a = tmp;
pst->capacity = newCapacity;
}
pst->a[pst->top] = x;
pst->top++;
}
前のステップで top に -1 の値が割り当てられている場合、ここでの最初の判定条件を pst->top + 1 == pst->capacity に変更する必要があります。
スタックをポップする操作のほうが安心です。まずスタックが空かどうかを判断し、空でなければ直接top--にして、topを削除した要素の前の要素を指すようにして、この要素を新しい要素にします。スタックの一番上。
これを実行しても、実際にはスタックの元の最上位要素が削除されるわけではありませんが、ここで削除しなくても影響を受けないことに注意してください。
void STPop(ST* pst)
{
assert(pst);
assert(!STEmpty(pst));
pst->top--;
}
ポップ操作が実行されると、スタックが空になる可能性があることがわかりました。したがって、ブール値を使用して、スタックが空かどうかを判断できます。
bool STEmpty(ST* pst)
{
assert(pst);
return pst->top == 0;
}
特定の時点でのスタックの最上位要素の値を知りたい場合は、スタックの最上位要素を取得する関数を作成することもできます。
STDatatype STTop(ST* pst)
{
assert(pst);
assert(!STEmpty(pst));
return pst->a[pst->top - 1];
}
スタックの実際のアプリケーションでは、通常、スタックの最上位要素の取得とスタックのポップが一緒に使用されます。
1.2.3 スタック内の有効要素数の取得
場合によっては、スタックに保存されている有効なデータの数を知りたい場合は、関数を作成できます。
int STSize(ST* pst)
{
assert(pst);
return pst->top;
}
もちろん、以前に top の初期値が -1 だった場合、ここでは top+1 を返す必要があります。
2つのキュー
2.1 キューの概念と構造
キューはスタックとは対照的に、データ構造です。一方の端でデータの挿入操作、もう一方の端でデータの削除操作のみを許可する特別な線形テーブル。
挿入操作の終わりはキューの末尾と呼ばれ、削除操作の終わりはキューの先頭と呼ばれます。
2.2 キューの実装
キューは配列またはリンク リストとして実装することもできます。ただし、キューは先頭を削除する必要があるため、配列を使用するとデータを1つずつ移動する必要があり、時間コストがかかりすぎます。したがって、キューを実装する場合は、リンク リストを使用する方が適切です。
キュー内の各ノードの構造は次のとおりです。
typedef int QDatatype;
typedef struct QueneNode
{
struct QueneNode* next;
QDatatype data;
}QNode;
ここで、キューを操作する際には常にキューの先頭と末尾を知る必要があるため、先頭ポインタと末尾ポインタ、およびキュー内のデータ数を記録するために 2 つのポインタが必要です。したがって、これらのデータを保存するための構造を作成できます。
typedef struct Quene
{
QNode* phead;
QNode* ptail;
int size;
}Quene;
この構造では、リンク リストの先頭ノードがチームの先頭となり、末尾ノードがチームの末尾になります。
2.2.1 キューの初期化と破棄
初期化と破棄は単一リンク リストの場合と非常によく似ています。ここにソース コードを直接示します。分からない場合は、この記事の該当部分をご確認ください。[データ構造] ヘッドレス + 一方向 + 非循環リンクリストの追加、削除、確認、変更 (ソースコードは最後に添付)
void QueneInit(Quene* pq)
{
assert(pq);
pq->phead = NULL;
pq->ptail = NULL;
pq->size = 0;
}
void QueneDestroy(Quene* pq)
{
assert(pq);
QNode* cur = pq->phead;
while (cur)
{
QNode* next = cur->next;
free(cur);
cur = next;
}
pq->phead = pq->ptail = NULL;
pq->size = 0;
}
2.2.2 キューの挿入と削除
挿入および削除の操作も、単一リンク リストの挿入および削除と同様です。ここでの違いは、キュー内に 2 つの構造があり、1 つはキュー内のノードの構造、もう 1 つはキュー自体の構造であるためです。したがって、挿入および削除操作を実行する場合は、キュー自体の構造に影響を与えるかどうかを考慮する必要があります。したがって、いくつかの判定条件を追加する必要があります。
//插入(相当于尾插)
void QuenePush(Quene* pq, QDatatype x)
{
assert(pq);
QNode* newnode = (QNode*)malloc(sizeof(QNode));
if (newnode == NULL)
{
perror("malloc fail");
return;
}
newnode->data = x;
newnode->next = NULL;
if (pq->ptail == NULL)
{
assert(pq->phead == NULL);
pq->phead = pq->ptail = newnode;
}
else
{
pq->ptail->next = newnode;
pq->ptail = newnode;
}
pq->size++;
}
//删除(相当于头删)
void QuenePop(Quene* pq)
{
assert(pq);
assert(!QueneEmpty(pq));
if (pq->phead->next == NULL)
{
free(pq->phead);
pq->phead = NULL;
}
else
{
QNode* cur = pq->phead->next;
free(pq->phead);
pq->phead = cur;
}
pq->size--;
}
2.2.3 キュー内の先頭要素と末尾要素および有効要素数を取得する
これら 3 つの関数はコード上で直接実行できるため、より直感的です。
//获取队头元素
QDatatype QueneFront(Quene* pq)
{
assert(pq);
assert(!QueneEmpty(pq));
return pq->phead->data;
}
//获取队尾元素
QDatatype QueneBack(Quene* pq)
{
assert(pq);
assert(!QueneEmpty(pq));
return pq->ptail->data;
}
//队列中有效元素个数
int QueneSize(Quene* pq)
{
assert(pq);
return pq->size;
}
3 ソースコード
3.1スタック
3.1.1 スタック.h
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <assert.h>
#include <stdbool.h>
typedef int STDatatype;
typedef struct Stack
{
STDatatype* a;
int top;
int capacity;
}ST;
void STInit(ST* pst);
void STDestory(ST* pst);
void STPush(ST* pst, STDatatype x);
void STPop(ST* pst);
STDatatype STTop(ST* pst);
bool STEmpty(ST* pst);
int STSize(ST* pst);
3.1.2 スタック.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "Stack.h"
void STInit(ST* pst)
{
assert(pst);
pst->a = NULL;
pst->top = 0;//top将会指向栈顶元素的下一个位置
//pst->top=-1 //top将会指向栈顶元素的位置
pst->capacity = 0;
}
void STDestory(ST* pst)
{
assert(pst);
free(pst->a);
pst->a = NULL;
pst->top = 0;
pst->capacity = 0;
}
void STPush(ST* pst, STDatatype x)
{
assert(pst);
if (pst->top == pst->capacity)
{
int newCapacity = (pst->capacity == 0 )? 4 : 2 * pst->capacity;
STDatatype* tmp = (STDatatype*)realloc(pst->a, sizeof(STDatatype) * newCapacity);
if (tmp == NULL)
{
perror("realloc fail");
return;
}
pst->a = tmp;
pst->capacity = newCapacity;
}
pst->a[pst->top] = x;
pst->top++;
}
void STPop(ST* pst)
{
assert(pst);
assert(!STEmpty(pst));
pst->top--;
}
STDatatype STTop(ST* pst)
{
assert(pst);
assert(!STEmpty(pst));
return pst->a[pst->top - 1];
}
bool STEmpty(ST* pst)
{
assert(pst);
return pst->top == 0;
}
int STSize(ST* pst)
{
assert(pst);
return pst->top;
}
3.2 キュー
3.2.1 Quene.h
#pragma once
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <stdbool.h>
typedef int QDatatype;
typedef struct QueneNode
{
struct QueneNode* next;
QDatatype data;
}QNode;
typedef struct Quene
{
QNode* phead;
QNode* ptail;
int size;
}Quene;
void QueneInit(Quene* pq);
void QueneDestroy(Quene* pq);
void QuenePush(Quene* pq,QDatatype x);
void QuenePop(Quene* pq);
QDatatype QueneFront(Quene* pq);
QDatatype QueneBack(Quene* pq);
int QueneSize(Quene* pq);
bool QueneEmpty(Quene* pq);
3.2.2 クエンc
#define _CRT_SECURE_NO_WARNINGS 1
#include "Quene.h"
bool QueneEmpty(Quene* pq)
{
assert(pq);
return pq->size == 0;
}
void QueneInit(Quene* pq)
{
assert(pq);
pq->phead = NULL;
pq->ptail = NULL;
pq->size = 0;
}
void QueneDestroy(Quene* pq)
{
assert(pq);
QNode* cur = pq->phead;
while (cur)
{
QNode* next = cur->next;
free(cur);
cur = next;
}
pq->phead = pq->ptail = NULL;
pq->size = 0;
}
void QuenePush(Quene* pq, QDatatype x)
{
assert(pq);
QNode* newnode = (QNode*)malloc(sizeof(QNode));
if (newnode == NULL)
{
perror("malloc fail");
return;
}
newnode->data = x;
newnode->next = NULL;
if (pq->ptail == NULL)
{
assert(pq->phead == NULL);
pq->phead = pq->ptail = newnode;
}
else
{
pq->ptail->next = newnode;
pq->ptail = newnode;
}
pq->size++;
}
void QuenePop(Quene* pq)
{
assert(pq);
assert(!QueneEmpty(pq));
if (pq->phead->next == NULL)
{
free(pq->phead);
pq->phead = NULL;
}
else
{
QNode* cur = pq->phead->next;
free(pq->phead);
pq->phead = cur;
}
pq->size--;
}
QDatatype QueneFront(Quene* pq)
{
assert(pq);
assert(!QueneEmpty(pq));
return pq->phead->data;
}
QDatatype QueneBack(Quene* pq)
{
assert(pq);
assert(!QueneEmpty(pq));
return pq->ptail->data;
}
int QueneSize(Quene* pq)
{
assert(pq);
return pq->size;
}