(詳細説明) データ構造 ----------- スタックとキュー C言語実装

この章では、次の知識ポイントについて詳しく説明します。

目次

1: スタック

        1: スタックの定義、スタックの特性

        2: スタックの分析を実現するための構造とその理由は何ですか?

        3:(超詳しく解説)スタックテストケースとアタッチテストケースの共通インターフェース

2: キュー

        1: キューの定義、キューの特徴

        2: キューと理由の分析を実現するためにどのような構造が使用されていますか?

        3:(超詳しい解説)キューとアタッチテストケースのインターフェース


本文が始まります~:-^-

        1: スタックの定義と特徴

        まず、スタックは特殊な線形テーブルであり、一方の端でのみ挿入削除の操作を行うことができ、データの削除挿入を行う端をスタックの先頭、もう一方の端をスタックの底と呼びますスタック内のデータ要素は、後入れ先出しの原則に従います。

        以下はスタックの操作の名前です。

                スタックのプッシュ: データ要素がスタックに挿入されることを示します。プッシュ/プッシュとも呼ばれます。受信データはスタックの最上位にあります。

                ポップアップ: スタック内のデータ要素を削除する操作を表します。出力データはスタックの最上位にあります。

                スタックの特性:データ要素はスタック一端のみ挿入および削除でき、データ要素は後入れ先出しの原則に従います。

        2: スタックを実装するために使用される構造とその理由は何ですか?

        シーケンス テーブルとリンク リストの長所と短所を比較した結果、スタックを実装する際にはシーケンス テーブルを使用してスタックのデータ構造を実装することを好みます。もちろん、リンクリストを使用して実装することもできます。

        理由: スタックの先入れ後出し機能に従って、スタックの最上位にあるデータ要素のみを挿入および削除できます。

        シーケンステーブルの末尾挿入と末尾削除の計算量はO(1) であり、シーケンステーブルのキャッシュ使用率はリンクリストよりも速いため、この構造はこれに非常に適しています。シーケンステーブルの方がリンクリストよりも優れています。

         3: (詳細) スタックの共通インターフェイス

        スタックの一般的な操作は、スタックの初期化、スタックの破壊、スタックのプッシュ、スタックのポップ、およびスタックの最上位要素の値の取得です。

        スタックが空かどうか、スタックのサイズの解釈

        これらのインターフェースを説明する前に、まずスタックの定義について説明します。

        3 つのメンバー変数が含まれます。S はオープンスペースの最初のアドレスです。top は要素数を記録するために使用され、配列内の最後の要素の次の位置です。capacity は配列スペースのサイズを示すために使用されます。 。

        コードは以下のように表示されます。

//动态的顺序栈
typedef int STDataType;

typedef struct Stack
{
	STDataType* S;
	int top;//用来表示栈中最后一个元素的下一个元素的位置
	int capacity;
}ST;

        1: スタックの初期化: 3 つのメンバー変数の値をすべて空または 0 にします

        コードは以下のように表示されます。

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

        2: スタックプッシュ操作: スタックにプッシュする前に、スペースが十分であるかどうかを判断し、シーケンステーブルのランダムアクセス機能を直接使用してスタックをエンドインサートする必要があります。スタックが完成しました。

次のコードには非常に本質があります。初期化時に配列用のスペースをオープンしなかったため、三項演算子を巧みに使用して、新しく        オープンしたスペースのサイズを割り当てました。最初の場合は、サイズを作成したら空間を4つに分割したら、その後は2倍ずつ空間を拡張していきます。

         コードは以下のように表示されます。

        

void PushStack(ST* ps, STDataType x)
{
	assert(ps);
	//先检查空间够不够
	if (ps->top == ps->capacity)
	{
		int newcapcity = (ps->capacity == 0 ? 4 : 2 * ps->capacity);
		STDataType* tmp = (STDataType*)realloc(ps->S, sizeof(STDataType) * newcapcity);
		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}
		ps->S = tmp;
		ps->capacity = newcapcity;
	}
	ps->S[ps->top] = x;
	++ps->top;
}

         3: スタックのポップ操作: ここでは、最初にスタックが空かどうかを判断するために暴力的な解決策を使用する必要があります。空の場合はエラーを報告し、空でない場合はポップ操作を実行できます。私たちの頂点――。

        コードは以下のように表示されます。

void PopStack(ST* ps)
{
	assert(ps);
	//非空
	assert(ps->top > 0);

	ps->top--;

}

        4: スタックの最上位要素を取得する操作: まず、スタックが空かどうかを判断する必要があります。空の場合、このインターフェイスはエラーを報告します。

        このコードで注意する必要があるのは、スタックの最上位要素の添え字がtop ではなくtop-1であることです。

        コードは以下のように表示されます。

        

STDataType GetStack(ST*ps)
{
	assert(ps);
	//非空
	assert(ps->top > 0);

	return ps->S[ps->top-1];
}

        5: スタックのサイズ: トップに戻るだけです。

        コードは以下のように表示されます。

        

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

        6: スタックが空かどうかを判断します: top が 0 の場合、スタックに要素が存在しないことを意味し、 trueを返します。要素が存在する場合は、trueを返します。ここでは、これら 2 つのキーワードがないことに注意してくださいC 言語では、ヘッダー ファイル <stdbool .h> を導入する必要があります。

        コードは以下のように表示されます。

        

bool EmptyStack(ST* ps)
{
	assert(ps);
	  //这里代码的意思是如果top为0,则返回真,不为0返回false
    return ps->top == 0;
  
}

        スタックの特性を反映するために、スタック内のデータを出力するときに、配列を直接走査しません。

        私たちは--->を使用しています

        

while (!EmptyStack(&st))
	{
		printf("%d ", GetStack(&st));
		PopStack(&st);
	}

        ここでは、スタックの最上位の要素が毎回取得され、スタックの最上位の要素が削除されます。この方法でのみスタックの特性を反映できます。

        

        図:

         

        スタックの詳細な説明はここで終わります。



    1: キューの定義と特徴

キューの定義: キューも特別な線形テーブルです。一端で挿入を実行しもう一端で削除のみ        を実行できます。挿入された端をキューの末尾と呼び削除された端をキューのヘッドと呼びます。この構造は非常に優れています私たちが食堂に並んでいる状況を想像すると、誰かが来たら列の最後尾に入れられ、食べ終わった人は先頭から列から抜けていきます。

        デキュー:キューの先頭にある要素を削除する操作

        エンキュー:キューの最後にデータを挿入する操作。

       キューの機能:先入れ先出しの原則に従って、削除操作はキュー先頭でのみ実行でき、挿入操作はキュー最後で実行できます

        写真:

        

         2: キューを実現するためにどのような構造が使用されているのか、その理由は何ですか?

        最初に答えを発表します。シーケンス テーブルと比較して、通常、キューのデータ構造を実装するためにリンク リストを使用します。

        理由: 先頭を削除する場合はリンク リストの方が便利ですが、末尾を挿入する場合は末尾ポインタを定義するだけで済み、時間を節約できます。

        3: (詳細) キューの共通インターフェース

        キューのインターフェースには、キューの初期化、キューの破棄、エンキュー操作、デキュー操作、キューが空かどうかの判断が含まれます。

head要素を取得し、tail要素を取得し、キューのサイズを判断します。 

       まず、キューの構造コード分析を理解しましょう。

typedef int QueueDataType;

typedef struct QNode
{
	struct QNode* next;
	QueueDataType data;
}QNode;

typedef struct Queue
{
	QNode* head;
	QNode* tail;
	int size;//用来记录有多少个结点,可以直接算大小
}Que;

     まず、キューの各要素はリンクリストの基本構造であり、キューを表すにはリンクリストの先頭ノードと末尾ノードを記録するだけでよく、通常はパラメータを渡すときにアドレスを渡すだけなので、先頭ポインタと末尾ポインタを構造体定義し、キュー要素の数を記録するメンバー変数sizeを追加するだけです。

            2 つのポインターを同じ構造体に入れる利点の 1 つは、構造体ポインターを介してポインターの値を変更するだけで済むことです。それ以外の場合は、値を変更するために 2 次ポインターを使用する必要があることです。

 

        1: キューの初期化: あなたの初期化が私のものと異なる場合、次のインターフェースの手順にいくつかの違いが生じる可能性があります。初期化は一意ではありません

        コードは以下のように表示されます。

            

void QueueInit(Que* pq)
{
	assert(pq);
	pq->head = pq->tail = 0;
	pq->size = 0;
}

        2: エンキュー操作: ここには 2 つのケースがあります。1 つ目は、最初の要素を挿入するときに 2 つのポインタのポインタを変更する必要があることです。もう 1 つのケースは、末尾ポインタの後に 1 つを挿入するだけで済みます。新しいノードだけで十分ですが、同時に末尾ポインタで新しいノードを指し、挿入後の size++ を指定します。

        コードは以下のように表示されます。

        

void QueuePush(Que* pq, QueueDataType x)
{
	assert(pq);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail:");
		exit(-1);
	}
	
	//为第一个结点的时候
	if (pq->head == NULL)
	{
		pq->head = pq->tail = newnode;
	}
	//不是第一个结点
	else
	{
		pq->tail->next = newnode;
	}
	pq->tail = newnode;
	newnode->data = x;
	newnode->next = NULL;
	pq->size++;
}

        3: デキュー操作: まず、キュー内にデータ要素があるかどうかを判断する必要があります。2 つのタイプがあります。1 つはキュー内に要素が 1 つだけある場合、その要素を削除する必要がある場合、そしてhead と tail の値を空に設定します。

        コードは以下のように表示されます。

void QueuePop(Que* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	//只有一个结点的时候
	if (pq->head->next == NULL)
	{
		free(pq->head);
		pq->head = pq->tail = NULL;
	}
	else
	{
		QNode* phead = pq->head->next;
		free(pq->head);
		pq->head = phead;
	}
	pq->size--;

       4: キューの先頭要素を取得します。これは非常に簡単です。まずキューが空かどうかを判断し、空でない場合は head->data を返すだけです。それだけです。

        コードは以下のように表示されます。

QueueDataType QueueFront(Que* pq)
{
	assert(pq);//判断pq是否有意义
	assert(!QueueEmpty(pq));

	return pq->head->data;
}

        5: キューの末尾要素を取得します。上記のアイデアと同様に、末尾ポインタが指すデータ要素を返すだけで済みます。

        

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

        6: キューが空かどうかを判断します。キューが空の場合、ヘッド ポインタは空です。ヘッドが NULL の場合は true を返し、それ以外の場合は false を返します。

        コードは以下のように表示されます。

               

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

        7: キュー内の要素数の判断: 定義した構造に従ってキューに入ることができ、サイズを返すだけで済みます。

        

int QueueSize(Que* pq)
{
	assert(pq);
	return pq->size;
}

        場合によってはスタックをキューに変換したり、キューをスタックに変換したりすることもできますが、どのように変換されるかを考えてみましょう。


 

 !この章は終わりました。辛抱強く見ていただきありがとうございました。

        

おすすめ

転載: blog.csdn.net/2201_75964502/article/details/132567454