目次
1: ソース ファイルとヘッダー ファイルを作成する
ヘッダー ファイル: SeqList.h
ソースファイル: text.c SeqList.c
このうち、ヘッダーファイルは変数の定義、関数の宣言、および必要なヘッダーファイルのインクルードに使用され、SeqList.c はシーケンステーブルの一部の関数の実現 (関数定義) に使用され、text.c は関数のテストに使用されます。
2: シーケンス テーブル構造の宣言と初期化
(1): シーケンステーブル構造宣言
シーケンステーブルは、格納する要素の数に応じてスペース容量を調整できることを願っているため、構造体に格納する必要があるデータ型へのポインターを宣言し、データを記録するために int 型の 2 つの変数を宣言する必要があります。 in memory 有効なデータの数とメモリのサイズ。(ここでのメモリサイズとは、メモリに格納できる最大データ数のことです)
(2): 構造体の初期化
SeqList.h で初期化関数を宣言し、SeqList.c で初期化関数 SeqListInit() を定義します (後続の関数はすべて同じです)。関数のパラメーターは構造体ポインターであり、関数の戻り値は void です。
3: メモリを動的に適用してスペースを解放する
(1): メモリを動的に適用する
後続の head 挿入データと tail 挿入用のメモリ領域を適用するかどうかを判断する必要があることを考慮して、領域を判断して適用する機能を関数 SeqListCheckCapacity() にカプセル化し、関数パラメータは構造体ポインタであり、関数は戻り値は void です。realloc() 関数を使用して、最初のスペース アプリケーションとその後のメモリ拡張を行います。
//判断是否申请内存
void SeqListCheckCapacity(SL* ps)
{
if (ps->size == ps->capacity) //判断数组个数是否和最大容量相同,是就进行扩容
{
//判断是否第一次储存数据,如果是就申请可以储存4个数据的空间
int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
//进行空间扩容,扩大为原先的二倍(注意强制转换realloc()函数的类型)
SeqListDataType* tmp = (SeqListDataType*)realloc(ps->data, newcapacity * sizeof(SeqListDataType));
//如果申请空间不成功,不会释放原数组,会返回一个空地址,我们需要报错并退出程序
if (tmp == NULL)
{
printf("reallco error\n");
exit(-1);
}
//将新的内存大小赋给结构体变量
ps->capacity = newcapacity;
ps->data = tmp;
}
}
(2): 空き容量を増やす
動的に適用したメモリ空間が時間内に解放された場合、後で空間のアドレスを見つけることができず、メモリ リークが発生する可能性があります.このような状況を回避するには、SeqListDestroy() 関数を設計する必要があります。 function パラメーターは構造体ポインターであり、関数の戻り値は void です。
4: 尾の挿入と尾の削除
(1): 尻尾挿入
データを挿入するたびにメモリを適用する関数を呼び出す必要があります. 有効な要素の数がメモリ サイズよりも少なく、メモリ空間が十分にある場合は、新しいデータを (サイズ + 1) 番目の位置に配置します, 対応する要素はサイズとして添字付けされます. 要素数のサイズに1を追加します. 有効な要素の数がメモリのサイズと等しい場合, 容量を拡張し、新しいデータを (サイズ + 1) 番目の位置、対応する要素の添字は size であり、要素の数は size に 1 加算されます。
(2): テール削除
配列の有効要素数の大きさを挿入・検索・出力の基準としており、サイズを1つ増やした位置に要素があればデータが「失われた」とみなすことができるので、疑似削除の効果を得るために、サイズを直接 1 減らすことができます。アサーション関数 assert() を使用します。サイズの値が 0 の場合、つまり配列にデータがない場合、プログラムはエラーを報告し、エラーの原因を表示します。
5: 頭の挿入と頭の削除
(1): 頭の挿入
テール挿入と同様に、各ヘッド挿入の前にメモリ アプリケーション関数を呼び出す必要もありますが、必要なのは最初の位置、つまり添え字 0 に挿入することです。すべてのデータを 1 ビット戻してから挿入する必要があります。データ、サイズに 1 を追加することに注意してください。
(2): 頭を削除
末尾の出力とは異なり、先頭の削除によって最後のデータが「切断」されることはありませんが、最初のデータの後にすべての有効なデータが保持されるため、2 番目のデータから始まる要素を前方に移動して上書きすることができます。最初のデータ、最後のサイズマイナス -1。データの削除に関しては、アサーション関数 assert() も使用する必要があります。サイズの値が 0 の場合、つまり、配列にデータがない場合、プログラムはエラーを報告し、エラーの原因を表示します。 .
6: 印刷して検索する
(1): 印刷
関数 function が実現されているかどうかを確認しやすくするために、印刷関数 SeqListPrintf() を設計します。その関数は、配列内の有効な要素の数をトラバースして出力することです。
(2): 検索
シーケンス テーブルに格納されているデータは順序どおりではないため、配列内の有効な各データをトラバースし、同じ値を見つけて位置を返す必要があります (添字を返すことも可能です)。関数 SeqListFind() を設計します。関数の最初のパラメーターは引き続き構造体へのポインターであり、2 番目のパラメーターはデータ型であり、関数の戻り値は int です。
7:指定挿入・指定削除
(1): 指定挿入
先頭挿入と同様ですが、n 番目の位置とその後のデータを 1 ビットシフトし、新しいデータを n 番目の位置に配置するだけで済みますが、挿入位置が有効なデータの範囲外にならないように注意する必要があります。レンジエリア。
(2): 削除するように指定
先頭の削除と似ていますが、n 番目 + 1 の位置とそれ以降のデータを 1 ビット進めればよいのですが、削除する位置が有効なデータ範囲外の領域になることはありません。
8: メニューと最終コード
(1): メニュー
メニューを設計し、switch() 関数を使用して、ユーザーがこのシーケンス テーブルを使用できるようにします。
//功能选择
int n = 0;
//插入数据
int x = 0;
//插入位置
int k = 0;
SL s1;
//开始进行初始化
SeqListInit(&s1);
while (1)
{
menu();
scanf("%d", &n);
switch (n)
{
case 1: {printf("输入数据: "); scanf("%d", &x); SeqListPushBack(&s1, x); break; }
case 2: {SeqListPopBack(&s1); break; }
case 3: {printf("输入数据: "); scanf("%d", &x); SeqListPushFront(&s1, x); break; }
case 4: {SeqListPopFront(&s1); break; }
case 5: {printf("分别输入位置和数据:"); scanf("%d %d", &k, &x); SeqListInsert(&s1, k, x); break; }
case 6: {printf("输入位置:"); scanf("%d", &k); SeqListErase(&s1, k); break; }
case 7: {printf("输入数据: "); scanf("%d", &x);SeqListFind(&s1, x); break; }
case 8: {SeqListPrintf(&s1); printf("\n"); break; }
case 9: {SeqListDestroy(&s1); SeqListInit(&s1); break; }
}
printf("\n");
}
(2): 最終コード
SeqList.h
#pragma once
//三个必须的头文件
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
//重定义,方便更改数组中存储的元素类型
typedef int SeqListDataType;
//顺序表结构定义
typedef struct SeqList
{
SeqListDataType* data; //储存数据
int size; //数组中的有效元素个数
int capacity; //数组的内存大小
}SL;
//对顺序表初始化
void SeqListInit(SL* ps);
//释放空间
void SeqListDestroy(SL* ps);
//尾部插入
void SeqListPushBack(SL* ps, SeqListDataType x);
//打印
void SeqListPrintf(SL* ps);
//尾部删除
void SeqListPopBack(SL* ps);
//头部插入
void SeqListPushFront(SL* ps,SeqListDataType x);
//申请内存
void SeqListCheckCapacity(SL* ps);
//头部删除
void SeqListPopFront(SL* ps);
//查找元素
int SeqListFind(SL* ps, SeqListDataType x);
//指定插入
void SeqListInsert(SL* ps, int n, SeqListDataType x);
//删除指定位置元素
void SeqListErase(SL* ps, int n);
SeqList.c
#include "SeqList.h"
//对顺序表初始化
void SeqListInit(SL* ps)
{
ps->data = NULL;//初始化指针
ps->size = 0;
ps->capacity = 0;
}
//判断是否申请内存
void SeqListCheckCapacity(SL* ps)
{
if (ps->size == ps->capacity) //判断数组个数是否和最大容量相同,是就进行扩容
{
//判断是否第一次储存数据,如果是就申请可以储存4个数据的空间
int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
//进行空间扩容,扩大为原先的二倍(注意强制转换realloc()函数的类型)
SeqListDataType* tmp = (SeqListDataType*)realloc(ps->data, newcapacity * sizeof(SeqListDataType));
//如果申请空间不成功,不会释放原数组,会返回一个空地址,我们需要报错并退出程序
if (tmp == NULL)
{
printf("reallco error\n");
exit(-1);
}
//将新的内存大小赋给结构体变量
ps->capacity = newcapacity;
ps->data = tmp;
}
}
//释放空间
void SeqListDestroy(SL* ps)
{
//调用free()函数释放空间
free(ps->data);
}
//尾部插入
void SeqListPushBack(SL* ps, SeqListDataType x)
{
//判断是否需要扩容
SeqListCheckCapacity(ps);
//将新数据值放第size个位置
ps->data[ps->size] =x;
//放入数据后size加1
ps->size++;
}
//尾部删除
void SeqListPopBack(SL* ps)
{
assert(ps->size > 0);
ps->size--;
}
//打印
void SeqListPrintf(SL* ps)
{
for (int i = 0; i < ps->size; i++)
{
printf("%d ", ps->data[i]);
}
}
//头部插入
void SeqListPushFront(SL* ps, SeqListDataType x)
{
//判断是否需要扩容
SeqListCheckCapacity(ps);
int tem = ps->size; //保留初始的size
//调用for循环,将所有数据后移一位
for (int i = 0; i < tem; i++)
{
ps->data[ps->size] = ps->data[ps->size-1];
ps->size--;
}
//插入新数据,size加1
ps->data[0] = x;
ps->size = tem + 1;
}
//头部删除
void SeqListPopFront(SL* ps)
{
assert(ps->size > 0);
//把所有数据后移一位
for (int i = 0; i < ps->size; i++)
{
ps->data[i] = ps->data[i + 1];
}
//size减1
ps->size--;
}
//查找
int SeqListFind(SL* ps, SeqListDataType x)
{
//遍历数组,寻找相同值
for (int i = 0; i < ps->size; i++)
{
//查找成功返回位置
if (ps->data[i] == x)
{
printf("数据在位置%d\n", i + 1);
return 0;
}
}
printf("查找失败\n");
}
//指定插入
void SeqListInsert(SL* ps, int n, SeqListDataType x)
{
//判断插入位置是否在有效数据范围内
if (n<1 || n>ps->size)
{
printf("非法输入\n");
return;
}
//判断是否需要扩容
SeqListCheckCapacity(ps);
int tem = ps->size; //记录初始的size
for (int i = n-1; i < ps->size; i++)
{
ps->data[ps->size] = ps->data[ps->size-1];
ps->size--;
}
//修改第n个位置的值
ps->data[n - 1] = x;
//size加1
ps->size = tem + 1;
}
//指定删除
void SeqListErase(SL* ps, int n)
{
if (n<1 || n>ps->size)
{
printf("非法输入\n");
return;
}
for (int i = n - 1; i < ps->size; i++)
{
//如果要删除的元素是最后一个元素,就不进行覆盖,跳出循环。
if (n == ps->size)
break;
ps->data[n-1] = ps->data[n];
n++;
}
//size减1
ps->size--;
}
text.c
#define _CRT_SECURE_NO_WARNINGS
#include "SeqList.h"
void menu()
{
printf("**************************\n");
printf("***1.尾插2.尾删***********\n");
printf("***3.头插4.头删***********\n");
printf("***5.指定插入6.指定删除***\n");
printf("***7.查找8.打印9.清空*****\n");
printf("******************请输入-》");
}
int main()
{
//功能选择
int n = 0;
//插入数据
int x = 0;
//插入位置
int k = 0;
SL s1;
//开始进行初始化
SeqListInit(&s1);
while (1)
{
menu();
scanf("%d", &n);
switch (n)
{
case 1: {printf("输入数据: "); scanf("%d", &x); SeqListPushBack(&s1, x); break; }
case 2: {SeqListPopBack(&s1); break; }
case 3: {printf("输入数据: "); scanf("%d", &x); SeqListPushFront(&s1, x); break; }
case 4: {SeqListPopFront(&s1); break; }
case 5: {printf("分别输入位置和数据:"); scanf("%d %d", &k, &x); SeqListInsert(&s1, k, x); break; }
case 6: {printf("输入位置:"); scanf("%d", &k); SeqListErase(&s1, k); break; }
case 7: {printf("输入数据: "); scanf("%d", &x);SeqListFind(&s1, x); break; }
case 8: {SeqListPrintf(&s1); printf("\n"); break; }
case 9: {SeqListDestroy(&s1); SeqListInit(&s1); break; }
}
printf("\n");
}
//结束及时释放空间
SeqListDestroy(&s1);
return 0;
}