<データ構造>キューの実装

コンテンツ

序文

        キューの概念

        キューの構造

        キューアプリケーションシナリオ

キューの実装

        キュー構造を作成する

        キューの初期化

        キューの破棄

        エンキュー

        デキュー

        キューが空です

        キュー要素の数を取得する

        キューヘッド要素を取得します

        キューの最後にある要素を取得します

トータルコード

        Queue.hファイル

        Queue.cファイル

        Test.cファイル


序文

キューの概念

  • キュー:一方の端でのデータ挿入操作ともう一方の端での削除データ操作のみを許可する特別な線形テーブル。キューには先入れ先出し(先入れ先出し)
  • エンキュー:挿入操作を実行する端は、キューのテールと呼ばれます
  • デキュー:削除操作を実行する端は、キューの先頭と呼ばれ

キューと前の記事で学習したスタックの間にはまだ一定の違いがあります。キューは先入れ先出しを明確に示しています。キューのエントリの順序がABCDの場合、デキューの順序はABCDである必要があります。これは、Aで出入りし、次にBで出入りし、次にCDで出入りするかどうかに関係なく、影響がないためです。その最終的なデキュー順序ABCD。これはまだスタックとは異なります。結局のところ、スタックはLIFOです。

キューの構造

キューアプリケーションシナリオ

列:

  1. 均等化キューイング
  2. 幅優先走査...

スタック:

  1. ブラケットマッチングを解決する
  2. 逆ポーランド記法ソルバー
  3. 再帰から非再帰へ..。

キューの実装

  • 実装する前に、まず、配列構造とチェーン構造のどちらを使用するかを検討する必要があります。上で使用したスタックは配列構造ですが、キューも使用する必要がありますか?
  • 実際にはそうではありません。連鎖構造を使用する必要があります。以前のスタック削除データはデータを移動する必要がなく、配列構造を使用してニーズを満たすことができ、データを削除するときにキューは次のデータを先頭に移動する必要があります。チェーン構造を使用するのは非常に簡単です。ノードポイントと配列を変更するだけです。構造がデータの移動を実現するのは非常に面倒です。要約すると、チェーン構造の使用が最適です。さらに、単一リンクリストは要件を満たすことができ、他のより複雑なチェーン構造を使用する必要はありません。

キュー構造を作成する

  •  アイデア:

ここでは、各ノードを記録するチェーン構造を定義するだけでなく、キューの先頭と末尾を記録する構造を定義するために、2つの構造を定義します。このように、キューの後続のテールがデータを入力し、キューの先頭がデータを出力するのに便利です。

  • Queue.hファイル:
//创建队列结构
typedef int QDataType; //方便后续更改存储数据类型,本文以int为例
 //创建队列节点
typedef struct QueueNode
{
	QDataType data; //存储数据
	struct QueueNode* next; //记录下一个节点
}QNode;
 //保存队头和队尾
typedef struct Queue
{
	QNode* head; //头指针
	QNode* tail; //尾指针
}Queue;

キューの初期化

  •  アイデア:

キューは空にすることができますが、ヘッドポインターとテールポインターを管理する構造体を空にすることはできないため、最初にアサートする必要があります。次に、データを挿入する前にキューを空にする必要があるため、ヘッドポインタとテールポインタを空にするだけです。

  • Queue.hファイル:
//初始化队列
void QueueInit(Queue* pq);
  • Queue.cファイル:
//初始化队列
void QueueInit(Queue* pq)
{
	assert(pq);
	pq->head = pq->tail = NULL;
}

キューの破棄

  •  アイデア:

キューを破棄すると、キュー内のすべてのデータが破棄されます。次に、リンクリストをトラバースして、空きデータを1つずつ破棄する必要があります。まず、curポインタをpq-> headとして定義します。これは、最初のデータを保存し、curをトラバースし、空でない場合は解放するために使用されます。最後に、尻尾と頭を空のままにします。

  • Queue.hファイル:
//销毁队列
void QueueDestory(Queue* pq);
  • Queue.cファイル:
//销毁队列
void QueueDestory(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->head;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	pq->head = pq->tail = NULL;
}

エンキュー

  •  アイデア:

キューへの入力は実際には非常に簡単です。テールを挿入するだけで済みます。最初に、新しく挿入されたデータを保存するために新しいノードを作成する必要があります。ただし、テールを挿入する前に、キューの先頭にデータがなく、空の場合は、ヘッドノードとテールノードを新しいノードのnewnodeノードにポイントするだけでよいことを考慮する必要があります。逆に、最初にデータがある場合は、テールの次のノードを新しいノードのnewnodeにポイントしてから、新しいノードをテールに割り当てるだけで済みます。

  • Queue.hファイル:
//入队列
void QueuePush(Queue* pq, QDataType x);
  •  Queue.cファイル:
//入队列
void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	//创建一个新节点保存数据
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	//暴力检测newnode,因为malloc的都要检测
	assert(newnode);
	newnode->next = NULL;
	newnode->data = x;
	//如果一开始没有数据,为空的情况
	if (pq->tail == NULL)
	{
		assert(pq->head == NULL);
		pq->head = pq->tail = newnode;
	}
	else
	{
		pq->tail->next = newnode;
		pq->tail = newnode;
	}
}

デキュー

  •  アイデア:

特殊なケース:

ここで、データを削除する場合は、まず特殊なケースを考慮する必要があります。データが1つしか残っていない場合は、もう一度削除します。このとき、データは削除されますが、ヘッドは空で、テールはワイルドポインタになります。この現象を回避します。生成は、個別に説明され、頭と尾が空になります。

一般的:

この時点で、ヘッドの次のノードを保存し、ヘッドを次のノードに移動し、古いヘッドを空にするために、次のポインタを定義するだけで済みます。

  •  Queue.hファイル:
//出队列
void QueuePop(Queue* pq);
  • Queue.cファイル:
//出队列
void QueuePop(Queue* pq)
{
	assert(pq);
	assert(pq->head && pq->tail); //tail和head均不能为空
	//特殊:当删到head=tail的位置时
	if (pq->head->next == NULL)
	{
		free(pq->head);
		pq->head = pq->tail = NULL;
	}
	//一般情况
	else
	{
		//保存head的下一个节点
		QNode* next = pq->head->next;
		free(pq->head);
		pq->head = next;
	}
}

キューが空です

  •  アイデア:

頭が空であるか尾が空である場合、それは空の状態であり、直接戻るだけです。

  • Queue.hファイル:
//判空
bool QueueEmpty(Queue* pq);
  • Queue.cファイル:
//判空
bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->head == NULL;
}

キュー要素の数を取得する

  •  アイデア:

要素の数を見つけることは難しくありません。最初のデータpq->headとしてcurポインターを定義し、その数を記録するための変数サイズを定義するだけです。順番にcurをトラバースします。空でない場合、サイズは++です。このトラバーサルのアイデアは複雑ではありませんが、時間計算量はO(N)に達しますが、これはあまり良くありません.O(1)が必要な場合は、構造を定義するときに追加のサイズ変数を直接定義できます最初は、有効な要素の数を記録するために特別に使用されます。キューサイズから毎回キューサイズ++に入る数--。この実装の方が優れていますが、独立したモジュールにカプセル化するために、トラバーサルメソッドが引き続き使用されます。次のように:

  • Queue.hファイル:
//获取有效元素个数
size_t QueueSize(Queue* pq);
  • Queue.cファイル:
//获取有效元素个数
size_t QueueSize(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->head;
	size_t size = 0;
	while (cur)
	{
		size++;
		cur = cur->next;
	}
	return size;
}

キューヘッド要素を取得します

  •  アイデア:

まず、ヘッドを空にすることはできないと断言する必要があります。ヘッドが空の場合、ヘッド要素を取得して、ヘッドのデータを直接返すにはどうすればよいですか。

  • Queue.hファイル:
//获取队头元素
QDataType QueueFront(Queue* pq);
  • Queue.cファイル:
//获取队头元素
QDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(pq->head); //头部不能为空
	return pq->head->data;
}

キューの最後にある要素を取得します

  •  アイデア:

キューの先頭の要素を取得した経験により、キューの末尾が単純になり、先頭を末尾に変更するだけで、構造は上記と同じになります。

  • Queue.hファイル:
//获取队尾元素
QDataType QueueBack(Queue* pq);
  • Queue.cファイル:
//获取队尾元素
QDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(pq->tail); //尾部不能为空
	return pq->tail->data;
}

トータルコード

Queue.hファイル

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>

//创建队列结构
typedef int QDataType; //方便后续更改存储数据类型,本文以int为例
 //创建队列节点
typedef struct QueueNode
{
	QDataType data; //存储数据
	struct QueueNode* next; //记录下一个节点
}QNode;
 //保存队头和队尾
typedef struct Queue
{
	QNode* head; //头指针
	QNode* tail; //尾指针
}Queue;

//初始化队列
void QueueInit(Queue* pq);

//销毁队列
void QueueDestory(Queue* pq);

//入队列
void QueuePush(Queue* pq, QDataType x);

//出队列
void QueuePop(Queue* pq);

//判空
bool QueueEmpty(Queue* pq);

//获取有效元素个数
size_t QueueSize(Queue* pq);

//获取队头元素
QDataType QueueFront(Queue* pq);

//获取队尾元素
QDataType QueueBack(Queue* pq);

Queue.cファイル

#define _CRT_SECURE_NO_WARNINGS 1
#include"Queue.h"

//初始化队列
void QueueInit(Queue* pq)
{
	assert(pq);
	pq->head = pq->tail = NULL;
}

//销毁队列
void QueueDestory(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->head;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	pq->head = pq->tail = NULL;
}

//入队列
void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	//创建一个新节点保存数据
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	//暴力检测newnode,因为malloc的都要检测
	assert(newnode);
	newnode->next = NULL;
	newnode->data = x;
	//如果一开始没有数据,为空的情况
	if (pq->tail == NULL)
	{
		assert(pq->head == NULL);
		pq->head = pq->tail = newnode;
	}
	else
	{
		pq->tail->next = newnode;
		pq->tail = newnode;
	}
}

//出队列
void QueuePop(Queue* pq)
{
	assert(pq);
	assert(pq->head && pq->tail); //tail和head均不能为空
	//特殊:当删到head=tail的位置时
	if (pq->head->next == NULL)
	{
		free(pq->head);
		pq->head = pq->tail = NULL;
	}
	//一般情况
	else
	{
		//保存head的下一个节点
		QNode* next = pq->head->next;
		free(pq->head);
		pq->head = next;
	}
}

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

//获取有效元素个数
size_t QueueSize(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->head;
	size_t size = 0;
	while (cur)
	{
		size++;
		cur = cur->next;
	}
	return size;
}

//获取队头元素
QDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(pq->head); //头部不能为空
	return pq->head->data;
}

//获取队尾元素
QDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(pq->tail); //尾部不能为空
	return pq->tail->data;
}

Test.cファイル

#define _CRT_SECURE_NO_WARNINGS 1
#include"Queue.h"
void TestQueue()
{
	Queue q;
	QueueInit(&q);
	//插入数据
	QueuePush(&q, 1);
	QueuePush(&q, 2);
	QueuePush(&q, 3);
	QueuePush(&q, 4);
	//打印
	while (!QueueEmpty(&q))
	{
		printf("%d ", QueueFront(&q));
		QueuePop(&q);
	}
	printf("\n");
}
int main()
{
	TestQueue();
	return 0;
}

おすすめ

転載: blog.csdn.net/bit_zyx/article/details/123967787