データ構造:単連結リストの実装(C言語)

ここに画像の説明を挿入

個人ホームページ:Shuiyue Mengjinghua
個人コラム:「C言語」 「データ構造」


序文

このブログが実装する、ヘッドレスの一方向非循環リンク リスト。


1. シングルリンクリストを実現するためのアイデアと図

1. ノード定義(SListNode)

ノードを次の構造として定義します。
ここに画像の説明を挿入
そのメンバー変数は data と next です。

後でデータ フィールドの内容を変更できるように、int の名前を STLDataType に変更します。

//无头单向不循环链表
typedef int SLTDataType;

typedef struct SListNode
{
    
    
	SLTDataType data;
	struct SListNode* next;
}SListNode;

2. ノードの申請(BuySListNode)

データを配置するスペースを動的に宣言します。以下のように、
ここに画像の説明を挿入
データの内容を仮パラメータ x に設定し、その横に NULL を設定します。

//申请一个节点
SListNode* BuySListNode(SLTDataType x)
{
    
    
	SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));
	if (newnode == NULL)
	{
    
    
		perror("malloc");
		exit(-1);
	}
	newnode->data = x;
	newnode->next = NULL;

	retur

3. 単一リンクリスト印刷(SListPrint)

リンクされたリストを終了ノードまでループします。構造体ポインター変数 cur を作成し、cur = cur->next をループし、cur == NULL になるまで cur->data の内容を出力します。
ここに画像の説明を挿入

//单链表打印
void SListPrint(SListNode* plist)
{
    
    
	SListNode* cur = plist;

	while (cur != NULL)
	{
    
    
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}

4. 単一リンクリスト末尾挿入 (SListPushBack)

  • リンク リストが NULL ではない (リンク リストに要素がある) 場合は、まずリンク リストを走査して末尾ノードを見つけ、末尾ノードが新しいノードを指すようにして末尾の挿入を完了します。
  • リンク リストが NULL (リンク リストに要素がない) の場合、この時点で、末尾の挿入を完了するには、新しいノードが先頭ノードと直接等しくなる必要があります。(このリンクされたリストにはセンチネル ビットがありません)
  • 入力されたヘッドノードが無効な場合は、そのままエラー判定を行います。

ここに画像の説明を挿入

ここに画像の説明を挿入
リンクされたリストが NULL の場合、ヘッド ノード自体の内容を変更する必要があるため、ヘッド ノードへのポインタが必要です。この記事では、ヘッド ノード自体が構造体ポインタであるため、次のセカンダリ ポインタが必要です。末尾挿入関数のパラメータ。

//单链表尾插
void SListPushBack(SListNode** pplist, SLTDataType x)
{
    
    
	assert(pplist);

	SListNode* newnode =  BuySListNode(x);
	if (*pplist != NULL)
	{
    
    
		SListNode* cur = *pplist;
		while (cur->next != NULL)
		{
    
    
			cur = cur->next;
		}
		cur->next = newnode;
	}
	else
	{
    
    
		*pplist = newnode;
	}
}

5. 単一リンクリストの先頭挿入(SListPushFront)

ヘッド挿入の場合、リンク リストに要素があるかどうかは関係ありません。新しいノードがヘッド ノードを指すようにし、ヘッド ノードを新しいノードと等しくするだけで済みます。

ここに画像の説明を挿入
ヘッド挿入リンク リストはヘッド ノードの内容を確実に変更するため、ヘッド挿入関数の仮パラメータは二次ポインタでもあります。

//单链表的头插
void SListPushFront(SListNode** pplist, SLTDataType x)
{
    
    
	assert(pplist);

	SListNode* newnode = BuySListNode(x);
	
	newnode->next = *pplist;
	*pplist = newnode;
}

6. 単一リンクリストの末尾削除 (SListPopBack)

  • リンクされたリスト要素が 2 つ以上ある場合、末尾ノードを見つけるには 2 つのポインター変数 prev と next が必要です。prev = cur、cur = cur->next をループし、next を末尾ノードにポイントし、prev を前のノードにポイントします。末尾ノードの 1 つ、空き末尾ノード、prev が指すノードは NULL を指します。
  • リンクされたリスト要素が 1 つしかない場合は、ヘッド ノードを直接解放し、ヘッド ノードを NULL に設定します。
  • リンクリストに要素が無い場合はそのまま間違っていると判定されます。

ここに画像の説明を挿入

ここに画像の説明を挿入
リンク リストの要素が 1 つだけの場合は、この時点でリンク リストを削除します。ヘッド ノードの内容を変更するには、削除関数の仮パラメータにセカンダリ ポインタが必要です。

//单链表的尾删
void SListPopBack(SListNode** pplist)
{
    
    
	assert(pplist);
	//链表为NULL
	assert(*pplist);

	if ((*pplist)->next == NULL)
	{
    
    
		free(*pplist);
		*pplist = NULL;
	}
	else
	{
    
    
		SListNode* cur = *pplist;
		SListNode* prev = NULL;

		while (cur->next != NULL)
		{
    
    
			prev = cur;
			cur = cur->next;
		}
		prev->next = NULL;
		free(cur);
	}
}

7. 単一リンク リスト ヘッダーの削除 (SListPopFront)

ヘッド ノードの次のノードを保存するには、構造体ポインタ変数 next が必要です。その後、ヘッド ノードを解放して、ヘッド ノード = ポインタ変数 next になります。

  • リンクリストに要素が無い場合はそのまま間違っていると判定されます。

ここに画像の説明を挿入

//单链表头删
void SListPopFront(SListNode** pplist)
{
    
    
	assert(pplist);
	assert(*pplist);

	SListNode* next = (*pplist)->next;
	free(*pplist);
	*pplist = next;
}

8. 単一リンクリスト検索 (SListFind)

リンクされたリストを走査し、cur->data == x を比較するには、構造体ポインタ変数 cur が必要です。それらが等しい場合は、この時点の cur の内容 (ノードのアドレス) を戻します。リンクされたリストを走査した後に等しい要素がない場合は、NULL を返します。

//单链表的查找
SListNode* SListFind(SListNode* plist, SLTDataType x)
{
    
    
	SListNode* cur = plist;
	while (cur != NULL)
	{
    
    
		if (cur->data == x)
		{
    
    
			return cur;
		}
		cur = cur->next;
	}

	return NULL;
}

9. 単一リンクリストは、pos 位置の後に x を挿入します (SListInsertAfter)

newnode が pos の隣のノードを指すようにし、pos が newnode を指すようにします。

  • 最初に pos が newnode を指すようにし、newnode が pos の次のノードを指すようにすると、newnode がそれ自体を指すようになり、リンクされたリストがループを形成します。
  • この関数はヘッド ノードの内容に影響を与えないため、関数の仮パラメーターはセカンダリ ポインターを使用しません。

ここに画像の説明を挿入


//单链表在pos位置之后插入x
void SListInsertAfter(SListNode* pos, SLTDataType x)
{
    
    
	assert(pos);

	SListNode* newnode = BuySListNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}

10. 単一リンクリストは、pos 位置の後の値を削除します (SListEraseAfter)

pos の次のノードのアドレスを記録するには構造体ポインタ変数 next が必要です。pos は next の次のノードを指し、次に free(next) を指します。

  • リンクされたリストに要素が 1 つしかない場合、この関数を呼び出すことはできません。そうしないと、NULL ポインターの逆参照が発生します。
  • この関数はヘッド ノードの内容を変更しないため、仮パラメーターにセカンダリ ポインターを使用しません。

ここに画像の説明を挿入


//单链表删除在pos位置之后的值
void SListEraseAfter(SListNode* pos)
{
    
    
	assert(pos && pos->next);

	SListNode* next = pos->next;
	pos->next = next->next;
	free(next);
}

11. 単一リンクリストの破棄 (SListDestroy)

2 つの構造体ポインター prev と cur が必要です。最初に prev = cur とし、次に cur が次のノード free(prev) を指すようにし、cur が NULL を指すまで上記の操作を繰り返します。

ここに画像の説明を挿入
関数を呼び出した後、ヘッド ノードに NULL を設定する必要があります。

//单链表的销毁
void SListDestroy(SListNode* plist)
{
    
    
	assert(plist);

	SListNode* cur = plist;
	while (cur != NULL)
	{
    
    
		SListNode* prev = cur;
		cur = cur->next;
		free(prev);
	}
}

2. コードの実装

//slist.h  文件


#pragma once
#define _CRT_SECURE_NO_WARNINGS 1

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

//无头单向不循环链表
typedef int SLTDataType;

typedef struct SListNode
{
    
    
	SLTDataType data;
	struct SListNode* next;
}SListNode;


//申请一个节点
SListNode* BuySListNode(SLTDataType x);

//单链表打印
void SListPrint(SListNode* plist);

//单链表尾插
void SListPushBack(SListNode** pplist, SLTDataType x);

//单链表的头插
void SListPushFront(SListNode** pplist, SLTDataType x);

//单链表的尾删
void SListPopBack(SListNode** pplist);

//单链表头删
void SListPopFront(SListNode** pplist);

//单链表的查找
SListNode* SListFind(SListNode* plist, SLTDataType x);

//单链表在pos位置之后插入x
void SListInsertAfter(SListNode* pos, SLTDataType x);

//单链表删除在pos位置之后的值
void SListEraseAfter(SListNode* pos);

//单链表的销毁
void SListDestroy(SListNode* plist);
//slist.c    文件


#include "slist.h"

//申请一个节点
SListNode* BuySListNode(SLTDataType x)
{
    
    
	SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));
	if (newnode == NULL)
	{
    
    
		perror("malloc");
		exit(-1);
	}
	newnode->data = x;
	newnode->next = NULL;

	return newnode;
}


//单链表打印
void SListPrint(SListNode* plist)
{
    
    
	SListNode* cur = plist;

	while (cur != NULL)
	{
    
    
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}


//单链表尾插
void SListPushBack(SListNode** pplist, SLTDataType x)
{
    
    
	assert(pplist);

	SListNode* newnode =  BuySListNode(x);
	if (*pplist != NULL)
	{
    
    
		SListNode* cur = *pplist;
		while (cur->next != NULL)
		{
    
    
			cur = cur->next;
		}
		cur->next = newnode;
	}
	else
	{
    
    
		*pplist = newnode;
	}
}


//单链表的头插
void SListPushFront(SListNode** pplist, SLTDataType x)
{
    
    
	assert(pplist);

	SListNode* newnode = BuySListNode(x);
	
	newnode->next = *pplist;
	*pplist = newnode;
}

//单链表的尾删
void SListPopBack(SListNode** pplist)
{
    
    
	assert(pplist);
	//链表为NULL
	assert(*pplist);

	if ((*pplist)->next == NULL)
	{
    
    
		free(*pplist);
		*pplist = NULL;
	}
	else
	{
    
    
		SListNode* cur = *pplist;
		SListNode* prev = NULL;

		while (cur->next != NULL)
		{
    
    
			prev = cur;
			cur = cur->next;
		}
		prev->next = NULL;
		free(cur);
	}
}


//单链表头删
void SListPopFront(SListNode** pplist)
{
    
    
	assert(pplist);
	assert(*pplist);

	SListNode* next = (*pplist)->next;
	free(*pplist);
	*pplist = next;
}


//单链表的查找
SListNode* SListFind(SListNode* plist, SLTDataType x)
{
    
    
	SListNode* cur = plist;
	while (cur != NULL)
	{
    
    
		if (cur->data == x)
		{
    
    
			return cur;
		}
		cur = cur->next;
	}

	return NULL;
}

//单链表在pos位置之后插入x
void SListInsertAfter(SListNode* pos, SLTDataType x)
{
    
    
	assert(pos);

	SListNode* newnode = BuySListNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}


//单链表删除在pos位置之后的值
void SListEraseAfter(SListNode* pos)
{
    
    
	assert(pos && pos->next);

	SListNode* next = pos->next;
	pos->next = next->next;
	free(next);
}


//单链表的销毁
void SListDestroy(SListNode* plist)
{
    
    
	assert(plist);

	SListNode* cur = plist;
	while (cur != NULL)
	{
    
    
		SListNode* prev = cur;
		cur = cur->next;
		free(prev);
	}
}

要約する

上記は、ヘッドレス一方向非循環リンク リストの実装です。

ここに画像の説明を挿入

おすすめ

転載: blog.csdn.net/li209779/article/details/131991380