データ構造の深い理解(2)---シーケンステーブルの実現

シーケンステーブル

シーケンステーブルは一種の線形構造です----シーケンシャルストレージ構造

この構造体は、メモリ内の連続したスペースを占有します(配列と同様)。

シーケンステーブルは、次の2つの方法で実装できます。

  • 1つは、静的配列を使用して次のことを実現することです。
#define NUM 100
typedef struct SeqList{
    
    
    int a[NUM];
    int size;//表长
}SeqList;

NUMは配列のサイズであり、シーケンステーブルに配置できる要素の数です。

サイズを使用して、特定の瞬間に配列に配置された要素の数を記録できます。

  • 1つは、動的開発を使用して達成することです
typedef struct SeqList {
    
    
	SeqListType* list;
	int size;//表长
	int capacity;
}SeqList;

ポインタの使用と動的メモリ開発により、スペースのサイズを柔軟に制御できます

また、サイズを使用して、保存されている要素の数を記録します

容量は静的アレイのNUMに似ていますが、必要に応じて容量を拡張または削減できる点が異なります。

2つの比較:使用する前に実際に使用する必要のあるスペースの量を判断できないため、スペースのサイズを柔軟に制御できる動的開発の方法を使用します。

シーケンステーブルに実装する必要のある関数:

シーケンステーブルは、次の機能を実行できます。

//函数的声明
//顺序表的初始化----SeqListInit

//尾插一个元素----SeqListPushBack
//尾删一个元素----SeqListPopBack

//头插一个元素----SeqListPushFront
//头删一个元素----SeqListPopFront

//任何位置的元素插入----SeqListInsert
//任何位置的元素删除----SeqListErase

//检查容量----SeqListCheckCapacity

//销毁顺序表----SeqListDeStroy

次に、これらの関数を順番に実装します

シーケンステーブルの初期化

void SeqListInit(SeqList* psl)
{
    
    
	assert(psl);
	psl->capacity = 0;
	psl->size = 0;
	psl->list = NULL;
}

容量の初期値から開始することも、次のように初期値を0に割り当てることもできます。

シーケンステーブルの容量

注:シーケンステーブルを操作するときは、シーケンステーブルの容量がいっぱいかどうかに注意する必要があります。シーケンステーブルの容量がいっぱいの場合は、容量を拡張して、次の要素を追加し続けることができるようにする必要があります。シーケンステーブル。

void SeqListCheckCapacity(SeqList* psl)
{
    
    
	assert(psl);//判断psl是否为空指针

	if (psl->size == psl->capacity)
	{
    
    
		int newcapacity = psl->capacity == 0 ? 4 : psl->capacity * 2;
        //如果capacity的值为0(只有初始化的时候才可能为0),那么就将capacity 的值赋为4。
        //如果caoacity的值不为0,那么就让容量翻倍。

		SeqListType* tmp = (SeqListType*)realloc(psl->list, sizeof(SeqListType) * newcapacity);
		if (tmp == NULL)
		{
    
    
			printf("realloc:fail\n");
			exit(-1);
		}
		else
		{
    
    
			psl->list = tmp;
			psl->capacity = newcapacity;
		}

	}

}

サイズと容量の値が同じ場合は、シーケンステーブルの容量がいっぱいであることを意味し、容量を拡張する必要があります

注:展開が成功したかどうかを判断する必要があります(tmpがnullポインターであるかどうかを判断します)。

シーケンスリストに要素を追加します

シーケンスリストの最後に要素を追加します

注:1。シーケンステーブルに要素を追加するたびに、シーケンステーブルの容量を確認する必要があります。

2.要素を追加するたびに、sizeの値を1ずつ増やす必要があります。

void SeqListPushBack(SeqList* psl, SeqListType x)
{
    
    
	assert(psl);
	SeqListCheckCapacity(psl);
	psl->list[psl->size] = x;
	psl->size++;
}

テールに要素を追加する場合psl->list[psl->size]は、割り当てたいアドレスです。サイズの値は簡単に取得できるため、テールに要素を追加する操作は非常に簡単です。

シーケンスリストの先頭に要素を追加します

void SeqListPushFront(SeqList* psl, SeqListType x)
{
    
    
	assert(psl);

	SeqListCheckCapacity(psl);
	
	int begin = psl->size;
	while (begin > 0)
	{
    
    
		psl->list[begin] = psl->list[begin - 1];
		begin--;
	}
	psl->list[0] = x;
	psl->size++;
	
}

ここに画像の説明を挿入

シーケンステーブルの指定された位置に要素を追加します

void SeqListInsert(SeqList* psl, size_t pos, SeqListType x)
{
    
    
	assert(psl);
	if (pos > psl->size)
	{
    
    
		printf("越界");
	}
	SeqListCheckCapacity(psl);//检查容量
	size_t begin = psl->size;
	while (begin > pos)
	{
    
    
		psl->list[begin] = psl->list[begin - 1];
		begin--;
		}
	psl->list[pos] = x;
	psl->size++;
	
}

指定された位置に要素を追加することは、シーケンスリストの先頭に要素を追加することと非常に似ています。

ここに画像の説明を挿入

ただし、要素を挿入する前に、渡されたパラメータが条件を満たすかどうかを判断する必要があります

ここに画像の説明を挿入

挿入位置が異なれば、結果も異なります

ここに画像の説明を挿入

シーケンスリストから要素を削除します

シーケンステーブルで要素が削除されるたびに、サイズの値を1ずつ減らす必要があることに注意してください。

シーケンスリストの最後にある要素を削除します

//尾删一个元素
void SeqListPopBack(SeqList* psl)
{
    
    
	assert(psl);

	if (psl->size > 0)
	{
    
    
		psl->size--;
	}

}

最後に要素を削除するには、テーブルの長さ(size)を1だけ減らす必要があります。これは、sizeを使用してシーケンステーブルにある要素の数を記録するためです。これは、最後の要素を削除することと同じです。

シーケンスリストの先頭にある要素を削除します

//头删一个元素
void SeqListPopFront(SeqList* psl)
{
    
    
	assert(psl);

	int begin = 1;
	while (begin < psl->size)
	{
    
    
		psl->list[begin-1] = psl->list[begin];
		begin++;
	}
	psl->size--;
}

要素テーブルの先頭にある要素を削除するには、シーケンステーブルの2番目の要素から開始し、最後の要素の値まで前方にカバーし、最後から2番目の要素の位置をカバーするだけです。テーブルの長さ(サイズ)から1を引いて、head要素の削除を完了します。

ここに画像の説明を挿入

シーケンステーブルの指定された位置にある要素を削除します

void SeqListErase(SeqList* psl, size_t pos)
{
    
    

	assert(psl);
	assert(pos < psl->size);
	
		size_t begin = pos+1;
		while (begin < psl->size)
		{
    
    
			psl->list[begin-1] = psl->list[begin];
			begin++;
		}
		psl->size--;
	

}

指定された位置にある要素を削除する方法は、head要素を削除する方法と似ています。

ここに画像の説明を挿入

破壊命令表

動的メモリ開発の方法で実装されたシーケンステーブルを使用しているため、最終的に動的に開かれたスペースを解放する必要があります。

void SeqListDeStroy(SeqList* psl)
{
	assert(psl);

	free(psl->list);
	psl->capacity = 0;
	psl->size = 0;
	psl->list = NULL;
}

シーケンステーブルの完全な実装

ヘッドファイルSeqList.h

#pragma once
//顺序表

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
//结构的声明
//有两种顺寻标的声明,一种是静态的---以数组的形式
//另一种是动态的----动态内存开辟,第一种方式开辟出来的空间可能会不足,,也可能会浪费,所以我们采用动态内存开辟的方式

typedef int SeqListType;

typedef struct SeqList {
    
    
	SeqListType* list;
	int size;
	int capacity;
}SeqList;

//一个顺序表要实现的功能有:在任何一个位置插入一个元素,
//在任何一个位置删除一个元素,修改元素,查找元素


//函数的声明

//顺序表的初始化
void SeqListInit(SeqList* psl);

//尾插一个元素
void SeqListPushBack(SeqList* psl, SeqListType x);
//尾删一个元素
void SeqListPopBack(SeqList* psl);
//头插一个元素
void SeqListPushFront(SeqList* psl, SeqListType x);

//头删一个元素
void SeqListPopFront(SeqList* psl);

//任何位置的元素插入
void SeqListInsert(SeqList* psl, size_t pos, SeqListType x);
//任何位置的元素删除
void SeqListErase(SeqList* psl, size_t pos);
//检查容量
void SeqListCheckCapacity(SeqList* psl);

//销毁顺序表
void SeqListDeStroy(SeqList* pal);

//打印顺序表的内容
void SeqListPrint(SeqList* psl);

ソースファイルSeqList.c

#define _CRT_SECURE_NO_WARNINGS 1
//函数实现
#include"SeqList.h"

//顺序表的初始化
void SeqListInit(SeqList* psl)
{
	assert(psl);
	psl->capacity = 0;
	psl->size = 0;
	psl->list = NULL;
}



//检查容量
void SeqListCheckCapacity(SeqList* psl)
{
	assert(psl);

	if (psl->size == psl->capacity)
	{


		int newcapacity = psl->capacity == 0 ? 4 : psl->capacity * 2;

		SeqListType* tmp = (SeqListType*)realloc(psl->list, sizeof(SeqListType) * newcapacity);
		if (tmp == NULL)
		{
			printf("realloc:fail\n");
			exit(-1);
		}
		else
		{
			psl->list = tmp;
			psl->capacity = newcapacity;

		}

	}

}
//尾插一个元素
void SeqListPushBack(SeqList* psl, SeqListType x)
{
	assert(psl);


	SeqListCheckCapacity(psl);

	psl->list[psl->size] = x;
	psl->size++;

}


//尾删一个元素
void SeqListPopBack(SeqList* psl)
{
	assert(psl);

	if (psl->size > 0)
	{
		psl->size--;
	}

}


//头插一个元素
void SeqListPushFront(SeqList* psl, SeqListType x)
{
	assert(psl);

	SeqListCheckCapacity(psl);
	
	int begin = psl->size;
	while (begin > 0)
	{
		psl->list[begin] = psl->list[begin - 1];
		begin--;
	}
	psl->list[0] = x;
	psl->size++;
	

}

//头删一个元素
void SeqListPopFront(SeqList* psl)
{
	assert(psl);

	int begin = 1;
	while (begin < psl->size)
	{
		psl->list[begin-1] = psl->list[begin];
		begin++;
	}
	psl->size--;

}

//任何位置的元素插入
void SeqListInsert(SeqList* psl, size_t pos, SeqListType x)
{
	assert(psl);
	if (pos > psl->size)
	{

		printf("越界");

	}

	SeqListCheckCapacity(psl);
	
	
	
		size_t begin = psl->size;
		while (begin > pos)
		{
			psl->list[begin] = psl->list[begin - 1];
			begin--;
		}
		psl->list[pos] = x;
		psl->size++;
	
}

//任何位置的元素删除
void SeqListErase(SeqList* psl, size_t pos)
{

	assert(psl);
	assert(pos < psl->size);
	
		size_t begin = pos+1;
		while (begin < psl->size)
		{
			psl->list[begin-1] = psl->list[begin];
			begin++;
		}
		psl->size--;
	

}

//销毁顺序表
void SeqListDeStroy(SeqList* psl)
{
	assert(psl);

	free(psl->list);
	psl->capacity = 0;
	psl->size = 0;
	psl->list = NULL;
}

//打印顺序表的内容
void SeqListPrint(SeqList* psl)
{
	assert(psl);

	int i = 0;
	for (i = 0; i < psl->size; i++)
	{
		printf("%d ", psl->list[i]);
	}
	printf("\n");
}

ソースファイルtest.c

void menu()
{
    
    
	printf("********************************\n");
	printf("***  1.尾插      2.尾删      ***\n");
	printf("***  3.头插      4.头删      ***\n");
	printf("***  5.指定插入  6.指定删除  ***\n");
	printf("***  7.查看     -1.退出      ***\n");
	printf("********************************\n");
}


int main()
{
    
    

	SeqList data;
	InitSeqList(&data);
	int option = -1;
	do {
    
    
		menu();
		printf("请选择您想要进行的操作:>");
		scanf("%d", &option);
		SeqListType val = 0;
		size_t pos = 0;

		switch (option)
		{
    
    
		case 1:
		
			printf("请输入您想要插入的数据:>");
			scanf("%d", &val);
			SeqListPushBack(&data, val);
		

			break;
		case 2:

			SeqListPopBack(&data);
			break;
		case 3:
			printf("请输入您想要插入的数据:>");
			scanf("%d", &val);

			SeqListPushFront(&data, val);

			break;

		case 4:

			SeqListPopFront(&data);
			break;

		case 5:
			printf("请输入你想要插入的数据:>");
			scanf("%d", &val);
			printf("请输入你想要插入的位置:>");
			scanf("%d", &pos);
			SeqListInsert(&data, pos, val);

			break;

		case 6:
			printf("请输入您想要删除的位置:>");
			scanf("%d", &pos);
			SeqListErase(&data, pos);
			break;

		case 7:
	
			SeqListPrint(&data);
			break;
		case -1:
			printf("退出程序\n");
			return 0;
			break;

		default:
			printf("请重新选择\n");
			break;
		}


	} while (option);


	test2(&data);


	return 0;
}

まとめ

  • シーケンステーブルを操作するときは、シーケンステーブルの次の3つのプロパティについて明確にする必要があります。
  1. 保管スペースの開始場所
  2. シーケンステーブルの最大容量
  3. シーケンステーブルの現在の長さ
  • シーケンステーブルの現在の長さと開始位置を使用して、シーケンステーブルに要素を追加および削除できますが、
    各要素を追加するとテーブルの長さが1ずつ増加し、テーブルの長さが1つ減少することに注意してください。各要素が削除された後1。

おすすめ

転載: blog.csdn.net/cainiaochufa2021/article/details/123469139