【データ構造】 シーケンステーブル(リニアテーブル)の実現

目次

         1. シーケンステーブルとは何ですか?

         第二に、シーケンステーブルの動的実現

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

               2. シーケンステーブルの印刷

               3. シーケンステーブルチェックスペース

               4. シーケンステーブルの末尾挿入

               5. シーケンスリストの最後を削除

               6. シーケンスヘッダプラグ

               7. シーケンスヘッダーの削除

               8. 配列表の指定位置に挿入

               9. シーケンステーブルの指定位置を削除

               10. シーケンステーブルの検索

               11. シーケンステーブルの破壊

         3. ソースコード

               1、SeqList.h

               2、SeqList.c

               3、テスト.c

 


 

1. シーケンステーブルとは何ですか?

順序テーブルは、連続した物理アドレスを持つ記憶装置にデータ要素を順番に格納する線形構造であり、通常は配列で格納される。アレイ上のデータを追加、削除、確認、変更します。

シーケンス テーブルは次のように構成されています。

 

シーケンス テーブルは通常、次の 2 つのタイプに分類されます。

1. 静的シーケンス テーブル: 要素を格納するために固定長配列を使用します。

#define Max 10 
typedef int SLDataType;
typedef struct SeqList
{
	SLDataType data[Max];  //定长数组
	size_t size;  //有效数据的个数
}SeqList;

2. 動的シーケンス テーブル: 動的に開発された配列ストレージを使用します。

typedef int SLDataType;
typedef struct SeqList
{
	SLDataType* a;      // 指向动态开辟数组的指针
	size_t size;        // 有效数据个数
	size_t capicity;    // 容量空间的大小
}SeqList;

この記事では主に動的シーケンステーブルを使用します。静的シーケンステーブルは配列空間のサイズを事前に設定しているため、必要な空間サイズが事前設定されたサイズよりも大きい場合、この時点でオーバーフローが発生し、使用が不便ですここでは、可能な限り多くの配列スペースを開くために、動的シーケンス テーブルが使用されます。

第二に、シーケンステーブルの動的実現

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

//初始化
void SLInit(SL* ps)
{
	assert(ps);
	ps->a = NULL;
	ps->size = 0;
	ps->capacity = 0;
}

初期化中にシーケンス テーブル用のスペースを空けることもできます。もちろん、ポインタ a を直接 NULL に設定し、サイズと容量を 0 に設定することもできます。

  2. シーケンステーブルの印刷

//打印
void SLPrint(SL* ps)
{
	assert(ps);
	for (int i = 0; i < ps->size; i++)
	{
		printf("%d ", ps->a[i]);
	}
	printf("\n");
}

  3. シーケンステーブルチェックスペース

順序表の挿入および削除には、順序表のスペースのサイズが関係します。挿入するときは、まずスペースがいっぱいかどうかを判断する必要があります。いっぱいの場合は、挿入する前にスペースを空け続ける必要があります。

void SLCheckCapacity(SL* ps)
{
	assert(ps);
	//扩容
	if (ps->size == ps->capacity)
	{
		int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
		SLDataType* tmp = (SLDataType*)realloc(ps->a, newCapacity * sizeof(SLDataType));
		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}
		ps->a = tmp;
		ps->capacity = newCapacity;
	}
}

ここでチェックをスペースをチェックする関数としてカプセル化しているのは、後で挿入するときに使用しやすいようにするためです。

このシーケンステーブルを最初に作成したときの容量は0でしたが、このとき要素4つ分のスペースを空けておき、容量がいっぱいになったらそのまま容量を2倍にします。realloc関数にnullポインタを渡すと、このときのrealloc関数はmalloc関数と同等になるので、ここで直接realloc関数を使うことも可能です。

  4. シーケンステーブルの末尾挿入

//尾插 O(1)
void SLPushBack(SL* ps, SLDataType x)
{
	assert(ps);
	SLCheckCapacity(ps);
	ps->a[ps->size] = x;
	ps->size++;
}

まず、配列領域のサイズを確認し、容量が足りない場合は直接挿入できますが、容量がいっぱいの場合は、容量を拡張してから挿入する必要があります。SLCheckCapacity()の機能は、容量がいっぱいかどうかをチェックし、いっぱいの場合はすぐに容量を拡張します。

  

  5. シーケンステーブルの末尾を削除

//尾删
void SLPopBack(SL* ps)
{
	assert(ps);
	///暴力检查
	assert(ps->size > 0);
	ps->size--;
}

末尾の削除では実際には size のみが使用されますが、注意すべき 1 つの問題は、size = 0 の場合、シーケンス テーブル自体が空であるため、範囲外のアクセスを防ぐためにこれ以上縮小できないことです。そのため、ここで 2 つのアサーションが追加されます。これを防ぐことができます。

  6. シーケンスヘッダプラグ

//头插 O(N)
void SLPushFront(SL* ps, SLDataType x)
{
	assert(ps);
	//检查容量够不够
	SLCheckCapacity(ps);

	//挪动数据
	int end = ps->size - 1;
	while (end >= 0)
	{
		ps->a[end + 1] = ps->a[end];
		end--;
	}
	ps->a[0] = x;
	ps->size++;
}

まず、ヘッドプラグも容量のサイズをチェックして容量が十分であることを確認し、格納されたデータを 1 ビット後方に移動して最初の位置を空け、最後に新しい要素を挿入する必要があります。

  7. シーケンスヘッダーの削除

//头删
void SLPopFront(SL* ps)
{
	assert(ps);
	assert(ps->size > 0);
	//挪动数据
	int begin = 1;
	while (begin < ps->size)
	{
		ps->a[begin - 1] = ps->a[begin];
		begin++;
	}
	//暴力检查
	ps->size--;
}

先頭削除と末尾削除の一般的なロジックは同じですが、最初にサイズが 0 であるかどうかを確認する必要があります。次の操作では、後続のデータで前のデータを上書きし、最終的にヘッダーの削除の効果を完了するだけです。

  8. 配列表の指定位置に挿入 

任意の位置に挿入することは、ヘッド プラグとテール プラグのアップグレード バージョンに似ていますが、追加のパラメーター pos がある点が異なります。pos が開始位置にある場合はヘッド プラグと同等であり、pos が最後にある場合はヘッド プラグと同等です。テールプラグに相当します。

//在pos的位置插入数据
void SLInsert(SL* ps, int pos, SLDataType x)
{
	assert(ps);
	assert(pos >= 0);
	assert(pos <= ps->size);
	//检查容量够不够
	SLCheckCapacity(ps);
	int end = ps->size - 1;
	while (end >= pos)
	{
		ps->a[end + 1] = ps->a[end];
		end--;
	}
	ps->a[pos] = x;
	ps->size++;
}

挿入位置の添字を pos とすると、pos 位置から最後までのデータを全体的に後方に移動し、pos の位置を空にして、pos の位置に新しい要素を挿入するだけです。 、最後にサイズ++。

注:assert(pos >= 0);assert(pos <= ps->size); これらの 2 行のコードは、挿入される要素が 0 と size の間に制限され、他の場所には制限されず、要素が確実に要素間の要素は連続的に格納されます。

 

  9. シーケンステーブルの指定位置を削除 

//在pos的位置删除数据
void SLErase(SL* ps, int pos)
{
	assert(ps);
	assert(pos >= 0);
	assert(pos < ps->size);
	//挪动数据覆盖
	int begin = pos + 1;
	while (begin < ps->size)
	{
		ps->a[begin - 1] = ps->a[begin];
		begin++;
	}
	ps->size--;
}

まず、サイズが 0 かどうかをアサートする必要があります。次に、pos の後ろのデータが全体として前方に移動され、pos の元の位置のデータがカバーされ、最後に size- がカバーされます。

  10. シーケンステーブルの検索

int SLFind(SL* ps, SLDataType x, int begin)
{
	assert(ps);

	for (int i = begin; i < ps->size; ++i)
	{
		if (ps->a[i] == x)
		{
			return i;
		}
	}
	return -1;
}

この関数のインターフェイスは非常に単純で、配列を 1 回走査し、見つかった場合は要素の添え字を返し、見つからなかった場合は -1 を返すだけです。

  11. シーケンステーブルの破壊

//销毁
void SLDestory(SL* ps)
{
	assert(ps);
	//if (ps->a != NULL)
	if (ps->a)
	{
		free(ps->a);
		ps->a = NULL;
		ps->capacity = 0;
		ps->size = 0;
	}
}

開始時には、動的に開かれた領域がヒープ領域に適用されており、メモリ リークを防ぐために、使い切った後は解放する必要があります。

3. ソースコード

  1、SeqList.h

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

//动态的顺序表 - 按需扩空间
#define N  10
typedef int SLDataType;

typedef struct SeqList
{
	SLDataType* a;
	int size;//记录存储多少个有效数据
	int capacity;//空间容量大小
}SL;

void SLPrint(SL* ps);//打印
void SLInit(SL* ps);//初始化
void SLDestory(SL* ps);//销毁
void SLCheckCapacity(SL* ps);//检查空间大小

//尾插
void SLPushBack(SL* ps, SLDataType x);
//尾删
void SLPopBack(SL* ps);

//头插
void SLPushFront(SL* ps, SLDataType x);
//头删
void SLPopFront(SL* ps);

//中间的插入
//在pos位置插入数据
void SLInsert(SL* ps, int pos, SLDataType x);
//中间的删除
//删除pos位置的数据
void SLErase(SL* ps, int pos);

//查找
int SLFind(SL* ps, SLDataType x);

  2、SeqList.c

#include "SeqList.h"

//打印
void SLPrint(SL* ps)
{
	assert(ps);
	for (int i = 0; i < ps->size; i++)
	{
		printf("%d ", ps->a[i]);
	}
	printf("\n");
}


//初始化
void SLInit(SL* ps)
{
	assert(ps);
	ps->a = NULL;
	ps->size = 0;
	ps->capacity = 0;
}

//销毁
void SLDestory(SL* ps)
{
	assert(ps);
	//if (ps->a != NULL)
	if (ps->a)
	{
		free(ps->a);
		ps->a = NULL;
		ps->capacity = 0;
		ps->size = 0;
	}
}

void SLCheckCapacity(SL* ps)
{
	assert(ps);
	//扩容
	if (ps->size == ps->capacity)
	{
		int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
		SLDataType* tmp = (SLDataType*)realloc(ps->a, newCapacity * sizeof(SLDataType));
		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}
		ps->a = tmp;
		ps->capacity = newCapacity;
	}
}
//尾插 O(1)
void SLPushBack(SL* ps, SLDataType x)
{
	assert(ps);
	SLCheckCapacity(ps);
	ps->a[ps->size] = x;
	ps->size++;
}
//尾删
void SLPopBack(SL* ps)
{
	assert(ps);
	//暴力检查
	assert(ps->size > 0);
	ps->size--;
}

//头插 O(N)
void SLPushFront(SL* ps, SLDataType x)
{
	assert(ps);
	//检查容量够不够
	SLCheckCapacity(ps);

	//挪动数据
	int end = ps->size - 1;
	while (end >= 0)
	{
		ps->a[end + 1] = ps->a[end];
		end--;
	}
	ps->a[0] = x;
	ps->size++;
}
//头删
void SLPopFront(SL* ps)
{
	assert(ps);
	assert(ps->size > 0);
	//挪动数据
	int begin = 1;
	while (begin < ps->size)
	{
    	ps->a[begin - 1] = ps->a[begin];
		begin++;
	}
	//暴力检查
	ps->size--;
}

//在pos的位置插入数据
void SLInsert(SL* ps, int pos, SLDataType x)
{
	assert(ps);
	assert(pos >= 0);
	assert(pos <= ps->size);
	//检查容量够不够
	SLCheckCapacity(ps);
	int end = ps->size - 1;
	while (end >= pos)
	{
		ps->a[end + 1] = ps->a[end];
		end--;
	}
	ps->a[pos] = x;
	ps->size++;
}
//在pos的位置删除数据
void SLErase(SL* ps, int pos)
{
	assert(ps);
	assert(pos >= 0);
	assert(pos < ps->size);
	//挪动数据覆盖
	int begin = pos + 1;
	while (begin < ps->size)
	{
		ps->a[begin - 1] = ps->a[begin];
		begin++;
	}
	ps->size--;
}

//查找
int SLFind(SL* ps, SLDataType x)
{
	assert(ps);
	for (int i = 0; i < ps->size; ++i)
	{
		if (ps->a[i] == x)
		{
            //找到
			return i;
		}
	}
    //没找到
	return -1;
}

  3、テスト.c

#define _CRT_SECURE_NO_WARNINGS 1	
#include"SeqList.h"
void menu()
{
	printf("*******************************\n");
	printf("******  0.退出   1.打印  ******\n");
	printf("******  2.尾插   3.尾删  ******\n");
	printf("******  4.头插   5.头删  ******\n");
	printf("******  6.任意插 7.任意删******\n");
	printf("******  8.在pos位置插入  ******\n");
	printf("******  9.删除pos元素值  ******\n");
	printf("*******************************\n");
}

int main()
{
	int input = 1;
	SL s;
	SLInit(&s);	//创建一个顺序表
	while (input)
	{
		int pos = 0;
		SLDatatype x = 0;
		SLDatatype y = 0;
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 0:
			printf("退出顺序表!\n");
			SLDestroy(&s);
			break;
		case 1:
			SLPrint(&s);
			break;
		case 2:
			printf("请输入一个值:>");
			scanf("%d", &x);
			SLPushBack(&s, x);
			break;
		case 3:
			SLPopBack(&s);
			break;
		case 4:
			printf("请输入一个值:>");
			scanf("%d", &x);
			SLPushFront(&s, x);
			break;
		case 5:
			SLPopFront(&s);
			break;
		case 6:
			printf("请输入下标和目标值:>");
			scanf("%d %d", &pos, &x);
			SLInsert(&s, pos, x);
			break;
		case 7:
			printf("请输入下标:>");
			scanf("%d", &pos);
			SLErase(&s, pos);
			break;
		case 8:
			printf("请输入要插入元素值和目标值:>");
			scanf("%d %d", &y, &x);
			SLInsert(&s, SeqListFind(&s, y), x);
			break;
		case 9:
			printf("请输入要删除元素值:>");
			scanf("%d", &y);
			SLErase(&s, SeqListFind(&s, y));
			break;
		default:
			printf("选择错误,请重新选择!\n");
			break;
		}
	}
	return 0;
}


 この記事に不備がある場合は、以下にコメントしてください。できるだけ早く修正します。

  古いアイアン、好きになることと注目することを忘れないでください!

おすすめ

転載: blog.csdn.net/m0_63198468/article/details/128462011