最も詳細な8つのリンクリスト構造と単一リンクリストの基本的な操作チュートリアル


ここに画像の説明を挿入

1.シャオシェンが言いたいこと

私は学校の新入生です、私は偉大な神々がより多くの指導を与えることができることを願っています前回、シーケンステーブルとその基本的な操作について学びました。次に、リンクリストを見てみましょう。役立つと思われる場合は、Xiaoshengにメモを付けることを忘れないでください。Xiaoshengは引き続き更新します。さあ、技術者!

2.リンクリストを知る

1.リンクリストの基本概念

リンクリストは、N個のデータ要素の有限シーケンスです。その長さは必要に応じて拡大または短縮できますは、前のシーケンステーブルと同様に、一種の線形テーブルです。シーケンシャルリストはシーケンシャルストレージ構造ですが、リンクリストはチェーンストレージ構造です。
ここに画像の説明を挿入

単一のリストノードを使用して、データと次のノードのアドレスを保存します、したがって、ノードは一般に複数の部分、つまりデータフィールドとポインタフィールドに分割されます。データフィールドには有効なデータが格納され、ポインタフィールドには次のノードのアドレスが格納されます。、単一リンクリストには1つのポインタフィールドしかなく、二重リンクリストには2つのポインタフィールドがあります。

2.単一リンクリストと順次リストの違いと長所と短所を理解する

これらは2つの異なるストレージ構造です。最初に違いについて説明しましょう。シーケンシャルテーブルはシーケンシャルストレージ構造です。その特徴は、論理的に隣接している2つの要素が物理的な位置でも隣接していることです。しかし、リンクリストは異なります、チェーンストレージ構造の特徴は、論理的に隣接する要素が物理的な場所で隣接している必要がないことです。チェーンストレージ構造は、ノードのポインタフィールドを介して次のノードの場所を直接見つけることができるためです。 ここに画像の説明を挿入
シーケンステーブルの長所と短所:
1。アドバンテージ添え字
2を使用して、必要なデータに直接アクセスできます。短所:必要に応じてメモリを割り当てることができません、拡張にはmallocまたはrealloc関数のみを使用でき、頻繁な拡張を簡単に実現できます。メモリの浪費やデータ漏洩などの問題を引き起こしやすい
単一リンクリストの長所と短所:
1。長所:リンクリストの長さは、実際のニーズに応じてノードを作成することで増減できます、メモリをより多く使用します。
2.デメリット:末尾または任意の位置で挿入または削除する場合、時間計算量と空間計算量は比較的大きくなります。毎回、ポインターを移動して必要な位置を見つける必要がありますが、これはシーケンシャルテーブルルックアップよりも効率的ではありません。

3.8種類のリンクリストを知る

リンクリストの種類を理解する前に、リンクリストのいくつかの特徴を理解する必要があります
1.単方向および双方向
2.有鉛および無鉛
3.環状および非環状

これらを組み合わせることができます。たとえば、一方向の先行循環リンクリスト、双方向の非先行非循環リンクリスト...
Xiaoshengは、すべての人に知らせるためにいくつかの絵を描きます。

~~一方向の循環リンクリスト

ここに画像の説明を挿入
ここに画像の説明を挿入

~~一方向の非循環リンクリスト

ここに画像の説明を挿入
ここに画像の説明を挿入

~~一方向の無鉛循環リンクリスト

ここに画像の説明を挿入

ここに画像の説明を挿入

~~一方向の無鉛非環式リンクリスト

ここに画像の説明を挿入

ここに画像の説明を挿入

~~双方向の見出し付き円形リンクリスト

ここに画像の説明を挿入
ここに画像の説明を挿入

~~二重見出しの非環式リンクリスト

ここに画像の説明を挿入
ここに画像の説明を挿入

~~頭のない双方向循環リンクリスト

ここに画像の説明を挿入
ここに画像の説明を挿入

~~双方向の無鉛非環式リンクリスト

ここに画像の説明を挿入
ここに画像の説明を挿入

2.単一リンクリストの基本操作

以下で参照する単一リンクリストは、単一無鉛非巡回リンクリストです。

1.基本操作のインターフェース(基本)

単一リンクリストは順次リストに似ています。追加、削除、変更、およびチェックは、単一リンクリストの基本的な操作です。最初に基本的なインターフェースを見てみましょう!

ここに画像の説明を挿入

2.単一リンクリストの構造定義

単一リンクリストのノードは、データフィールドとポインタフィールドの2つの部分に分かれていますしたがって、次の構造でノードを定義できます

typedef int SLTDataType;
typedef struct SListNode
{
    
    
	SLTDataType data;
	struct SListNode* pNext;
}SLTNODE;

3.ノードの作成

リンクリストの長さはその後増加するため、ノードの作成はある程度最初になります構造体ポインタ型の変数を作成して初期化し、最後に変数を返します、コードを介して直接分析できます。まず、ヘッドノードを使用したリンクリストの作成を見てみましょう。

SLTNODE* CreateNode(SLTDataType val)
{
    
    
	SLTNODE* pNew = (SLTNODE*)malloc(sizeof(SLTNODE));
	pNew->data = val;
	pNew->pNext = NULL;
	return pNew;
}

4.第1レベルのポインターと第2レベルのポインターの送信を区別します

リンクリストのヘッドポインタを変更する場合は、第2レベルのポインタを渡す必要があります。転送プロセスはコピーのプロセスであるため、ヘッドポインタを変更しても、第1レベルのポインタを渡すことはできません。ヘッドポインタ。仮パラメータの変更は実際のパラメータには影響しません。したがって、リンクリストのヘッドポインタを変更するには、セカンダリポインタを転送する必要があります。

5.単一リンクリストの挿入

~~単一リンクリストのヘッダー

先に強調したので、私たちが操作する単一リンクリストにはヘッドノードがありません、ヘッドプラグはとても便利です。ヘッドノードとの単一リンクリストの操作方法もとてもシンプルです。偉大な神々が自分で書くことができると思います。

//因为要改变头指针所以我们要传送头指针的地址,即二级结构体指针变量
 void SListPushFront(SLTNODE** ppHead, SLTDataType val)
{
    
    
	
	SLTNODE* pNew  = CreateNode(val);
	pNew->pNext = *ppHead;
	*ppHead = pNew;
}

~~単一リンクリストのテール挿入

テールを挿入するときは、単一​​リンクリストが空であるかどうかを考慮する必要があります。空の場合は、ヘッドポインタを新しいノードにポイントする必要があるためです。

void SListPushBack(SLTNODE** ppHead, SLTDataType val)
{
    
    
	SLTNODE* pNew = CreateNode(val);
	//判断链表是否为空,若为空则将头指针指向新结点
	if (*ppHead == NULL)
	{
    
    
		*ppHead = pNew;

	}
	else
	{
    
    
		SLTNODE* pTail = *ppHead;
		//通过循环让指针找到尾部
		while (pTail->pNext != NULL)
		{
    
    
			pTail = pTail->pNext;
		}
		pTail->pNext = pNew;
	}
	
}

ループの条件がpTail->pNext!= NULLであることに神々が気付いたのではないかと思いますが、pTail!= NULLに変更できますか?一見問題ないように見えますが、本当に問題ないのでしょうか。考えてみよう〜

まず、正しいループ条件pTail-> pNext!=NULLを見てみましょう。

便宜上、図では*ppHeadの代わりにpHeadを使用していますここに画像の説明を挿入このとき、pTail-> pNext!= NULLが確立され、pTailポインタが後方に移動しますここに画像の説明を挿入。このとき、pTail-> pNext!= NULLが確立され、pTailポインタが後方に移動します
ここに画像の説明を挿入
。ノードがないことに注意してください。この時点で遅れている場合、この時点でpTailが指すノードは内部にあります。ポインタフィールドにはnullポインタが格納されます。つまり、pTail-> pNextは空であり、pTailは最後のノードを指しますループ条件がpTail!= NULLの場合を見てみましょう。
ここに画像の説明を挿入
このとき、pTail!= NULLが確立されます。pTailポインタを後方に移動しても、
ここに画像の説明を挿入
pTail!=NULLが確立されます。pTailポインタを移動すると後方に、
ここに画像の説明を挿入
pTail!=NULLはまだ確立されています。pTailポインタシフト後
ここに画像の説明を挿入
上記から、pTail!= NULLの条件が実行された場合、pTailがテールノードを指しているときに停止しないため、ループ条件が間違っていることがわかります。

~~単一リンクリストの指定された位置に挿入します

~~位置の前に挿入

ここでは2つのケースがあります。posは最初のノードであり、posは最初のノードではありません。posが1の場合、ヘッド挿入と同等です。posが1でない場合、2つのポインターを使用して、ループを介してposとposの前のノードを検索します。
ここに画像の説明を挿入
ここに画像の説明を挿入

void SListInsert(SLTNODE** ppHead, SLTNODE* pos, SLTDataType val)
{
    
    
	//1.pos是第一个结点,在pos之前插入相当于头插
	if (*ppHead == pos)
	{
    
    
		SListPushFront(ppHead, val);
	}
	//2.pos不是第一个结点
	SLTNODE* pPrev = NULL;
	SLTNODE* pMove = *ppHead;
	while (pMove != pos)
	{
    
    
		pPrev = pMove;
		pMove = pMove->pNext;
	}
	SLTNODE* pNew = CreateNode(val);
	pPrev->pNext = pNew;
	pNew->pNext = pos;
}

ここでは2つのポインタを使用し、渡すことができますポインタ直接実装されていますか?やってみよう

void SListInsert(SLTNODE** ppHead, SLTNODE* pos, SLTDataType val)
{
    
    
	if (*ppHead == pos)
	{
    
    
		SListPushFront(ppHead, val);
	}
	 SLTNODE* pPrev = *ppHead;
	 while(pPrev->pNext != pos)
	 {
    
    
		  pPrev = pPrev->pNext;
	 }
	 SLTNODE* pNew = CreateNode(SLTDataType val);
	 pPrev->pNext = pNew;
	 pNew->pNext = pos;
	
	SLTNODE* pNew = CreateNode(val);
	pPrev->pNext = pNew;
	pNew->pNext = pos;
}

明らかにそれは可能です、posポインターが決定されているため、前のテール挿入のようにテールを検索する必要はありませんが、posの前のノードを見つけるだけで済みます。
ここに画像の説明を挿入
ここに画像の説明を挿入

~~位置の後に挿入

単一リンクリストは、前のノードから次のノードを見つけることができますが、後のノードから前のノードを見つけることはできませんしたがって、pos位置の後に挿入する場合、次のノードはposから直接見つけることができるため、 pos位置の前に挿入するときに、ポインタを円を描くように動かしてノードを見つける必要はありません。

 void SListInsertAfter(SLTNODE * pos, SLTDataType val)
{
    
    
	assert(pos);
	SLTNODE* pNew = CreateNode(val);
	pNew->pNext = pos->pNext;
	pos->pNext = pNew;
}

6.単一リンクリストの削除

削除と挿入はある程度類似しており、類推によって比較することができます~~~

~~単一リンクリストの先頭を削除します

元の最初のノードを解放し、ヘッドポインタを次のノードに移動しますここに画像の説明を挿入>ここに画像の説明を挿入

void SListPopFront(SLTNODE** ppHead)
{
    
    
	assert(*pHead);
	SLTNODE* next =  (*ppHead)->pNext;
	free(*ppHead);
	*ppHead = next;

}

~~単一リンクリストのテール削除

尾の削除時に考慮すべき3つのケースがあります。リンクリストが空であるかどうかに関係なく、リンクリストにはノードが1つだけあり、リンクリストには複数のノードがあります

void SListPopBack(SLTNODE** ppHead)
{
    
    
	//1.链表为空
	assert(*ppHead);
	//2.只有一个结点
	if ((*ppHead)->pNext == NULL)
	{
    
    
		free(*ppHead);
		*ppHead = NULL;
	}
	//3.有两个及以上的结点
	SLTNODE* pPrev = NULL;
	SLTNODE* pTail = *ppHead;
	while (pTail->pNext != NULL)
	{
    
    
		pPrev = pTail;
		pTail = pTail->pNext;
	}
	free(pTail);
	pPrev->pNext = NULL;
}

~~単一リンクリストの指定された位置を削除します

posがヘッドポインタと等しい場合、リンクリストのヘッド削除と同等です。前に記述したヘッド削除関数SListPopFront()を直接呼び出すことができます。

void SListErase(SLTNODE** ppHead,SLTNODE* pos)
{
    
    
	if (pos == *ppHead)
	{
    
    
		SListPopFront(*ppHead);
	}
	else
	{
    
    
		SLTNODE* pPrev = *ppHead;
		while (pPrev->pNext != pos)
		{
    
    
			pPrev = pPrev->pNext;
		}
		pPrev->pNext = pos->pNext;
		free(pPrev);
	}
}

7.単一リンクリストの検索

ポインタを連続的に移動してポインタを返すことにより、データフィールドに対応するデータを格納するノードを見つけます

SLTNODE* SListFind(SLTNODE* pHead,SLTDataType val)
{
    
    
	SLTNODE* pMove = pHead;
	while (pMove != NULL) //while(pMove)
	{
    
    
		if (pMove->data == val)
		{
    
    
			return pMove;
		}
	}
	return NULL;
}

8.単一リンクリストの破棄

破壊作戦は最も簡単なはずです、シャオシェンは言葉ではありません、私は神々がそれを一目で理解すると信じています

 void SListDestroy(SLTNODE** pphead)
{
    
    
	SLTNODE* pMove = *pphead;
	while (pMove !=NULL)
	{
    
    
		SLTNODE* next = pMove->pNext;
		free(pMove);
		pMove = next;
	}

	*pphead = NULL;
}

4.結論

これを見ることができてくれた偉大な神々にとても感謝しています。Xiaoshengはここにいるすべての人に感謝します。Xiaoshengは8つのリンクリストの構造と1つのリンクリストの基本的な操作をすべての人に大まかに理解させました。単語をコーディングするのは簡単で、オリジナルになるのは簡単ではありません。次の記事では、二重リンクリストと循環リンクリストを入力し、技術者に来ます(好きでフォローすることを忘れないでください) ~~~
ここに画像の説明を挿入
ここに画像の説明を挿入

おすすめ

転載: blog.csdn.net/qq_59955115/article/details/123588720