データ構造+ヘッドノード二重循環リンクリスト+(C言語)

見出し付き二重循環リンクリストの基本機能を説明する 

1.構造の定義

 a。最初は構造を定義し、最初の行はtypedefを使用して名前を変更し(その後の変更を容易にするため)、次に構造内のコンテンツの定義まず、データフィールドを使用してデータを格納します。二重循環リンクリスト、定義する必要があります2つのポインター、1つは後ろを指す_nextポインター、もう1つは前を指す_prevポインターです。typedefは構造体定義の前に追加されるため、最後の変数ListNode structListNodeの役割を置き換えることです。

typedef int LTDataType;
typedef struct ListNode
{
	LTDataType _data;
	struct ListNode* _next;
	struct ListNode* _prev;
}ListNode;

2.ノードを作成します

a。セカンダリポインタを使用しないため、関数の戻り値をlistnode *タイプに設定し、ノードの作成後にそのアドレスを返します。最初に、要求されたスペースアドレスを格納するポインタ変数を作成し、次にスペースを実行します。適用すると、後の操作のためにフロントポインタとバックポインタがそれぞれnullを指すようになり、渡されたパラメータxはデータフィールドの値であり、最後に直接割り当てます。その後、アドレスを返し、後続の操作を容易にします。

ListNode* ListCreate(LTDataType x) {
	ListNode* node = (ListNode*)malloc(sizeof(ListNode));
	node->_next = NULL;
	node->_prev = NULL;
	node->_data = x;
	return node;
}

3.初期化

a。初期化はヘッドノードを改善するために使用されます。これは、ヘッドノードが二重循環リンクリストであるため、このヘッドノードは有効なデータを伝送できないため、0に設定します。これは、二重循環リンクリストであるため、挿入する必要があります。または、テール挿入データのノーズブリードは、ヘッドノードを処理して、フロントポインターとリアポインターがそれ自体を指すようにし、後でデータを挿入するときに完了できるようにします(この初期化プロセスはヘッドノード専用です)。

void ListNodeInit(ListNode* phead) {
	phead->_next = phead;
	phead->_prev = phead;
}

4.二重リンクリストの印刷

a。ノードを印刷する必要があるので、ヘッドノードの最後から印刷を開始する必要があるため、ヘッドノードの後の最初のデータノードを格納するcurポインターを作成し、whileループを使用してトラバースして印刷します。はcurの次であるため、最後のデータノードは印刷できないため、ループが終了すると、最後のノードを手動で印刷します(もちろん、判定条件がある程度処理されていれば、オールインで印刷することもできます。ただし、ここでは詳しく説明しません)。

void ListPrint(ListNode* pHead) {
	if (NULL == pHead) {
		return;
	}
	ListNode* cur = pHead->_next;
	while (cur->_next != pHead) {
		printf("%d--->", cur->_data);
		cur = cur->_next;
	}
	printf("%d", cur->_data);
	printf("\n");
}

5.二重リンクリストのテール挿入

a。挿入する前に、入力パラメーターが有効かどうかを判断します。空でない場合は、後続の操作を実行し、最初に前のノード作成関数によって作成されたスペースの最初のアドレスを格納するポインター変数curを定義し、次にこれはテール挿入であるため、テールノードを見つける必要があります。リンクリストは二重循環リンクリストであるため、ヘッドノードの前のノードをテールノードとして取得し、ポインタ変換を実行して挿入するだけで済みます。最後の新しいノード(図に示すように)

 

void ListPushBack(ListNode* pHead, LTDataType x) {
	if (NULL == pHead) {
		return;
	}
	ListNode* cur = ListCreate(x);
	ListNode* tail = pHead->_prev;
	tail->_next = cur;
	pHead->_prev = cur;
	cur->_next = pHead;
	cur->_prev = tail;
}

6.二重リンクリストのテールの削除

a。削除する前に、入力パラメータが有効かどうかを判断します。空でない場合は、フォローアップ操作を実行します。テール挿入と同様に、最初にテールノードを見つけてテールとして記録し、次にポインタを変換して、削除後に解放します。スペース(詳細は写真を参照)

void ListPopBack(ListNode* pHead) {
	if (NULL == pHead || pHead->_next == pHead) {
		return;
	}
	ListNode* tail = pHead->_prev;
	pHead->_prev = pHead->_prev->_prev;
	pHead->_prev->_next = pHead;
	free(tail);
}

7.二重リンクリストヘッダー

a ..ヘッダー挿入を行う前に、最初に入力パラメーターが有効かどうかを判断します。空でない場合は、後続の操作で、前のノード作成関数によって作成されたスペースの最初のアドレスを格納するポインター変数curを最初に定義します。 、そして次にヘッドプラグであるため、ヘッドの次のデータノードを見つけて、ポインタ変換を実行して挿入する必要があります(図を参照)。

void ListPushFront(ListNode* pHead, LTDataType x) {
	if (NULL == pHead) {
		return;
	}
	ListNode* cur = ListCreate(x);
	ListNode* Next = pHead->_next;
	pHead->_next = cur;
	cur->_next = Next;
	Next->_prev = cur;
	cur->_prev = pHead;
}

8.二重リンクリストヘッダーの削除

a。削除する前に、まず入力パラメータが有効かどうかを判断します。空でない場合は、フォローアップ操作が実行されます。ヘッド挿入と同様に、最初にヘッドの次のデータノードcurを見つけ、次にヘッドを見つけます。 2番目のデータノード、つまり、ヘッドの次のヘッドの次のノードは、関連するポインタ操作を実行し、最後にcurを解放します(図を参照)。

void ListPopFront(ListNode* pHead) {
	if (NULL == pHead || pHead->_next == pHead) {
		return;
	}
	ListNode* cur = pHead->_next;
	ListNode* Next = pHead->_next->_next;
	pHead->_next = Next;
	Next->_prev = pHead;
	free(cur);
}

9.二重リンクリストルックアップ

a。ここで関数を定義するときは、戻り値をlistnode *タイプとして設定し、対応するパラメーターxを渡し、whileループを使用してリンクリスト全体をトラバースし、データフィールドがxに等しいノードを見つけて、のアドレスを返します。このノードが見つからない場合は、nullを返します。

ListNode* ListFind(ListNode* pHead, LTDataType x) {
	if (NULL == pHead || pHead->_next == pHead) {
		return NULL;
	}
	ListNode* temp = pHead->_next;
	while (temp != pHead) {
		if (temp->_data == x) {
			return temp;
		}
		temp = temp->_next;
	}
	return NULL;
}

10.二重リンクリストがposの前に挿入されます

a。ヘッドプラグと同様に、操作を行う前に、まず位置を見つけて次の操作で見つけることができるかどうかを判断します。ここで、上記の検索機能を使用する場合は、挿入したい位置を見つけてから挿入します。次に、これを見つけます。位置の前のノードはprvとして記録され、新しく作成されたノードはcurを使用してそのアドレスを格納します。次に、ポインターが変換され、次のprvがcurを指し、ノードposの前のノードがこの位置はcurを指し、次にcurがあります。prevはprvを指し、次のcurはposを指し、挿入が完了します。

void ListInsert(ListNode* pos, LTDataType x) {
	if (NULL == pos) {
		printf("插入数据的位置不存在!\n");
		return;
	}
	ListNode* cur = ListCreate(x);
	ListNode* prv = pos->_prev;
	prv->_next = cur;
	pos->_prev = cur;
	cur->_prev = prv;
	cur->_next = pos;
}

11.二重リンクリストは、位置のノードを削除します

a。操作を実行する前に、位置が見つかり、後続の操作で見つけることができるかどうかを判断します。まず、検索関数を呼び出して挿入する位置を見つけてposとして記録し、次に前の位置を取得して次のように記録します。 prvを記録し、後者をNextとして記録し、ポインタを操作すると、次のprvがNextを指し、前のNextがprvを指し、最後にpos位置のノードのスペースが解放されます。正常に削除されました。

void ListErase(ListNode* pos) {
	if (NULL == pos) {
		printf("删除数据的位置不存在!\n");
		return;
	}
	ListNode* prv = pos->_prev;
	ListNode* Next = pos->_next;
	prv->_next = Next;
	Next->_prev = prv;
	free(pos);
}

12.リンクリストを破棄します

a。すべての操作が完了したら、リンクリストを破棄する必要があります。これは非常に簡単です。whileループを使用してheaddeleteまたはtaildelete関数を呼び出し、headノードを除くすべてのデータノードを削除してスペースを解放します(ここではヘッド削除を使用します)、ここでパラメーターを渡すときにセカンダリポインターを渡すことに注意してください。これは、ヘッドノードのスペースを解放した後、ヘッドポインターによって格納されたアドレスにスペースがないためです。ヘッドポインタは次のようになります。ワイルドポインタの場合、ワイルドポインタではないように、NULLに再割り当てする必要がありますが、ポインタの値を実際に変更するには、パラメータを渡すときに、変更する前にポインタのアドレスを渡す必要があります。したがって、セカンダリポインタ、つまり渡されたポインタのアドレスを使用します。

void ListDestory(ListNode** pHead) {
	if (NULL == (*pHead)) {
		return;
	}
	while ((*pHead)->_next!=*pHead) {
		ListPopFront(*pHead);
	}
	free(*pHead);
	(*pHead) = NULL;
}

最後は、一連のテストとテストのスクリーンショット、およびすべてのコードです。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
//结构体定义
typedef int LTDataType;
typedef struct ListNode
{
	LTDataType _data;
	struct ListNode* _next;
	struct ListNode* _prev;
}ListNode;
//创建结点
ListNode* ListCreate(LTDataType x) {
	ListNode* node = (ListNode*)malloc(sizeof(ListNode));
	node->_next = NULL;
	node->_prev = NULL;
	node->_data = x;
	return node;
}
//初始化
void ListNodeInit(ListNode* phead) {
	phead->_next = phead;
	phead->_prev = phead;
}
// 双向链表销毁
void ListDestory(ListNode** pHead) {
	if (NULL == (*pHead)) {
		return;
	}
	while ((*pHead)->_next!=*pHead) {
		ListPopFront(*pHead);
	}
	free(*pHead);
	(*pHead) = NULL;
}
// 双向链表打印
void ListPrint(ListNode* pHead) {
	if (NULL == pHead) {
		return;
	}
	ListNode* cur = pHead->_next;
	while (cur->_next != pHead) {
		printf("%d--->", cur->_data);
		cur = cur->_next;
	}
	printf("%d", cur->_data);
	printf("\n");
}
// 双向循环链表尾插
void ListPushBack(ListNode* pHead, LTDataType x) {
	if (NULL == pHead) {
		return;
	}
	ListNode* cur = ListCreate(x);
	ListNode* tail = pHead->_prev;
	tail->_next = cur;
	pHead->_prev = cur;
	cur->_next = pHead;
	cur->_prev = tail;
}
// 双向链表尾删
void ListPopBack(ListNode* pHead) {
	if (NULL == pHead || pHead->_next == pHead) {
		return;
	}
	ListNode* tail = pHead->_prev;
	pHead->_prev = pHead->_prev->_prev;
	pHead->_prev->_next = pHead;
	free(tail);
}
// 双向链表头插
void ListPushFront(ListNode* pHead, LTDataType x) {
	if (NULL == pHead) {
		return;
	}
	ListNode* cur = ListCreate(x);
	ListNode* Next = pHead->_next;
	pHead->_next = cur;
	cur->_next = Next;
	Next->_prev = cur;
	cur->_prev = pHead;
}
// 双向链表头删
void ListPopFront(ListNode* pHead) {
	if (NULL == pHead || pHead->_next == pHead) {
		return;
	}
	ListNode* cur = pHead->_next;
	ListNode* Next = pHead->_next->_next;
	pHead->_next = Next;
	Next->_prev = pHead;
	free(cur);
}
// 双向链表查找
ListNode* ListFind(ListNode* pHead, LTDataType x) {
	if (NULL == pHead || pHead->_next == pHead) {
		return NULL;
	}
	ListNode* temp = pHead->_next;
	while (temp != pHead) {
		if (temp->_data == x) {
			return temp;
		}
		temp = temp->_next;
	}
	return NULL;
}
// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x) {
	if (NULL == pos) {
		printf("插入数据的位置不存在!\n");
		return;
	}
	ListNode* cur = ListCreate(x);
	ListNode* prv = pos->_prev;
	prv->_next = cur;
	pos->_prev = cur;
	cur->_prev = prv;
	cur->_next = pos;
}
// 双向链表删除pos位置的节点
void ListErase(ListNode* pos) {
	if (NULL == pos) {
		printf("删除数据的位置不存在!\n");
		return;
	}
	ListNode* prv = pos->_prev;
	ListNode* Next = pos->_next;
	prv->_next = Next;
	Next->_prev = prv;
	free(pos);
}
//测试函数
void main() {
	ListNode* phead = ListCreate(0);
	ListNodeInit(phead);
	ListPushBack(phead, 10);
	ListPushBack(phead, 11);
	ListPushBack(phead, 12);
	ListPushBack(phead, 13);
	ListPushBack(phead, 14);
	ListPushBack(phead, 15);
	ListPrint(phead);
	ListPopBack(phead);
	ListPopBack(phead);
	ListPopBack(phead);
	ListPopBack(phead);
	ListPrint(phead);
	ListPushFront(phead, 1);
	ListPushFront(phead, 5);
	ListPushFront(phead, 3);
	ListPushFront(phead, 4);
	ListPrint(phead);
	ListPopFront(phead);
	ListPopFront(phead);
	ListPopFront(phead);
	ListPrint(phead);
	ListInsert(ListFind(phead,11), 90);
	ListPrint(phead);
	ListErase(ListFind(phead, 90));
	ListPrint(phead);
	ListDestory(&phead);
}

スクリーンショットをテストする

 

おすすめ

転載: blog.csdn.net/weixin_49312527/article/details/121582276