【データ構造】 C言語実装順序表(超詳細)

目次

コンセプトと構造

インターフェース関数の実装

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

能力判定

 シーケンステーブルの最後に挿入

 シーケンステーブルの末尾から削除

シーケンスヘッダーの挿入

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

シーケンステーブルのルックアップ

シーケンステーブルの指定位置に挿入

シーケンステーブルの指定位置を削除します。

印刷シーケンステーブル

破壊順序表

シーケンステーブルの完全なコード


コンセプトと構造

シーケンステーブルは線形テーブルの一種で、連続した物理アドレスを持つ記憶装置を使用してデータ要素を順番に格納する線形構造で、一般的には配列記憶装置が使用されます。アレイ上のデータの追加、削除、確認、変更を完了します。

シーケンス テーブルは通常、次のように分割されます。

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

#define N 100
//顺序表的静态存储
typedef int SLDatatype;
typedef struct SeqList
{
	SLDataType Data[N]; //定长数组
	int size;           //有效数据个数
}SeqList;

この格納方法には、データを格納する際に制限があります。格納されるデータの量が非常に少ない場合、配列の長さが長すぎるため、領域の無駄が発生します。格納されるデータの量が非常に多い場合、配列の領域を確保できなくなる可能性があります。十分ですが、この保存方法は拡張できません。したがって、シーケンス テーブルを実装する場合、通常は動的シーケンス テーブルを使用します。これにより、スペースの利用率が向上します。

2. 動的シーケンス テーブル - 動的に開かれた配列を使用して要素を格納します。

//顺序表的动态存储
typedef int SLDatatype;
typedef struct SeqList
{
	SLDataType* Data; //定长数组
	int size;         //有效数据个数
	int capacity;     //空间容量的大小
}SeqList;

インターフェース関数の実装

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

//初始化链表
void SLInit(SeqList* ps)
{
	assert(ps);   //判空,如果传入的空指针,后面对它进行解引用就会报错
	ps->data = NULL; //将data初始化为空指针
	ps->capacity = ps->size = 0;
}

シーケンステーブルの初期化が完了したので、次はシーケンステーブルの追加、削除、確認、変更を行います。

シーケンス リストに要素を追加する機能を実装する前に、まずシーケンス リストがいっぱいかどうかを確認するチェックを実装します。いっぱいの場合は、拡張する必要があります。

能力判定

//容量判断
void Check_Capacity(SeqList* ps)
{
	assert(ps);
	if (ps->capacity == ps->size) //判断顺序表中有效数据个数是否已经达到容量大小
	{
		int new_capacity = ps->capacity == 0 ? 4 : (ps->capacity) * 2;
        //如果容量为0的话,此时就是第一次向顺序表中添加元素,capacity就设为4
        //如果容量不为0,此时就是有效数据个数达到容量,就进行扩容,新容量设置为原容量的2倍
		SLDataType* tmp = (SLDataType*)realloc(ps->data, new_capacity * sizeof(SLDataType));
		if (tmp == NULL)  //如果扩容失败,就终止程序。
		{
			perror("ralloc fail");
			exit(-1);
		}
		ps->data = tmp;
		ps->capacity = new_capacity;
	}
}

ここでは、拡張に realloc 関数を使用します。初めて挿入するとき、ps->data ポインタはまだ null ポインタです。このとき、realloc を使用することもできます。realloc 関数が使用される場合、受信ポインタがは null ポインターであり、その機能は malloc と同じです。 

 シーケンステーブルの最後に挿入

//顺序表尾插
void SL_PushBack(SL* ps, SLDataType x)
{
	assert(ps);
	Check_Capacity(ps);
	ps->data[ps->size] = x;
	ps->size++;
}

 シーケンステーブルの末尾から削除

//顺序表尾删
void SL_PopBack(SL* ps)
{
	assert(ps);
	assert(ps->size > 0); //如果顺序表中已经没有元素,那么就不用进行删除,所以
                          //这里需要检查顺序表中是否还有元素。
	ps->size--;
}

 末尾の削除であっても先頭の削除であっても、シーケンス テーブルをチェックして、テーブル内に要素があるかどうかを確認する必要があります。

シーケンスヘッダーの挿入

//顺序表头插
void SL_PushFront(SL* ps, SLDataType x)
{
	assert(ps);
	Check_Capacity(ps); //检查容量
    //将所有数据向后移动一位
	for (int i = ps->size - 1; i >= 0; i--)
	{
		ps->data[i + 1] = ps->data[i];
	}
	ps->data[0] = x;
	ps->size++;
}

ヘッドを挿入するときは、容量を確認するだけでなく、データを 1 ビット後方に移動してから挿入する必要があります。そうしないと、データが失われます。

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

//顺序表头删
void SL_PopFront(SL* ps)
{
	assert(ps);
    //如果顺序表中只有一个数据,那么直接将数据个数-1
    //如果对数据进行挪动,会造成越界
	if (ps->size == 1)
	{
		ps->size--;
		return;
	}
    如果数据个数不为1,就将数据中第2个到最后一个都往前移动一位
	for (int i = 1; i < ps->size; i++)
	{
		ps->data[i - 1] = ps->data[i];
	}
	ps->size--;
}

シーケンステーブルのルックアップ

//顺序表查找
int SL_Find(SeqList* ps, SLDataType x)
{
	assert(ps);
	for (int i = 0; i < ps->size; i++)
	{
		if (ps->data[i] == x)
		{
            //找到就返回下标
			return i;
		}
	}
    //找不到就返回-1
	return -1;
}

シーケンステーブルの指定位置に挿入

//顺序表指定位置插入
void SL_Insert(SeqList* ps, int pos, SLDataType x)
{
	assert(ps);
    //判定pos是否合法
	assert(pos <= ps->size);
	assert(pos >= 0);
	Check_Capacity(ps); //检查容量是否够用
    //将pos位置后的元素全部都向后移动一位
	for (int i = ps->size-1; i >= pos; i--)
	{
		ps->data[i + 1] = ps->data[i];
	}
	ps->data[pos] = x;
	ps->size++;
}

シーケンステーブルの指定位置を削除します。

//顺序表指定位置删除
void SL_Erase(SeqList* ps, int pos)
{
	assert(ps);
    //检查pos位置是否合法
	assert(pos >= 0);
	assert(pos < ps->size);
    //将pos位置后面的元素全都向前移动一位
	for (int i = pos; i < ps->size; i++)
	{
		ps->data[i] = ps->data[i + 1];
	}
	ps->size--;
}

配列テーブルの指定された位置での挿入と削除により、末尾の挿入と末尾の削除、および先頭のプラグの削除を書き換えることができます。

//尾插
void SL_PushBack(SeqList* ps, SLDataType x)
{
	SL_Insert(ps, ps->size, x);
}

//尾删
void SL_PopBack(SeqList* ps)
{
	SL_Erase(ps, ps->size - 1);
}
//头插
void SL_PushFront(SeqList* ps, SLDataType x)
{
	SL_Insert(ps, 0, x);
}
//头删
void SL_PopFront(SeqList* ps)
{
	SL_Erase(ps,0);
}

印刷シーケンステーブル

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

破壊順序表

//销毁顺序表
void SL_Destroy(SeqList* ps)
{
	assert(ps);
	free(ps->data);
	ps->data = NULL;
	ps->capacity = ps->size = 0;
}

シーケンステーブルの完全なコード

SeqList.h

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

typedef int SLDataType;
typedef struct SeqList
{
	SLDataType* data;
	int size;
	int capacity;
}SeqList;

//初始化顺序表
void SLInit(SeqList* ps);

//删除顺序表
void SL_Destroy(SeqList* ps);

//打印顺序表
void SLPrint(SeqList* ps);

//检查容量
void Check_Capacity(SeqList* ps);

//尾插尾删
void SL_PushBack(SeqList* ps,SLDataType x);
void SL_PopBack(SeqList* ps);

//头插头删
void SL_PushFront(SeqList* ps, SLDataType x);
void SL_PopFront(SeqList* ps);

//顺序表查找
int SL_Find(SeqList* ps, SLDataType x);

//顺序表指定位置插入
void SL_Insert(SeqList* ps, int pos, SLDataType x);

//顺序表指定位置删除
void SL_Erase(SeqList* ps, int pos);

SeqList.c

#include "SeqList.h"

void SLInit(SeqList* ps)
{
	assert(ps);  
	ps->data = NULL; 
	ps->capacity = ps->size = 0;
}

void SL_Destroy(SeqList* ps)
{
	assert(ps);
	free(ps->data);
	ps->data = NULL;
	ps->capacity = ps->size = 0;
}

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

void Check_Capacity(SeqList* ps)
{
	assert(ps);
	if (ps->capacity == ps->size) 
	{
		int new_capacity = ps->capacity == 0 ? 4 : (ps->capacity) * 2;
		SLDataType* tmp = (SLDataType*)realloc(ps->data, new_capacity * sizeof(SLDataType));
		if (tmp == NULL)
		{
			perror("ralloc fail");
			exit(-1);
		}
		ps->data = tmp;
		ps->capacity = new_capacity;
	}
}

void SL_PushBack(SeqList* ps, SLDataType x)
{
	//assert(ps);
	//Check_Capacity(ps);
	//ps->a[ps->size] = x;
	//ps->size++;
	SL_Insert(ps, ps->size, x);
}

void SL_PopBack(SeqList* ps)
{
	//assert(ps);
	//assert(ps->size > 0);
	//ps->size--;
	SL_Erase(ps, ps->size - 1);
}

void SL_PushFront(SeqList* ps, SLDataType x)
{
	//assert(ps);
	//Check_Capacity(ps);
	//for (int i = ps->size - 1; i >= 0; i--)
	//{
	//	ps->a[i + 1] = ps->a[i];
	//}
	//ps->a[0] = x;
	//ps->size++;
	SL_Insert(ps, 0, x);
}

void SL_PopFront(SeqList* ps)
{
	//assert(ps);
	//if (ps->size == 1)
	//{
	//	ps->size--;
	//	return;
	//}
	//for (int i = 1; i < ps->size; i++)
	//{
	//	ps->data[i - 1] = ps->data[i];
	//}
	//ps->size--;
	SL_Erase(ps,0);
}

int SL_Find(SeqList* ps, SLDataType x)
{
	assert(ps);
	for (int i = 0; i < ps->size; i++)
	{
		if (ps->data[i] == x)
		{
			return i;
		}
	}
	return -1;
}

void SL_Insert(SeqList* ps, int pos, SLDataType x)
{
	assert(ps);
	assert(pos <= ps->size);
	assert(pos >= 0);
	Check_Capacity(ps);
	for (int i = ps->size-1; i >= pos; i--)
	{
		ps->data[i + 1] = ps->data[i];
	}
	ps->data[pos] = x;
	ps->size++;
}

void SL_Erase(SeqList* ps, int pos)
{
	assert(ps);
	assert(pos >= 0);
	assert(pos < ps->size);
	for (int i = pos; i < ps->size; i++)
	{
		ps->data[i] = ps->data[i + 1];
	}
	ps->size--;
}

以上が配列表に関する内容ですが、皆様のお役に立てれば幸いです。

おすすめ

転載: blog.csdn.net/m0_74459723/article/details/128512663