【データ構造】二重連結リストの詳細説明

ここに画像の説明を挿入します
単結合リストを学習した後は、二重結合リストの方がはるかに単純です。二重結合リストでは、先頭の挿入、末尾の挿入、先頭の削除、末尾の削除、および任意の位置への挿入と任意の位置の削除が簡単になります。今日は、Xiao Zhang をフォローして、一緒に学びましょう!

二重リンクリストの分類

ヘッダーのない双方向リンクリスト

ここに画像の説明を挿入します

双方向の循環リンクリスト

ここに画像の説明を挿入します

双方向の見出しの非巡回リンク リスト、双方向の非巡回リンク リストもあります。双方向の見出しの循環リンク リストの使用に焦点を当て、リーダーには監視役の地位もあります。

双方向の循環リンクリスト

見出し付き双方向循環リンク リスト: 最も複雑な構造で、一般にデータを個別に保存するために使用されます。実際に使用されるリンク リスト データ構造は、すべて見出し付きの双方向循環リンク リストです。また、この構造は複雑ですが、コードを使用して実装すると、この構造が多くの利点をもたらし、実装が簡単であることがわかります。これは、後でコードを実装するときにわかります。

双方向循環リンクリストのインターフェース実装

ListNode* ListCreate(int x)//创建新结点
ListNode* createhead()//创建哨兵位
void ListPushBack(ListNode* phead, int x)//尾插
void SListPrint(ListNode* phead)//打印链表
void SListPushFront(ListNode* phead, int x)//头插
void SListPopBack(ListNode* phead)//尾删
void SListPopFront(ListNode* phead)//头删
void SListInsert(ListNode* pos, int x)//pos前插
void SListErase(ListNode* pos)//删除pos;
ListNode* SListFind(ListNode* phead,int x)//查找链表中第一个x

0.ノード構造の作成

typedef struct ListNode
{
    
    
	int data;//数据
	struct ListNode* next;//下个结点地址
	struct ListNode* prev;//上个结点地址
}ListNode;

1. 新しいノードを作成します

ListNode* ListCreate(int x)//创建新结点
{
    
    
	ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));//给新结点申请空间
	if (newnode == NULL)
	{
    
    
		perror("malloc error");//没申请到,malloc返回NULL给newnode
		
	}
	newnode->data = x;//新结点的数据给x
	newnode->next = NULL;//新结点的下一个结点地址为空
	newnode->prev = NULL;//新结点的上一个结点地址为空
	return newnode;//新结点的地址返回回去
}

2. 見張りの位置を作成する

このセンチネル ビットはリンク リストのヘッド ノードとして機能し、データは保存されません。

ListNode* createhead()
{
    
    
	ListNode* head = ListCreate(-1);//哨兵位结点的数据随便给个-1
	head->next = head;
	head->prev = head;
	return head;//返回哨兵位的头结点地址
}

ここに画像の説明を挿入しますデータを持った新たなノードがない場合は、まず、先頭の自分のアドレスに、前のノードのアドレスと次のノードのアドレスを格納します。

3.テールプラグ

void ListPushBack(ListNode* phead, int x)//尾插
{
    
    
	ListNode* newnode = ListCreate(int x);
	ListNode* tail = phead->prev;//记录尾结点的地址
	newnode->prev =tail;//新结点的prev存放尾结点的地址-1
	newnode->next = phead;//新结点的next存放头结点哨兵位的地址->2
	phead->prev = newnode;//头结点的prev存放新结点的地址->4
	tail->next = newnode;//尾结点的next存放新节点的地址->3

}

分析: ここに画像の説明を挿入します
tail は末尾ノードのアドレスを記録するために使用されるため、1、2、3、4 の順序は任意に切り替えることができます。記録がない場合は、最初に新しいノードの前と次を保存することを忘れないでください。次に、head-> prev、head->prev->next を変更し、変更プロセス中に末尾ノードのアドレスが失われるのを防ぎます。
ここに画像の説明を挿入します
同じことがセンチネル位置の後に新しいノードを挿入する場合にも当てはまります。

4. 二重リンクリストを印刷する

void SListPrint(ListNode* phead)//打印链表
{
    
    
	ListNode* cur = phead->next;
	while (cur != phead)
	{
    
    
		printf("%d->", cur->data);
		cur = cur->next;
    }
	printf("\n");


}

分析: リンク リスト全体をトラバースするポインタ cur を定義します。これは循環リンク リストであるため、間違いなくセンチネル ビットを指します。センチネル ビット ノードのデータは使用されないため、cur ポインタはノードからトラバースを開始します。 cur==phead になるまでヘッド ノードのセンチネル ビットの隣に配置され、ループが終了し、毎回 cur->data を出力し、その後 cur を次のノードに移動します。

5.ヘッドプラグ

void SListPushFront(ListNode* phead, int x)//头插
{
    
    
	ListNode* newnode = ListCreate(x);
	
	newnode->next = phead->next;
	phead->next->prev = newnode;
	newnode->prev = phead;
	phead->next = newnode;

}

分析します:ここに画像の説明を挿入します
知らせ: まず newnode->next; と newnode->prev を保存します;
先に手順 4 に進むと、phead->next のアドレスは newnode になり、phead->next の以前のアドレスは失われます。
もう 1 つの方法は、 * next=phead->next; を保存し、その後 1、2、3、4 と順序を逆にすることです。

6.尾部の削除

void SListPopBack(ListNode* phead)//尾删
{
    
    
	ListNode* last = phead->prev->prev;//记录尾结点的上一个结点的地址
	phead->prev = last;//头节点哨兵位的prev存入last的地址
	last->next = phead;//last的next存入phead的地址

}

分析:ここに画像の説明を挿入します末尾ノードを削除したい場合は、まず末尾ノードの前のノードのアドレスを記録します。これは、末尾ノードを削除するには、末尾の最後のノードの隣に phead のアドレスを保存するためです。ノード。phead の prev には、最後のアドレスが格納されます。
ここに画像の説明を挿入しますセンチネル ビットに加えて、ノードの末尾の削除もあり、センチネル ビットのヘッド ノードが残ります。上記と同じコードが適用されます。

7. ヘッダーの削除

void SListPopFront(ListNode* phead)//头删
{
    
    
	ListNode* next = phead->next;
	phead->next = next->next;
	next->next->prev = phead;
}

分析:ここに画像の説明を挿入しますまずセンチネル ビットの次のノード アドレスを next に保存し、次に phead の次を next->next に保存し、next->next->prev を phead の最初のアドレスに保存します。

8. pos ポインタが指すノードを前方に挿入します。

void SListInsert(ListNode* pos, int x)//pos前插
{
    
    
	ListNode* newnode = ListCreate(x);//申请新结点将地址存放在newnode变量中
	newnode->next = pos;//新结点的下一个结点保存pos指针指向节点的地址
	newnode->prev = pos->prev;//新结点的prev保存pos指针指向的节点的上一个节点的地址
	pos->prev->next=newnode;//pos指针指向的节点的前一个结点的next保存newnode指针指向的结点
	pos->prev = newnode;//pos指针指向的结点的prev保存newnode指针指向结点的地址
}

分析します:ここに画像の説明を挿入します

9. pos ポインタが指すノードを削除します。

void SListErase(ListNode* pos)//删除pos;
{
    
    
	ListNode* last = pos->prev;//记录pos指针指向结点的前一个结点地址
	ListNode* next = pos->next;//记录pos指针指向结点的后一个结点地址
	last->next = next;//操作1
	next->prev = last;//操作2

}

分析します:ここに画像の説明を挿入します

10. リンクされたリストで最初に出現する x を検索します。

ListNode* SListFind(ListNode* phead,int x)
{
    
    
	ListNode* cur = phead->next;//cur指针先指向phead的下一个结点的位置
	while (cur != phead)//循环遍历
	{
    
    
		if (cur->data == x)//第一次找到
		{
    
    
			return cur;返回结点数据等于x的地址



		}
		cur = cur->next;//cur指针指向下一个结点





	}
	return NULL;
}

分析: 印刷されたリンク リストを印刷するのと同様に、ループは phead->next から開始して実行されます。cur が phead と等しくない場合、ループは続行されます。cur->data の場合、x、cur の内容 (データ) を返します。ループの最後に x のノード アドレスが見つからない場合は、リンク リストに x に等しいノードがないことを意味し、NULL が返されます。

11. 二重リンクリストの破壊

void Destroy(ListNode* phead)
{
    
    
	
	ListNode* cur = phead->next;
	while (cur != phead)
	{
    
    
		ListNode* next = cur->next;
		free(cur);
		cur = next;
	}
	free(phead);
}

分析、最初に phead の次のノードを指すポインタ cur を定義し、トラバーサルを開始し、最初に cur が指すノードの次のノードのアドレスを next に保存し、次に cur ポインタが指すスペースを解放します。 cur に保存されているノードへのポイント next の次のノードのアドレスが循環され、すべてのノードが順番に解放され、サイクルの最後にセンチネル ビットが解放されます。

12. 完全なソースコード

#include <stdio.h>
#include <stdlib.h>
typedef struct ListNode
{
    
    
	int data;
	struct ListNode* next;
	struct ListNode* prev;
}ListNode;
ListNode* ListCreate(int x)//创建新结点
{
    
    
	ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
	if (newnode == NULL)
	{
    
    
		perror("malloc error");
		//return;
	}
	newnode->data = x;
	newnode->next = NULL;
	newnode->prev = NULL;
	return newnode;
}
ListNode* createhead()
{
    
    
	ListNode* head = ListCreate(-1);
	head->next = head;
	head->prev = head;
	return head;
}
void ListPushBack(ListNode* phead, int x)//尾插
{
    
    
	ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
	newnode->data = x;
	ListNode* tail = phead->prev;
	newnode->prev =tail;
	phead->prev = newnode;
	tail->next = newnode;
	newnode->next = phead;
	

}
void SListPrint(ListNode* phead)//打印链表
{
    
    
	ListNode* cur = phead->next;
	while (cur != phead)
	{
    
    
		printf("%d->",cur->data);
		cur = cur->next;
    }
	printf("\n");


}
void SListPushFront(ListNode* phead, int x)//头插
{
    
    
	ListNode* newnode = ListCreate(x);
	
	newnode->next = phead->next;
	phead->next->prev = newnode;
	newnode->prev = phead;
	phead->next = newnode;

}
void SListPopBack(ListNode* phead)//尾删
{
    
    
	ListNode* last = phead->prev->prev;
	phead->prev = last;
	last->next = phead;

}
void SListPopFront(ListNode* phead)//头删
{
    
    
	ListNode* next = phead->next;
	phead->next = next->next;
	next->next->prev = phead;
}
void SListInsert(ListNode* pos, int x)//pos前插
{
    
    
	ListNode* newnode = ListCreate(x);
	

	newnode->next = pos;
	newnode->prev = pos->prev;
	pos->prev->next=newnode;
	pos->prev = newnode;
}
void SListErase(ListNode* pos)//删除pos;
{
    
    
	ListNode* last = pos->prev;
	ListNode* next = pos->next;
	last->next = next;
	next->prev = last;

}
ListNode* SListFind(ListNode* phead,int x)
{
    
    
	ListNode* cur = phead->next;
	while (cur != phead)
	{
    
    
		if (cur->data == x)
		{
    
    
			return cur;



		}
		cur = cur->next;





	}
	return NULL;
}
//双链表的销毁
void Destroy(ListNode* phead)
{
    
    
	
	ListNode* cur = phead->next;
	while (cur != phead)
	{
    
    
		ListNode* next = cur->next;
		free(cur);
		cur = next;
	}
	free(phead);
}
int main()
{
    
    
	ListNode* list;
	list = createhead();
	printf("尾插:");
    ListPushBack(list,1);
	ListPushBack(list,2);
	ListPushBack(list,3);
	ListPushBack(list,4);
	SListPrint(list);
	printf("头插:");
	SListPushFront(list, 8);
	SListPushFront(list, 7);
	SListPushFront(list, 6);
	SListPushFront(list, 5);
	SListPrint(list);
	printf("尾删:");
	SListPopBack(list);
	SListPopBack(list);
	SListPrint(list);
	printf("头删:");
	SListPopFront(list);
	SListPopFront(list);
	SListPopFront(list);
	SListPrint(list);
	printf("插入pos指向结点前面:");
	SListInsert(list->next->next, 1000);
	SListPrint(list);
	printf("删除pos指向的结点:");
	SListErase(list->next->next);
	SListPrint(list);
	printf("查找第一个x的位置并打印出来:");
	ListNode* p=SListFind(list, 8);
	printf("%d", p->data);
	printf("修改查找到的结点:\n");
	p->data = 10000;
	SListPrint(list);
	printf("销毁打印销毁后哨兵位的下一个结点的数据,如果为随机值说明已经被销毁:");
	Destroy(list);
	printf("%d",list->data);
	
	
}

13. コンパイルして実行する

ここに画像の説明を挿入します

おすすめ

転載: blog.csdn.net/yyqzjw/article/details/132812838
おすすめ