C/C++ データ構造: 【ギフトデモ】単連結リスト構造の導入と実装

リンクされたリスト

リンクリストの概念と構造

概念的な理解

リンク リストは、物理記憶構造内の非連続かつ非順次の記憶構造であり、データ要素の論理的順序は、リンク リスト内のポインタのリンク順序によって実現されます。

知識ポイント:

  1. リンクされたリストは論理的には連続していますが、物理的には必ずしも連続しているわけではありません。
  2. 連結リストの連結方法は、データ間のポインタリンクによって実現される連結リストの順序です。
  3. 非シーケンシャルで添字ガイダンスがないため、二分探索やバブルソートなどの検索方法は使用できません。

構造的な理解

単一リンクリストを例に挙げます

画像-20220207190801482

知識ポイント:

  1. 上の図からわかるように、チェーン構造は論理的には連続していますが、物理的には必ずしも連続しているわけではありません。
  2. 各ノードは、各ノード値 (データ) を格納する際に、その後継ノード (次のノード) を示すアドレス情報も格納する必要があります。
  3. 実際のノードは通常、ヒープから適用されます。
  4. スペースはヒープから適用され、ヒープは特定の戦略に従って割り当てられるため、2 回適用されるスペースは連続的である場合もあれば、不連続である場合もあります。

リンクリストの分類

1. 一方向または双方向

画像-20220209145756546

2. リードするかリードしないか

画像-20220209145841249

3. 循環的と非循環的

画像-20220401180304343

リンク リスト構造は非常に多くありますが、実際には次の 2 つの構造が最も一般的に使用されます。

画像-20220209150003808

  1. ヘッドレス一方向非循環リンク リスト:単純な構造。通常、データを単独で保存するためには使用されません。実際には、これは、ハッシュ バケット、グラフの隣接リストなど、のデータ構造の下部構造のようなものです。また、この構造は筆記試験の面接でもよく現れます。
  2. 主要な双方向循環リンク リスト:最も複雑な構造で、一般にデータを個別に保存するために使用されます。実際に使用されるリンク リストのデータ構造は、リード付きの双方向循環リンク リストです。また、この構造は複雑ですが、コードを使用して実装すると、この構造が多くの利点をもたらし、実装が簡単であることがわかります。後でコードを実装するときにわかります。

単一リスト

定義: ノード構造

┌───┬───┐

│データ │次へ │

└───┴───┘

データフィールド --> ノード値を格納するデータフィールド

次のフィールド --> ノードの直接の後続ノードのアドレス (位置) を格納するポインタ フィールド。

リンク リストは、リンク リストの n 個のノードを各ノードのポインタ フィールドを介して論理的な順序で結合します。各ノードにポインタ フィールドが 1 つだけあるリンク リストを単一リンク リスト (Single Linked List) と呼びます。

単一リンク リストは、名前が示すように、単一リンク リストです。単一リンク リストの実行方向は 1 つだけです。リンク リスト ノードには、現在のノードのデータと次のノードのアドレス、およびリンクされたリストの終わりが格納されます。 list は NULL ポインタです。

参照コード

C言語

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

typedef int SLTDataType; 

typedef struct SListNode 		// 单链表结点结构
{
    
    
	SLTDateType data;  			// 单链表数据信息
	struct SListNode* next;		// 当前结点的后继结点信息
}SLTNode;

void IniSLTNode()
{
    
    
    SLTNode* plist = NULL;      // 初始化单链表
    
}

ジャワ

class ListNode {
    
    
    public int val;
    public ListNode next;

    public ListNode(int val) {
    
    
        this.val = val;
    }
}

public class MyLinkedList {
    
    

    public ListNode head;							//链表的头引用

    public void IniSLTNode() 
    {
    
    
        ListNode listNode1 = new ListNode(12);		// 链表插入12
        ListNode listNode2 = new ListNode(23);		// 链表插入23
        ListNode listNode3 = new ListNode(34);		// 链表插入34
        ListNode listNode4 = new ListNode(45);		// 链表插入45
        ListNode listNode5 = new ListNode(56);		// 链表插入56
        listNode1.next = listNode2;
        listNode2.next = listNode3;
        listNode3.next = listNode4;
        listNode4.next = listNode5;
        this.head = listNode1;
    }
}

出力: 出力リンクリストを走査します。

デザインのアイデア:

単一リンク リストの末尾ポインタはデフォルトでは空 (NULL) であるため、リンク リスト全体を走査して出力する必要がある場合、出力する最後の末尾ノード位置に到達したかどうかを判断するだけで済みます。出力が完了したら、トラバース ポインタを次のノードへのポインタなどに調整する必要があります。

**時間計算量: **O(N)

参照コード:

C言語

void SListPrint(SLTNode* phead)
{
    
    
	SLTNode* cur = phead;
	while (cur != NULL)
	{
    
    
		printf("%d->", cur->data);
		cur = cur->next;
	}
}

ロジックの実装:

rId44

挿入:テール挿入方式

デザインのアイデア:

  1. NULL ポインターはヘッド ノードとして使用できないため、最初にヘッド ノードが空かどうかを判断します。
  2. リンクされたリストの末尾ノードを見つけて、末尾ノードが見つかった場合にのみデータを配置します。

単一リンク リストは前方および一方向にトラバースされ、ノードはノード値と次のノードのアドレスを格納するため、単一リンク リストの末尾挿入操作を実装するときの論理操作は次のようになります。リンクされたリストにデータがあり、ない場合は先頭にデータを挿入します。挿入する場合は、末尾ポインタが NULL になる位置まで先頭をトラバースし、データへのノードの挿入を適用し、後続ポインタの位置を設定します。新しく適用されたノードの値をヌルポインタ NULL に設定します。最後に、新しく適用されたノード アドレスを元のノードの末尾のポインタ フィールドに接続して、リンク リストの末尾挿入を実現します。

**時間計算量: **O(N)

参考コード

C言語

void SListPushBack(SLTNode** phead, SLTDataType x)
{
    
    
    // 新申请一个结点
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	newnode->data = x;
	newnode->next = NULL;

    // 如果头节点是空的,就直接将申请到的节点插入
	if (*phead == NULL)
	{
    
    
		*phead = newnode;
	}
	else  // 如果头节点非空,那就去寻找尾结点
	{
    
    
		SLTNode* tail = *phead;  
		while (tail->next != NULL)
		{
    
    
			tail = tail->next;
		}// 找到尾结点后
		tail->next = newnode; // 赋值新的尾结点; 
	}	
}

ロジックの実装:

rId48

挿入:ヘッド挿入方式

デザインのアイデア:

  1. 新規ノード情報を申請し、新規申請ノードの次ノードアドレスを現在のリンクリストの先頭ノードとして設定する。
  2. 現在のリンクリストの先頭ノードを適用ノード情報に設定します。

※ヌルポインタをヘッドノードとして使用することはできないため、末尾を挿入する場合、ヘッドノードがヌルポインタであるかどうかを考慮する必要があります。head を挿入する場合、head ノードが NULL ポインタであっても、head ノードの NULL を tail ポインタに変換することと等価となるため、 head を挿入する際に head ノードがnull ポインタであるかどうかは無視できます

**時間計算量: **O(1)

参照コード:

C言語

void SListPushFront(SLTNode** phead, SLTDataType x)
{
    
    
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	newnode->data = x;
	newnode->next = *phead;
    
	*phead = newnode;
}

ロジックの実装:

rId52

削除:末尾の削除

デザインのアイデア:

  1. まず連結リストの先頭ノードが空かどうかを判断し、空の場合は最後まで削除できません。
  2. 単一リンクリストの最後にある最後のノードの次のポインタアドレスはヌルポインタであるため、次のポインタアドレスがヌルであるノードを見つけるためにトラバースするだけで済みます。
  3. 末尾データを見つけたら、そのアドレスを null ポインタとして設定し、その前のノードのリンク ポインタを null に設定します。

最後のデータを削除した後は、前のデータのリンク ポインタを空に設定する必要があるため、理想的な解決策は、現在のトラバーサル ポインタのアドレスを記録するために prev ポインタを設定することです。トラバーサル ポインタの温度が終端に達すると、そのデータは削除されます。 node, prev ポインタの位置は、削除されたノードの前のノードの位置であり、このとき、prev のリンクポインタが NULL を指していれば、リンクリストの削除操作は完全に実現できます同時に、リンクされたリストに現在ノードが 1 つしかない場合、前のノードのリンク ポインタを空にする必要がないことに注意してください。

**時間計算量: **O(N)

参照コード:

C言語

void SListPopBack(SLTNode** phead)
{
    
    
	if (*phead == NULL) 			// 判断单链表是否为空,如果为空无法进行尾插操作
		return;

	if ((*phead)->next == NULL)		// 如果单链表只有一个数据,只需要释放当前结点即可
	{
    
    
		free(*phead);
		*phead = NULL;
	} 								// 如果单链表长度 > 1
    else 
    {
    
    
        SLTNode* prev = NULL; 		// 记录指针
        SLTNode* temp = *phead;	// 遍历指针
        while (temp->next != NULL)	// 当遍历指针没有到达链表末尾
        {
    
    
            prev = temp;			// 在进入下一次遍历之前,遍历指针记录着遍历指针当前的位置
            temp = temp->next;		// 遍历指针进入下一步
        }

        free(temp);					
        temp = NULL;				// 找到尾结点后,释放尾结点并置为空指针
        prev->next = NULL;			// 记录指针存储着被删除结点的上一结点信息,此时将其结点的链接指针置为空完成尾删。
        
		/* 第二种写法,不需要prev记录指针,连续解引用。
		SLTNode* temp = *phead;
        while (temp->next->next != NULL)
        {
            temp = temp->next;
        }

        free(temp->next);
        temp->next = NULL;
        */
	}
}

ロジックの実装:

rId56

削除:先頭削除メソッド

デザインのアイデア:

  1. 先頭を削除する場合でも、リンク リストが空の場合を考慮する必要があります。
  2. ヘッド ノードを削除するときは、2 番目のノードを新しいヘッド ノードとして定義する必要があります。そうしないと、リンク リストが折りたたまれます。

**時間計算量: **O(1)

参照コード:

C言語版

void SListPopFront(SLTNode** phead)
{
    
    
	if (*phead == NULL)					// 考虑链表是否为空
		return;

	SLTNode* next = (*phead)->next;		// 将新的头结点改为第二个结点地址
	free(*phead);						
	*phead = next;						// 释放头结点
}

ロジックの実装:

画像

ルックアップ: リンクされたリストを値でルックアップします。

デザインのアイデア:

  1. 検索の考え方はリンク リストを出力するのと同じで、最初の要素が見つからない場合は、それをずっとたどるだけで済みます。
  2. 最初の要素が見つかった場合は現在のノードに戻り、最後まで歩いても要素が見つからなかった場合は NULL を返します。

**時間計算量: **O(N)

参照コード:

C言語

SLTNode* SListFindElem(SLTNode* phead, SLTDataType x)
{
    
    
	SLTNode* cur = phead;		// 遍历指针cur
	while (cur)					// 只要指针非空
	{
    
    
		if (cur->data == x)		// 找到数据data
			return cur;			// 找到返回结点信息
		else
			cur = cur->next;	// 没找到继续遍历
	}
	return NULL;				// 没找到返回空NULL
}

考える:

ノードとシーケンス位置の情報を同時に返すように書き換えることはできますか? それとも、リンクされたリスト内のすべての要素を見つけますか?

ロジックの実装:

rId64

検索: シーケンスによってリンクされたリストを検索します。

デザインのアイデア:

  1. 単一リンクリストは、最初のノードから開始し、ポインタをたどって 1 つずつ下に検索するだけで済みます。
  2. ポインターを走査して、n 番目のノードの終わりまで走査します。

**時間計算量: **O(N)

参照コード:

C言語

SLTNode* SListGetIndexData(SLTNode* phead, int index)
{
    
    
    if (phead == NULL)
        return NULL;
    if (index == 0)
        return phead;

    int count = 1;
    SLTNode* cur = phead;
    // while(cur != NULL)
    while (cur && count < index)
    {
    
    
        cur = cur->next;
        count++;
        // if (count == index) break; 
    }
    return cur;
}

Insert:ノードの後に​​要素を挿入(nodeメソッド)

ノード メソッドの意味: 受信パラメータはリンク リストのノードであり、このノードの後に​​のみ挿入する必要があります。

デザインのアイデア:

  1. まず、受信挿入ノードが空かどうかを判断し、空の場合は終了し、そうでない場合は続行します。
  2. 新しいノード newnode のリンク ポインタを、挿入されたノード pos のリンク ポインタ アドレスを指し、最初に後ろに接続します。
  3. 新しいノードの先頭に接続します newnode: 挿入されたノードのリンク ポインタが新しいノードのアドレスを指すようにします。

**時間計算量: **O(1)

参照コード:

void SListInsertAfterUsingElem(SLTNode* pos, SLTDataType x)
{
    
    
	if (pos == NULL)
        return;

	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
    newnode->data = x;
    
	newnode->next = pos->next;
	pos->next = newnode;
}

挿入: ノードの後に​​要素を挿入します (シーケンスメソッド)

デザインのアイデア:

  1. まずヘッド ノードが空かどうかを判断し、空の場合は終了し、そうでない場合は続行します。
  2. トラバーサル ポインタ prev を使用して挿入位置に移動します。
  3. ノード メソッドと一致して、新しいノード リンク ポインタがトラバーサル ポインタのリンク ポインタ アドレスを指すようにします。
  4. 次に、トラバーサル ポインタのリンク ポインタが新しいノードのアドレスを指すようにします。

**時間計算量: **O(N)

参照コード:

C言語

void SListInsertAfterUsingIndex(SLTNode** phead, int index, SLTDateType x)
{
    
    
    if (index == 0 && phead == NULL)
        return;
    SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
    newnode->data = x;

    SLTNode* prev = *phead;
    for (int count = 0; count < index; count++)
    {
    
    
        prev = prev->next;
        if (prev == NULL)
        {
    
    
            printf("超出链表长度导致了插入失败\n");
            return;
        }
    }
    newnode->next = prev->next;
    prev->next = newnode;
}

Insert:ノードの前に要素を挿入(nodeメソッド)

(前方挿入は後方挿入よりも複雑ですが、前方挿入操作の概念をよく理解してください。これは、その後の C++ STL ライブラリの分析に非常に役立ちます)

ノードの前に要素を挿入する必要がある場合は、前のノードのリンク ポインタを新しいノードに変更し、新しいノードのリンク ポインタを現在のノードのアドレスに設定する必要がありますこの文に聞き覚えはありますか? そう、リンクリストの末尾削除方式の考え方(末尾削除方式の立入検査!!)に似ています!ただし、ここでは要素を削除する必要はありません。主に行うことは、前のノードのリンク ポインターを置き換えることです。

デザインのアイデア:

  1. 前のセクションを使用して、ノード値に基づいて要素を検索し、最初にノード情報を見つけることができます。
  2. トラバーサル ポインタを使用してターゲット ノードの前の位置に移動し、新しいノードを挿入して元のノードに接続します。
  3. 挿入アドレスが head 要素内にある場合は、新しいノードのリンク ポインタを元の head 要素のアドレスに設定するだけで済みます。

**時間計算量: **O(N)

参照コード:

void SListInsertBeforeUseElem(SLTNode** phead, SLTNode* pos, SLTDateType x)
{
    
    
    if (phead == NULL)
        return;
    if (pos == NULL)			   		// 确定信息在链表中存在
        return;
    SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
    newnode->data = x;
	newnode->next = NULL;
    
	if (*pphead == pos)					// 如果插入元素的地址是头元素
	{
    
    
		newnode->next = *phead;
		*phead = newnode;
	}
	else
	{
    
    
		SLTNode* posPrev = *phead;		// 用来找到pos的前一个位置的遍历指针
		while (posPrev->next != pos)	// 找到pos的前一个位置
		{
    
    
			posPrev = posPrev->next;
		}

		posPrev->next = newnode;		// 将前一个位置的链接指针为新结点
		newnode->next = pos;			// 将新结点的链接指针为插入位置
	}
}


挿入: ノードの前に要素を挿入します (シーケンス メソッド)

デザインのアイデア:

  1. シーケンスに基づいてリンク リストの要素を見つける方法と似ています。前の位置に戻るだけでよい場合。
  2. 現在のシーケンスの前の位置に到達した後は、ノード メソッドの挿入操作と一致します。

**時間計算量: **O(N)

参照コード:

void SListInsertBeforeUseIndex(SLTNode** phead, int index, SLTDateType x)
{
    
    

    SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
    newnode->data = x;
    newnode->next = NULL;

    if (index == 0)								// 如果插入元素的地址是头元素, 直接插入
    {
    
    
        newnode->next = *phead;
        *phead = newnode;
    }
    else
    {
    
    
        int count = 0;
        SLTNode* posPrev = *phead;				// 遍历指针

        SLTNode* prev = NULL; 					// 记录指针
        while (posPrev->next != NULL)
        {
    
    
            prev = posPrev;			  			// 在进入下一次遍历之前,记录指针会记录着遍历指针当前的位置
            posPrev = posPrev->next;			// 遍历指针进入下一步
            count++;
            if (count == index)
            {
    
    
                prev->next = newnode;			// 将前一个位置的链接指针的指向为新结点
            	newnode->next = posPrev;		// 将新结点的链接指针为插入位置
                return;
            }
        }
        printf("超出链表长度导致了插入失败\n");		// 如果未达到指定位置而已经遍历完成
        return;
    }
}

削除:リンクリストの指定ノードを削除(nodeメソッド)

デザインのアイデア:

  1. 削除されたノードがヘッド ノードの場合は、個別に処理する必要があります。これはヘッドの削除であるため、前のヘッド削除関数を直接呼び出すことができます。
  2. 非ヘッド ノードの場合、削除ロジックは何ですか? ノードを削除するには、その前のノードを検索し、接続ポインタを削除したノードの次のポインタ アドレスにポイントし、その後、削除するノードのポインタを解放して解放する必要があります。

**時間計算量: **O(N)

参考コード

void SListEraseUsingElem(SLTNode** phead, SLTNode* pos)
{
    
    
	if(phead == NULL || pos == NULL)	// 确定传入合法性
        return;

	if (*phead == pos)					// 头删
	{
    
    
		SListPopFront(phead);
	}
	else
	{
    
    
		SLTNode* prev = *phead;			// 遍历指针prev
		while (prev->next != pos)		
		{
    
    
			prev = prev->next;			// 找到删除结点的上一个结点位置
		}

		prev->next = pos->next;			// 将上一结点的链接指针 指向 删除结点的下一结点地址
		free(pos);						// 释放删除结点
		pos = NULL;						// 这里的pos可以置空也可以不置空,因为pos是一个形参,出了函数就会被栈帧销毁
	}
}

削除:リンクリストの指定ノードを削除(シーケンス方式)

デザインのアイデア:

  1. この考え方はノード メソッドと似ていますが、シーケンス メソッドでは削除されたノードの前の位置を見つけるためにループを使用する必要がある点が異なります。
  2. 削除シーケンスの前の位置に到達したら、削除操作を開始します。接続ポインタを削除されたノードの次のポインタ アドレスにポイントし、削除するノードのポインタを解放して解放します。

**時間計算量: **O(N)

参照コード:

void SListEraseUsingIndex(SLTNode** phead, int index)
{
    
    
    if (phead == NULL)								// 确定传入合法性
        return;

    if (index == 0)
    {
    
    
        SListPopFront(phead);
    }
    else
    {
    
    
        int count = 0;
        SLTNode* posPrev = *phead;				    // 遍历指针,等会它会指向删除结点

        SLTNode* prev = NULL; 					    // 记录指针,之后会指向删除结点的前一个位置
        while (posPrev->next != NULL)
        {
    
    
            prev = posPrev;			  			    // 在进入下一次遍历之前,记录指针会记录着遍历指针当前的位置
            posPrev = posPrev->next;			    // 遍历指针进入下一步
            count++;
            if (count == index)
            {
    
    
                prev->next = posPrev->next;			// 将前一个位置的链接指针指向删除结点的下一个位置
                free(posPrev);
                posPrev = NULL;						// 思考一下,这里的posPrev要不要置空呢?
                return;
            }
        }
        printf("超出链表长度导致了删除失败\n");      	 // 如果未达到指定位置而已经遍历完成
        return;
    }
}

削除:ノード以降の要素を削除(ノードメソッド)

デザインのアイデア:

ノード以降の要素を削除するには、シーケンスの最後の 2 位置にあるノードのアドレスを取得してリンクし、次のノードに解放する必要があるため、次のスキームがあります。

  1. 受信ノードを取得した後、まずその次のノード情報 (つまり、削除されたノード) を一時的にコピーします。
  2. 元のノードの次のポインタ アドレスを次のノードの次のノード アドレスにポイントします。
  3. 次に削除したノードを解放します。

**時間計算量: **O(1)

参考コード

void SListEraseAfterUsingElem(SLTNode* pos)
{
    
    
    if (pos == NULL || pos -> next == NULL)		// 检查传入合法性
        return;

	SLTNode* next = pos->next;					// next为结点后的元素,这里先拷贝一份出来
	pos->next = next->next;						// 将结点指向删除元素的下一个结点
	free(next);									// 释放被删除的结点
    next = NULL;								// 同样,这里的next是否置空都可以,但出于职业素养我还是置空吧OWO~
}

削除:ノード以降の要素を削除(シーケンスメソッド)

デザインのアイデア:

この考え方はノード メソッドと一致していますが、シーケンス メソッドにはシーケンスの位置情報しかないため、この位置までループする必要があります。

  1. 削除シーケンスの位置がリンクリストの長さを超える場合は、処理をさらに考慮する必要があります。

時間計算量: O(N)

参考コード

void SListEraseAfterUsingIndex(SLTNode** phead, int index)
{
    
    
    printf("%d: ", index);
    SLTNode* posPrev = *phead;
    for (int count = 0; count < index; count++)
    {
    
    
        posPrev = posPrev->next;
        if (posPrev == NULL)
        {
    
    
            printf("超出链表长度导致了删除失败\n");   // 如果已经到达尾部仍为到达序列位置时直接退出
            return;
        }
    }
    SListEraseAfterUsingElem(posPrev);			 // 因为遍历指针已经走到尾删的结点处,直接调用结点法偷个小懒OWO吧~
}

破棄: リンクされたリストを破棄します。

デザインのアイデア:

リンクされたリストを破棄することは、リンクされたリストを出力し、すべての行を走査し、すべてのスペースを解放するという考えと一致します。

  1. レコードポインタを設計し、それを解放しながら次のノードのアドレスを記録します。
  2. ノードを解放した後、ループは次のノードに入り、最後まで解放され続けます。

**時間計算量: **O(N)

参照コード:

C言語

void SListDestory(SLTNode** pphead)
{
    
    
	if (phead == NULL)
        return;

	SLTNode* cur = *phead;
	while (cur)
	{
    
    
		SLTNode* next = cur->next;
		free(cur);
		cur = next;
	}
	*phead = NULL;
}

シーケンスリストとリンクリストの違い

配列表: 1 人の白人が 100 人の醜い
白人をカバーします。

  1. 空間的な連続性。
  2. ランダムアクセスがサポートされています。

醜い:

  1. 中間部分または前部分での挿入と削除の時間計算量は O(N) です。
  2. 容量拡張のコストは比較的高くなります。

画像-20220213012910545

リンク リスト: 1 つ (ファット ブラック) がすべての
ファット ブラックを破壊します:

  1. ノード単位で格納されます。
  2. ランダムアクセスはサポートされていません。

全て:

  1. 任意の位置での挿入と削除の時間計算量は O(1) です。
  2. 容量拡張の問題はなく、1本入れるとスペースが空きます。

画像-20220213013018296

シーケンステーブル

アドバンテージ

  1. ランダムアクセスがサポートされています。サポートするためにランダム アクセス構造を必要とするアルゴリズムが適しています。
  2. CPU キャッシュ使用率が高くなります。(キャッシュ使用率の詳細については、以下の詳細な説明を参照してください)

欠点:

  1. 頭の途中での挿入と削除の時間は非効率的です; O(N)
  2. 継続的な物理的スペース。スペースが十分でない場合は、将来的に増やす必要があります。
  3. 容量の増加にはある程度の消費があります。
  4. 頻繁な容量拡張を避けるために、通常は容量を倍数に増やしますが、使い切らないとある程度のスペースが無駄になる可能性があります。

リンクされたリスト

アドバンテージ:

  1. 任意の位置での挿入と削除が非常に効率的です。O(1);
  2. オンデマンドで空きスペースを申請します。

欠点:

  1. ランダムアクセスはサポートされていません。(添字付きのアクセス) は、一部の並べ替え、バイナリ検索などがこの構造には適用されないことを意味します。
  2. リンクされたリストは値を格納すると同時にリンク ポインタを格納しますが、これはある程度消費されます。
  3. CPU キャッシュ使用率の低下。
違い シーケンステーブル リンクされたリスト
収納スペース 物理的に連続していなければならない 論理的には連続しているが、必ずしも物理的に連続しているとは限らない
ランダムアクセス O(1) をサポート サポートされていない: O(N)
任意の位置に要素を挿入または削除する 要素を移動する必要がある場合があり、効率が低い O(N) ポインタを変更して指すようにするだけです
入れる ダイナミックシーケンステーブル、スペースが足りない場合は拡張する必要があります 容量の概念がない
アプリケーションシナリオ 要素の効率的な保管 + 頻繁なアクセス 任意の位置での頻繁な挿入と削除
キャッシュの使用率 高い 低い

詳細情報: CPU キャッシュとは何ですか

CPU キャッシュについての以前の紹介を参照してください: CPU キャッシュとは何ですか?

シングルリンクリストは終わりました〜会議は終わりました!

要約する

単一リンクリストの実装と導入ノートはこれで終わりです。~ 関連する知識ポイントは重要であるだけでなく、非常に多くの内容を含むため、長さが長くなります。多くのことを得ることができると信じてください。質問や間違いがある場合は、コメント欄で交換して学んでください。

このシリーズの記事が役立つと思われる場合は、著者を「いいね」してフォローすることを忘れないでください。皆さんの励ましが、私が今後も創作、共有、追加を続ける原動力です。みんなで頂上で会えますように。著者公式アカウント「01 Programming Cabin」にゲストとしてようこそ!小屋を追って迷わずプログラミングを学ぼう!

おすすめ

転載: blog.csdn.net/weixin_43654363/article/details/124567659