コンテンツ
1.シーケンステーブルの概要
- コンセプトと構造
シーケンステーブルは、データ要素が連続した物理アドレスを持つストレージユニットのセグメントに順次格納される線形構造であり、通常は配列ストレージを使用します。アレイでの完全なデータの追加、削除、検索、および変更。
シーケンステーブルは、一般的に次のように分割できます。
1.静的順序テーブル:固定長配列を使用して要素を格納します。
2.動的シーケンステーブル:動的に開発されたアレイストレージを使用します。
- 必要とする:
シーケンステーブルでは、格納するデータが0から始まり、順番に連続して格納される必要があり、中央にスペースを入れることはできません。
- インターフェイスの実装
静的シーケンステーブルは、保存する必要のあるデータの量がわかっている場合にのみ役立ちます。静的シーケンステーブルの固定長配列により、Nが大きくなり、スペースが無駄になり、少ないほど十分ではありません。したがって、実際には動的シーケンステーブルを基本的に使用し、必要に応じてスペースサイズを動的に割り当てますので、以下に動的シーケンステーブルを実装します。
- ノート:
この記事では、SeqList.h、SeqList.c、およびTest.cの3つのフォルダーを作成します。これらは、それぞれ宣言、定義、および実装に使用されます。
テキストは始まります:
2.準備
1.シーケンステーブルを作成します
- SeqList.hファイル:
//创建顺序表 typedef int SLDataType; //确保以后想存其它类型的时候方便改动,本文以存放整型数据为例 typedef struct SeqList { SLDataType* a; //动态开辟数组 int size; //存储顺序表中有效数据个数 int capacity;//存储空间个数-->记录最大容量 }SeqList;
2.初期化シーケンステーブル
- SeqList.hファイル:
//初始化顺序表 void SeqListInit(SeqList* psl);
- SeqList.cファイル:
//初始化通讯录 void SeqListInit(SeqList* psl) { assert(psl); psl->a = NULL; psl->size = 0; psl->capacity = 0; }
3.拡張が必要かどうかを確認します
- SeqList.hファイル:
//检测是否需要扩容 void SeqListCheckCapacity(SeqList* psl);
- SeqList.cファイル:
//检测是否需要扩容 void SeqListCheckCapacity(SeqList* psl) { assert(psl); //如果满了,就要扩容 if (psl->size == psl->capacity) { size_t newCapacity = psl->capacity == 0 ? 4 : psl->capacity * 2; //防止原始capacity的容量本身为0,导致后续扩容仍为0 SLDataType* tmp = realloc(psl->a, sizeof(SLDataType) * newCapacity); if (tmp == NULL) { printf("realloc fail\n"); exit(-1); } else { psl->a = tmp; psl->capacity = (int)newCapacity; } } }
- 知らせ:
reallocのポインタが空の場合、reallocはmallocと同等です。
4.破壊シーケンステーブル
- SeqList.hファイル:
//销毁顺序表 void SeqListDestroy(SeqList* psl);
- SeqList.cファイル:
//销毁顺序表 void SeqListDestroy(SeqList* psl) { assert(psl); free(psl->a); psl->a = NULL; psl->capacity = psl->size = 0; }
5.注文表を印刷する
- 考え:
forループを順番に出力するだけです
- SeqList.hファイル:
//打印顺序表 void SeqListPrint(SeqList* psl);
- SeqList.cファイル:
//打印顺序表 void SeqListPrint(SeqList* psl) { assert(psl); for (int i = 0; i < psl->size; i++) { printf("%d ",psl->a[i]); } printf("\n"); }
3、4つの機能
1.データを追加します
ヘッドプラグ
- 考え:
テールプラグよりも少し複雑です。ヘッドプラグは、最後の番号を後ろに移動してから、前の番号を後ろに移動するというように、最初の位置が空になるまで続きますが、前提は、最初の位置が空になるようにすることです。十分なスペース。拡張するには不十分
- SeqList.hファイル:
//头插 void SeqListPushFront(SeqList* psl, SLDataType x);
- SeqList.cファイル:
//头插 void SeqListPushFront(SeqList* psl, SLDataType x) { assert(psl); SeqListCheckCapacity(psl); //检测容量 int end = psl->size - 1; while (end >= 0) { psl->a[end + 1] = psl->a[end]; end--; } psl->a[0] = x; psl->size++; }
- Test.cファイル:
int main() { SeqList s; SeqListInit(&s); //一定要加上&,因为形参的改变不会影响实参,要传地址 //先尾插4个数字 SeqListPushBack(&s, 1); SeqListPushBack(&s, 2); SeqListPushBack(&s, 3); SeqListPushBack(&s, 4); SeqListPrint(&s); //尾插4次后打印 //头插2个数字 SeqListPushFront(&s, 0); SeqListPushFront(&s, -1); SeqListPrint(&s); //头插2次后打印 return 0; }
- 効果は次のとおりです。
テールプラグ
- 考え:
実際、配列aの添え字サイズは、最後のデータの次の位置です。テール挿入では、ps-> sizeでデータを挿入するだけで済みますが、最初に容量がいっぱいかどうかを確認することが前提です。
- SeqList.hファイル:
//尾插 void SeqListPushBack(SeqList* psl, SLDataType x);
- SeqList.cファイル:
//尾插 void SeqListPushBack(SeqList* psl, SLDataType x) { assert(psl); SeqListCheckCapacity(psl); //检测容量 psl->a[psl->size] = x; psl->size++; }
- Test.cファイル:
int main() { SeqList s; SeqListInit(&s); //一定要加上&,因为形参的改变不会影响实参,要传地址 //尾插5个数字 SeqListPushBack(&s, 1); SeqListPushBack(&s, 2); SeqListPushBack(&s, 3); SeqListPushBack(&s, 4); SeqListPushBack(&s, 5); SeqListPrint(&s); //打印 return 0; }
- 効果は次のとおりです。
下付き文字の挿入を指定する
- 考え:
実際、指定された位置に挿入するという考え方は、上記のテール挿入と非常に似ていますが、指定された挿入位置が有効なデータサイズの範囲内にあることを確認する必要があります。そうしないと、境界を越えてしまいます。シーケンステーブルにはなりません。
- SeqList.hファイル:
//在pos位置插入x void SeqListInsert(SeqList* psl, size_t pos, SLDataType x);
- SeqList.cファイル:
//在pos位置插入x void SeqListInsert(SeqList* psl, size_t pos, SLDataType x) { assert(psl); //暴力检查 /*assert(pos <= psl->size && psl);*/ //温和检查 if (pos > psl->size) { printf("pos 越界:%d\n", (int)pos); return; } SeqListCheckCapacity(psl); //检测容量 int end = psl->size - 1; while (end >= (int)pos) { psl->a[end + 1] = psl->a[end]; end--; } psl->a[pos] = x; psl->size++; }
- Test.cファイル:
int main() { SeqList s; SeqListInit(&s); //先尾插4个数字 SeqListPushBack(&s, 1); SeqListPushBack(&s, 2); SeqListPushBack(&s, 3); SeqListPushBack(&s, 4); SeqListPrint(&s); //尾插4次后打印 //指定下标插入2个数字 SeqListInsert(&s, 10, 100); SeqListInsert(&s, 1, 20); SeqListPrint(&s); //插入成功后打印 SeqListDestroy(&s); return 0; }
- 効果は次のとおりです。
- 知らせ:
指定された添え字挿入を実装すると、pos = sizeの場合、実装はテール挿入であることがわかります。
//尾插 void SeqListPushBack(SeqList* psl, SLDataType x) { assert(psl); //法一: /*SeqListCheckCapacity(psl); //检测容量 psl->a[psl->size] = x; psl->size++;*/ //法二: SeqListInsert(psl, psl->size, x); }
同様に、pos = 0の場合、ヘッダーは次のように実装されます。
//头插 void SeqListPushFront(SeqList* psl, SLDataType x) { assert(psl); //法一: /*SeqListCheckCapacity(psl); //检测容量 int end = psl->size - 1; while (end >= 0) { psl->a[end + 1] = psl->a[end]; end--; } psl->a[0] = x; psl->size++;*/ //法二: SeqListInsert(psl, 0, x); }
2.データを削除します
ヘッダーの削除
- 考え:
ヘッドの削除では拡張を考慮する必要はありません。ヘッドの削除では、2番目の数値を前方に移動してから、次の数値を前方に移動する必要があります。データを削除するときは、有効なデータサイズが一定>=0であることを確認してください。後続のデータは正常に追加できます。
- SeqList.hファイル:
//头删 void SeqListPopFront(SeqList* psl);
- SeqList.cファイル:
//头删 void SeqListPopFront(SeqList* psl) { assert(psl); if (psl->size > 0) { int begin = 1; while (begin < psl->size) { psl->a[begin - 1] = psl->a[begin]; begin++; } psl->size--; } }
- Test.cファイル:
int main() { SeqList s; SeqListInit(&s); //尾插10个数字 for (int i = 1; i <= 10; i++) { SeqListPushBack(&s, i); } SeqListPrint(&s); //尾插10个数字后打印 //头删12次数据 for (int i = 1; i <= 12; i++) { SeqListPopFront(&s); } SeqListPrint(&s); //头删12次后打印 //头插5个数字 for (int i = -5; i <= -1; i++) { SeqListPushFront(&s, i); } SeqListPrint(&s); //头插5次后打印 return 0; }
- 効果は次のとおりです。
尾の削除
- 考え:
ここで、sizeは、作成された配列内の有効なデータの数です。有効なデータの数を-1に設定するだけで済みます。印刷時に、元の最後のデータは自然に削除されますが、削除の数が削除された場合は注意が必要です。データサイズが負になる可能性があります。この現象を回避するには、size> 0の場合にのみデクリメントする必要があります。同様に、nullポインタの着信を防ぐには、アサートアサーションが必要です。
- SeqList.hファイル:
//尾删 void SeqListPopBack(SeqList* psl);
- SeqList.cファイル:
//尾删 void SeqListPopBack(SeqList* psl) { assert(psl); if (psl->size > 0) { psl->size--; } }
- Test.cファイル:
int main() { SeqList s; SeqListInit(&s); //一定要加上&,因为形参的改变不会影响实参,要传地址 //尾插5个数字 SeqListPushBack(&s, 1); SeqListPushBack(&s, 2); SeqListPushBack(&s, 3); SeqListPushBack(&s, 4); SeqListPushBack(&s, 5); SeqListPrint(&s); //尾插5次后打印 //尾删6个数字 SeqListPopBack(&s); SeqListPopBack(&s); SeqListPopBack(&s); SeqListPopBack(&s); SeqListPopBack(&s); SeqListPopBack(&s); SeqListPopBack(&s); SeqListPrint(&s); //尾删6次后打印 //再尾插2个数字 SeqListPushBack(&s, 6); SeqListPushBack(&s, 7); SeqListPrint(&s); //再尾插2次打印 return 0; }
- 効果は次のとおりです。
下付き文字の削除を指定
- 考え:
実際、考え方は複雑ではありません。まず、それがnullポインターではないことを表明する必要があります。次に、添え字サイズの値が原因で、添え字pos<sizeが=sizeにならないようにする必要があります。が空で、有効なデータではありません。= sizeの場合、意味のない数値を削除します。次の手順は、頭の削除と同様で、posの最後の位置を前の位置に移動してから、次の位置に置き換えます。
- SeqList.hファイル:
//删除pos位置的数据 void SeqListErase(SeqList* psl, size_t pos);
- SeqList.cファイル:
//删除pos位置的数据 void SeqListErase(SeqList* psl, size_t pos) { assert(psl); assert(pos < psl->size); size_t begin = pos + 1; while (begin < psl->size) { psl->a[begin - 1] = psl->a[begin]; ++begin; } psl->size--; }
- Test.cファイル:
int main() { SeqList s; SeqListInit(&s); //先尾插4个数字 SeqListPushBack(&s, 1); SeqListPushBack(&s, 2); SeqListPushBack(&s, 3); SeqListPushBack(&s, 4); SeqListPrint(&s); //尾插4次后打印 //删除2个指定下标的数字 SeqListErase(&s, 3);//下标3 SeqListErase(&s, 1);//下标1 SeqListPrint(&s); //删除后打印 return 0; }
- 効果は次のとおりです。
- 知らせ:
pos = size-1の場合、最後の番号が削除され、テール削除が実装されるため、テール削除は次のように記述できます。
//尾删 void SeqListPopBack(SeqList* psl) { assert(psl); /*if (psl->size > 0) { psl->size--; }*/ SeqListErase(psl, psl->size - 1); }
pos = 0の場合、最初の番号が削除され、ヘッダーが削除されるため、ヘッダーの削除もこの方法で解決できます。
//头删 void SeqListPopFront(SeqList* psl) { assert(psl); //法一: /*if (psl->size > 0) { int begin = 1; while (begin < psl->size) { psl->a[begin - 1] = psl->a[begin]; begin++; } psl->size--; }*/ //法二:指定下标删除法 SeqListErase(psl, 0); }
3.データを検索する
- 考え:
アレイを繰り返し処理します。
- SeqList.hファイル:
//查找指定数字 int SeqListFind(SeqList* psl, SLDataType x);
- SeqList.cファイル:
//查找指定数字 int SeqListFind(SeqList* psl, SLDataType x) { assert(psl); for (int i = 0; i < psl->size; i++) { if (psl->a[i] == x) { return i; } } return -1; }
- Test.cファイル:
int main() { SeqList s; SeqListInit(&s); //先尾插4个数字 SeqListPushBack(&s, 1); SeqListPushBack(&s, 2); SeqListPushBack(&s, 3); SeqListPushBack(&s, 4); SeqListPrint(&s); //尾插4次后打印 int pos = SeqListFind(&s, 3); if (pos != -1) printf("找到了,下标是:%d", pos); else printf("找不到\n"); return 0; }
- 効果は次のとおりです。
4.データを変更します
- 考え:
変更された番号が有効なデータである場合は、指定された添え字の番号のみを変更する必要があります
- SeqList.hファイル:
//修改指定下标数字 void SeqListModify(SeqList* psl, size_t pos, SLDataType x);
- SeqList.cファイル:
//修改指定下标数字 void SeqListModify(SeqList* psl, size_t pos, SLDataType x) { assert(psl); assert(pos < psl->size); psl->a[pos] = x; }
- Test.cファイル:
int main() { SeqList s; SeqListInit(&s); //先尾插4个数字 SeqListPushBack(&s, 1); SeqListPushBack(&s, 2); SeqListPushBack(&s, 3); SeqListPushBack(&s, 4); SeqListPrint(&s); //尾插4次后打印 SeqListModify(&s, 1, 5); SeqListPrint(&s); //修改后打印 int pos = SeqListFind(&s, 3); if (pos != -1) { SeqListModify(&s, pos, 5000); SeqListPrint(&s); //查找再修改后打印 } return 0; }
- 効果は次のとおりです。
第四に、トータルコード
1、SeqList.h
#pragma once #include<stdio.h> #include<stdlib.h> #include<assert.h> //创建顺序表 typedef int SLDataType; //确保以后想存其它类型的时候方便改动,本文以存放整型数据为例 typedef struct SeqList { SLDataType* a; //动态开辟数组 int size; //存储顺序表中有效数据个数 int capacity;//存储空间个数-->记录最大容量 }SeqList; //初始化顺序表 void SeqListInit(SeqList* psl); //检测是否需要扩容 void SeqListCheckCapacity(SeqList* psl); //打印顺序表 void SeqListPrint(SeqList* psl); //销毁顺序表 void SeqListDestroy(SeqList* psl); //尾插 void SeqListPushBack(SeqList* psl, SLDataType x); //尾删 void SeqListPopBack(SeqList* psl); //头插 void SeqListPushFront(SeqList* psl, SLDataType x); //头删 void SeqListPopFront(SeqList* psl); //在pos位置插入x void SeqListInsert(SeqList* psl, size_t pos, SLDataType x); //删除pos位置的数据 void SeqListErase(SeqList* psl, size_t pos); //查找指定数字 int SeqListFind(SeqList* psl, SLDataType x); //修改指定下标数字 void SeqListModify(SeqList* psl, size_t pos, SLDataType x);
2、SeqList.c
#define _CRT_SECURE_NO_WARNINGS 1 #include"SeqList.h" //初始化通讯录 void SeqListInit(SeqList* psl) { assert(psl); psl->a = NULL; psl->size = 0; psl->capacity = 0; } //检测是否需要扩容 void SeqListCheckCapacity(SeqList* psl) { assert(psl); //如果满了,就要扩容 if (psl->size == psl->capacity) { size_t newCapacity = psl->capacity == 0 ? 4 : psl->capacity * 2; //防止原始capacity的容量本身为0,导致后续扩容仍为0 SLDataType* tmp = realloc(psl->a, sizeof(SLDataType) * newCapacity); if (tmp == NULL) { printf("realloc fail\n"); exit(-1); } else { psl->a = tmp; psl->capacity = (int)newCapacity; } } } //打印顺序表 void SeqListPrint(SeqList* psl) { assert(psl); for (int i = 0; i < psl->size; i++) { printf("%d ", psl->a[i]); } printf("\n"); } //销毁顺序表 void SeqListDestroy(SeqList* psl) { assert(psl); free(psl->a); psl->a = NULL; psl->capacity = psl->size = 0; } //尾插 void SeqListPushBack(SeqList* psl, SLDataType x) { assert(psl); //法一: /*SeqListCheckCapacity(psl); //检测容量 psl->a[psl->size] = x; psl->size++;*/ //法二:指定下标插入 SeqListInsert(psl, psl->size, x); } //尾删 void SeqListPopBack(SeqList* psl) { assert(psl); /*if (psl->size > 0) { psl->size--; }*/ SeqListErase(psl, psl->size - 1); } //头插 void SeqListPushFront(SeqList* psl, SLDataType x) { assert(psl); //法一: /*SeqListCheckCapacity(psl); //检测容量 int end = psl->size - 1; while (end >= 0) { psl->a[end + 1] = psl->a[end]; end--; } psl->a[0] = x; psl->size++;*/ //法二:指定下标插入法 SeqListInsert(psl, 0, x); } //头删 void SeqListPopFront(SeqList* psl) { assert(psl); //法一: /*if (psl->size > 0) { int begin = 1; while (begin < psl->size) { psl->a[begin - 1] = psl->a[begin]; begin++; } psl->size--; }*/ //法二:指定下标删除法 SeqListErase(psl, 0); } //在pos位置插入x void SeqListInsert(SeqList* psl, size_t pos, SLDataType x) { assert(psl); //暴力检查 /*assert(pos <= psl->size && psl);*/ //温和检查 if (pos > psl->size) { printf("pos 越界:%d\n", (int)pos); return; } SeqListCheckCapacity(psl); //检测容量 int end = psl->size - 1; while (end >= (int)pos) { psl->a[end + 1] = psl->a[end]; end--; } psl->a[pos] = x; psl->size++; } //删除pos位置的数据 void SeqListErase(SeqList* psl, size_t pos) { assert(psl); assert(pos < psl->size); size_t begin = pos + 1; while (begin < psl->size) { psl->a[begin - 1] = psl->a[begin]; ++begin; } psl->size--; } //查找指定数字 int SeqListFind(SeqList* psl, SLDataType x) { assert(psl); for (int i = 0; i < psl->size; i++) { if (psl->a[i] == x) { return i; } } return -1; } //修改指定下标数字 void SeqListModify(SeqList* psl, size_t pos, SLDataType x) { assert(psl); assert(pos < psl->size); psl->a[pos] = x; }
3、Test.c
#define _CRT_SECURE_NO_WARNINGS 1 #include"SeqList.h" void test1() { SeqList s; SeqListInit(&s); //一定要加上&,因为形参的改变不会影响实参,要传地址 //尾插5个数字 SeqListPushBack(&s, 1); SeqListPushBack(&s, 2); SeqListPushBack(&s, 3); SeqListPushBack(&s, 4); SeqListPushBack(&s, 5); SeqListPrint(&s); //尾插5次后打印 //尾删6个数字 SeqListPopBack(&s); SeqListPopBack(&s); SeqListPopBack(&s); SeqListPopBack(&s); SeqListPopBack(&s); SeqListPopBack(&s); SeqListPopBack(&s); SeqListPrint(&s); //尾删6次后打印 //再尾插2个数字 SeqListPushBack(&s, 6); SeqListPushBack(&s, 7); SeqListPrint(&s); //再尾插2次打印 } void TestSeqList2() { SeqList s; SeqListInit(&s); //一定要加上&,因为形参的改变不会影响实参,要传地址 //先尾插4个数字 SeqListPushBack(&s, 1); SeqListPushBack(&s, 2); SeqListPushBack(&s, 3); SeqListPushBack(&s, 4); SeqListPrint(&s); //尾插4次后打印 //头插2个数字 SeqListPushFront(&s, 0); SeqListPushFront(&s, -1); SeqListPrint(&s); //头插2次后打印 } void TestSeqList3() { SeqList s; SeqListInit(&s); //先尾插4个数字 SeqListPushBack(&s, 1); SeqListPushBack(&s, 2); SeqListPushBack(&s, 3); SeqListPushBack(&s, 4); SeqListPrint(&s); //尾插4次后打印 //头删 SeqListPopFront(&s); SeqListPopFront(&s); SeqListPopFront(&s); SeqListPopFront(&s); SeqListPopFront(&s); SeqListPopFront(&s); SeqListPrint(&s); //头删6次后打印 //头插2个数字 SeqListPushFront(&s, 0); SeqListPushFront(&s, -1); SeqListPrint(&s); //头插2次后打印 } void TestSeqList4() { SeqList s; SeqListInit(&s); //尾插10个数字 for (int i = 1; i <= 10; i++) { SeqListPushBack(&s, i); } SeqListPrint(&s); //尾插10个数字后打印 //头删12次数据 for (int i = 1; i <= 12; i++) { SeqListPopFront(&s); } SeqListPrint(&s); //头删12次后打印 //头插5个数字 for (int i = -5; i <= -1; i++) { SeqListPushFront(&s, i); } SeqListPrint(&s); //头插5次后打印 } void TestSeqList5() { SeqList s; SeqListInit(&s); //先尾插4个数字 SeqListPushBack(&s, 1); SeqListPushBack(&s, 2); SeqListPushBack(&s, 3); SeqListPushBack(&s, 4); SeqListPrint(&s); //尾插4次后打印 //指定下标插入2个数字 SeqListInsert(&s, 10, 100); SeqListInsert(&s, 1, 20); SeqListInsert(&s, 5, 50); SeqListPrint(&s); //指定插入2次后打印 SeqListDestroy(&s); } int main() { SeqList s; SeqListInit(&s); //先尾插4个数字 SeqListPushBack(&s, 1); SeqListPushBack(&s, 2); SeqListPushBack(&s, 3); SeqListPushBack(&s, 4); SeqListPrint(&s); //尾插4次后打印 SeqListModify(&s, 1, 5); SeqListPrint(&s); //修改后打印 int pos = SeqListFind(&s, 3); if (pos != -1) { SeqListModify(&s, pos, 5000); SeqListPrint(&s); //查找再修改后打印 } return 0; }