C言語-双方向の先行循環リンクリストとその長所と短所を追加、削除、チェック、変更します


序文

リンクリストには多くの種類がありますが、その中でも一方向非先行非循環リンクリストと双方向先行循環リンクリストが一般的に使用されています。どちらのリンクリストにも独自の用途があります。
双方向の先行循環リンクリストの構造は最も複雑であり、通常、データを個別に格納するために使用されます。実際に使用されるリンクリストのデータ構造は、主導権を握る双方向の循環リンクリストです。今日は、C言語を使用して、主要な二重リンクリストの追加、削除、および変更を実現します。


1.双方向の先行循環リンクリスト

1.双方向の先行循環リンクリスト構造

まず第一に:双方向の先行する循環リンクリストの構造を見てください

ここに画像の説明を挿入します
双方向先行循環リンクリストの各ノードが前後に接続されてループを形成していることがわかります。この構造を実現するには、ヘッドノードを作成する必要があります。ヘッドノードのテールポインタがポイントします。これに基づいて、他のノードの挿入と削除を実現します。

1.双方向の先行循環リンクリスト実装コード

コード部分は次のとおりです。
ヘッダーファイル
ListNode.h

#define pragama once
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<assert.h>

//typedef方便修改结构体变量的类型
typedef int LTDataType;

//构建链表结构体,结构体变量包括头指针,尾指针及data值
typedef struct ListNode {
    
    
	
	struct ListNode* pre;
	struct ListNode* next;
	LTDataType data;

}ListNode;

//创建新节点
ListNode* BuyListNode(LTDataType x);
//链表初始化->创造头结点
ListNode* InitListNode();
//打印链表
void ListPrint(ListNode* phead);
//销毁链表
void ListDistory(ListNode* phead);

//增删查改
void ListPushBack(ListNode* phead, LTDataType x);
void ListPushFront(ListNode* phead, LTDataType x);
void ListPopBack(ListNode* phead);
void ListPopFront(ListNode* phead);
ListNode* ListFind(ListNode* phead, LTDataType x);
void ListInsert(ListNode* pos, LTDataType x);
void ListErase(ListNode* pos);
void Listchange(ListNode* pos, LTDataType x);

本文
ListNode.c

#include"ListNode.h"



ListNode* BuyListNode(LTDataType x)
{
    
    
	ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
	newnode->data = x;
	newnode->next = NULL;
	newnode->pre = NULL;


	return newnode;
}


ListNode* InitListNode()
{
    
    
	//构造头结点
	ListNode* phead = (ListNode*)malloc(sizeof(ListNode));
	phead->next = phead;
	phead->pre = phead;

	return phead;
}

void ListPrint(ListNode* phead)
{
    
    
	assert(phead);

	//从头结点后开始,到头结点结束
	ListNode* cur = phead->next;
	while (cur != phead)
	{
    
    
		printf("%d", cur->data);
		cur = cur->next;
	}
	printf("\n");
}

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

//以下注释掉的代码段和留下的代码功能是一样的
void ListPushBack(ListNode* phead, LTDataType x)
{
    
    
	/*assert(phead);

	ListNode* newnode = BuyListNode(x);
	ListNode* tail = phead->pre;

	tail->next = newnode;
	newnode->pre = tail;
	newnode->next = phead;
	phead->pre = newnode;*/
	ListInsert(phead, x);
}

void ListPushFront(ListNode* phead, LTDataType x)
{
    
    
	/*assert(phead);

	ListNode* next = phead->next;
	ListNode* newnode = buyListNode(x);

	newnode->next = next;
	next->pre = newnode;
	phead->next = newnode;
	newnode->pre = phead;*/
	ListInsert(phead->next, x);

}

void ListPopBack(ListNode* phead)
{
    
    
	/*assert(phead);
	assert(phead->next != phead);
	
	ListNode* tail = phead->pre;
	ListNode* tailpre = tail ->pre;
	
	tailpre->next = phead;
	phead->pre = tailpre;
	
	free(tail);*/
	ListErase(phead->pre);
}

void ListPopFront(ListNode* phead)
{
    
    
	/*assert(phead);
	assert(phead->next != phead);
	
	ListNode* next = phead->next;
	ListNode* newnext = next ->next;
	
	phead->next = newnext;
	newnext->pre = phead;

	free(next);*/
	ListErase(phead->next);
}

ListNode* ListFind(ListNode* phead, LTDataType x)
{
    
    
	assert(phead);

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

//POS之前插入
void ListInsert(ListNode* pos, LTDataType x)
{
    
    
	assert(pos);
	ListNode* pospre = pos->pre;
	ListNode* newnode = BuyListNode(x);
	
	pospre->next = newnode;
	newnode->pre = pospre;
	newnode->next = pos;
	pos->pre = newnode;

}
//pos不能是phead! 否则会破坏双向链表的结构;
void ListErase(ListNode* pos)
{
    
    
	assert(pos);
	ListNode* pospre = pos->pre;
	ListNode* posnext = pos->next;

	pospre->next = posnext;
	posnext->pre = pospre;
	free(pos);
}
void Listchange(ListNode* pos, LTDataType x)
{
    
    
	assert(pos);
	pos->data = x;
}

テストコード
test.c

#include"ListNode.h"


void test()
{
    
    
	ListNode* phead = InitListNode();
	ListPushBack(phead, 1);
	ListPushBack(phead, 2);
	ListPushBack(phead, 3);
	ListPushBack(phead, 4);
	ListPushBack(phead, 5);
	ListPushFront(phead, 6);
	ListPrint(phead);

	ListNode* pos = ListFind(phead, 2);
	ListInsert(pos, 8);
	ListErase(pos);
	ListPrint(phead);
	ListNode* pos2 = ListFind(phead, 4);
	Listchange(pos2, 9);

	//ListDistory(phead);
	ListPrint(phead);

}
int main()
{
    
    
	test();
	return 0;
}

運転結果
![ここに画像の説明を挿入](https://img-blog.csdnimg.cn/20210202194546953.png

第二に、双方向の先行循環リンクリストの長所と短所

次の双方向循環リストの長所と短所を順次テーブルおよび一方向非循環リンクリストと比較するため、次の順次テーブルの長所と短所を同時に要約します。

1.双方向先行循環リンクリストの長所と短所

  • 利点:任意の位置でのO(1)の挿入と削除をサポートし、拡張する必要がなく、スペースを無駄にしません。
  • 短所:ランダムアクセスはサポートされておらず、キャッシュヒット率は比較的低いです。

2.シーケンステーブルの長所と短所

利点:

  • 相対的に言えば、スペース使用率は高く、追加のスペースは必要なく、スペースは小さいです(リンクリストはポインターを格納する必要があります)。
  • 物理的な空間は連続しています。ランダムアクセスをサポートし、添え字アクセスを使用できます(最大の利点)。キャッシュヒット率が高くなります。

短所:

  • ヘッダーまたは途中でデータを挿入または削除するには、データを1つずつ移動する必要があり、時間計算量はO(N)です。
  • スペースが足りない場合は容量を増やす必要がありますが、一般的には容量を倍数で増やすため、ある程度のメモリの浪費が発生する場合があります。

おすすめ

転載: blog.csdn.net/weixin_47460769/article/details/113572440