データ構造(C言語実装) - スタックとキューの導入と基本操作の実現(動的シーケンススタック+チェーンチーム)

1 はじめに

今日は、スタックとキューの 2 つの線形構造について学習します. スタックとキューは、操作が制限された線形テーブルであるため、制限されたデータ構造と呼ぶことができます.

2.積み重ねる

スタック: 固定端での要素の挿入と削除のみを許可する特別な線形リスト。データの挿入および削除操作が実行される一方の端は
スタックのトップと呼ばれ、もう一方の端はスタックのボトムと呼ばれます。スタック内のデータ要素は、LIFO (Last In First Out) の原則に従います。
プッシュ スタック: スタックの挿入操作はプッシュ/プッシュ/プッシュと呼ばれ、着信データはスタックの一番上にあります。
ポッピング: スタックの削除操作はポッピングと呼ばれ、データもスタックの一番上にあります。

スタックの実装は、通常、配列またはリンクされたリストを使用して実装できます. 比較的言えば、配列の構造は、配列の最後にデータを挿入するコストが比較的小さいため、より優れています. さらに、固定長の静的スタックは実際には一般的に実用的ではないため、以下で主に実装するのは、動的な拡張をサポートする順次スタックです。

2.1 構造定義

スタックの現在の最上部の位置と現在のスタックの最大容量を記録します。

//静态栈
//#define N 10
//typedef int STDataType;

//typedef struct Stack
//{
    
    
//	int a[N];
//	int top;
//}ST;

//动态栈
typedef int STDataType;

typedef struct Stack
{
    
    
	STDataType* a;
	int top;//栈顶
	int capacity;//容量
}ST;

2.2 スタックの初期化と破棄

初期化:

void StackInit(ST* ps)
{
    
    
	assert(ps);
	ps->a = NULL;
	ps->top = 0;
	ps->capacity = 0;
}

破壊:

void StackDestory(ST* ps)
{
    
    
	assert(ps);
	free(ps->a);
	ps->capacity = 0;
	ps->top = 0;
}

2.3 プッシュアンドポップ

スタックにプッシュするときは、最初にスタックがいっぱいかどうかを判断し、それがスタックの最初の要素であるかどうかにも注意してから、動的にメモリを開く必要があります。最後に、要素が現在のスタックの一番上に格納され、スタックの一番上が ++ になります。

スタックに:

void StackPush(ST* ps, STDataType x)
{
    
    
	assert(ps);
	if (ps->capacity == ps->top)
	{
    
    
		int newcapacity = (ps->capacity == 0) ? 4 : (2 * (ps->capacity));
		STDataType* tmp = (STDataType*)realloc(ps->a, sizeof(STDataType) * newcapacity);
		if (tmp == NULL)
		{
    
    
			perror("realloc");
			exit(-1);
		}
		ps->a = tmp;
		ps->capacity = newcapacity;
	}
	ps->a[ps->top] = x;
	ps->top++;
}

スタックをポップアウトするのは非常に簡単ですが、スタックが空であるかどうかに注意してください。空の場合、スタックは失敗します。それ以外の場合は、スタックの一番上が直接になる可能性があります。

ポップ:

void StackPop(ST* ps)
{
    
    
	assert(ps);
	assert(!StackEmpty(ps));
	ps->top--;
}

2.4 スタックの一番上の要素を取得する

空のスタックかどうかに注意してください

STDataType StackTop(ST* ps)
{
    
    
	assert(ps);
	assert(!StackEmpty(ps));
	return ps->a[ps->top - 1];
}

2.5 スタックが空かどうかを判断する

スタックの一番上の現在の位置は 0 です。これは、スタックが空であることを意味します。

bool StackEmpty(ST* ps)
{
    
    
	assert(ps);
	return ps->top == 0;
}

2.6 スタック内の要素の数を見つける

スタックの一番上にある現在の位置は要素の数です

int StackSize(ST* ps)
{
    
    
	assert(ps);
	return ps->top;
}

スタックの概念と基本操作の紹介の後、キューについて学びましょう。

3.キュー

キュー: 一方の端でデータを挿入し、もう一方の端でデータを削除することのみを許可する特殊な線形テーブル. キューには FIFO (先入れ先出し) の原則があります。
Enqueue: 挿入操作の最後は、キューの最後と呼ばれます。
キューの外: 削除操作の終了は、キューの先頭と呼ばれます。

キューは配列や連結リストの構造でも実装できますが、配列の構造を使うと、配列の先頭のデータをデキューして出力する効率が相対的に悪くなるので、連結リストの構造を使う方が良いです。低い。したがって、以下で主に実装するのはチェーンチームです。

3.1 構造定義

キューの各ノードには、データ フィールドと次のノードを指すポインター フィールドがあり、各キューにはヘッド ポインターとテール ポインターがあります。

//结点定义
typedef int QDataType;

typedef struct QueueNode
{
    
    
	QDataType data;
	struct QueueNode* next;
}QNode;

//队列结构
typedef struct Queue
{
    
    
	QNode* head;
	QNode* tail;
}Queue;

3.2 キューの初期化と破棄

キューの先頭ポインタと末尾ポインタを NULL に設定します。

初期化:

void QueueInit(Queue* pq)
{
    
    
	assert(pq);
	pq->head = NULL;
	pq->tail = NULL;
}

破棄するときは、各ノードを解放する必要があり、最終的にヘッド ポインターとテール ポインターは空になります。

破壊:

void QueueDestroy(Queue* pq)
{
    
    
	assert(pq);
	QNode* cur = pq->head;
	while (cur)
	{
    
    
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	pq->head = NULL;
	pq->tail = NULL;
}

3.3 エンキューとデキュー

待ち行列に入るときは, 動的にメモリを開放し, 待ち行列の最初の要素かどうかの判断に注意を払う必要がある. そうであれば, ヘッドポインタとテールポインタの両方がそれを指している. そうでなければ, あなただけテールポインターを変更する必要があります。

エンキュー:

void QueuePush(Queue* pq, QDataType x)
{
    
    
	assert(pq);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
    
    
		perror("malloc");
		exit(-1);
	}
	newnode->next = NULL;
	newnode->data = x;
	if (pq->tail == NULL)
	{
    
    
		pq->head = newnode;
		pq->tail = newnode;
	}
	else
	{
    
    
		pq->tail->next = newnode;
		pq->tail = newnode;
	}
}

キューから出るときは空のキューかどうかに注意しなければなりません. そうでない場合はキューの最後の要素かどうかを判断する必要があります. そうであれば, キューを離れた後にヘッドとテールのポインタを空に設定する必要があります.そうでない場合は、ヘッド ポインタを変更するだけで済みます。

デキュー:

void QueuePop(Queue* pq)
{
    
    
	assert(pq);
	assert(!QueueEmpty(pq));
	if (pq->head->next == NULL)
	{
    
    
		free(pq->head);
		pq->head = NULL;
		pq->tail = NULL;
	}
	else
	{
    
    
		QNode* next = pq->head->next;
		free(pq->head);
		pq->head = next;
	}
}

3.4 キューの先頭要素と末尾要素を取得する

ここは非常に簡単で、空キューかどうかの判断に注意するだけです。

チーム長:

QDataType QueueFront(Queue* pq)
{
    
    
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->head->data;
}

しっぽ:

QDataType QueueBack(Queue* pq)
{
    
    
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->tail->data;
}

3.5 キューが空かどうかを判断する

キューの先頭または末尾のポインターが空の場合、キューは空です。

bool QueueEmpty(Queue* pq)
{
    
    
	assert(pq);
	return pq->head == NULL;
}

3.6 キュー内の要素の数を見つける

要素の数は、キューをトラバースすることで取得できます。

int QueueSize(Queue* pq)
{
    
    
	assert(pq);
	QNode* cur = pq->head;
	int size = 0;
	while (cur)
	{
    
    
		cur = cur->next;
		size++;
	}
	return size;
}

キューの概念と基本操作も紹介されています。

4.エンディング

シーケンス テーブルとリンク リストについては以前に学習したので、ここでのスタックとキューの操作は比較的簡単に実装できます.主なことは、スタックとキューの特性を理解し、適切な構造を適切な場所で使用することです.アイデアは最も重要な重いです。

最後に, 忍耐強い読書とサポートに感謝したいと思います. この記事がよく書かれていると思う友人は、フォローして3回サポートすることができます. この記事に質問がある場合や間違いがある場合は, 私にプライベートメッセージを送るか、コメント欄ディスカッションにメッセージを残してください。

おすすめ

転載: blog.csdn.net/qq_43188955/article/details/130154150