線形テーブルシーケンステーブルの物理/記憶構造
[前に書いてあります] このブログは、2021 Wangdao Data Structure PubMed Review Guide のビデオ コースに従って整理された著者のメモです。それらはすべてコンパイラでテストされており、実行可能です。一部の関数は、教師のプロンプトに従ってコードの堅牢性が向上しているため、安心して使用できます。
ディレクトリナビゲーション (クリックしてジャンプ)
1. シーケンステーブルの関連知識
1.1. シーケンシャルストレージの概念
順次ストレージ:論理的に隣接する要素を物理的に隣接する記憶装置に格納することを指し、要素間の関係は記憶装置の隣接関係に反映されます。
1.2. データ要素のサイズを知る方法
C言語では次のようになりますsizeof(ElemType)
ElemType は、シーケンス テーブルに格納されているデータ要素のタイプです。
例えば:
sizeof(int)=4B;
sizeof(Customer)=8B;//Customer为包含两个整型元素的结构体
2 番目に、シーケンス テーブルの実装: 静的割り当て
2.1、静的割り当て
使用数组的方式来存放顺序表,数组的长度大小一旦确定了之后就不可以改变,这是静态数组的特点。
分配一整片连续的存储空间,大小为MaxSize*sizeof(ElemType)。
#define Maxsize 10 //宏定义一个常量,定义最大长度
typedef struct{
ElemType data[MaxSize]; //定义了一个静态数组,大小为Maxsize,用来存放数据元素
int length; //顺序表的当前长度
}SqList; //Sq表示sequence(顺序、序列)
//自己写代码的时候ElemType是什么类型吧ElemType替换掉就可以了。
2.2. 整数データを格納するシーケンステーブルを静的に確保し、シーケンステーブルを初期化して出力する
#include<stdio.h>
#define Maxsize 10 //定义最大长度
typedef struct {
int data[Maxsize]; //定义一个长度为Maxsize的数组用来存放数据元素
int length; //顺序表的当前长度
}SqList;
//基本操作——初始化一个顺序表
void InitList(SqList &L) {
for (int i = 0;i < Maxsize;i++) {
L.data[i] = 0; //将所有数据元素设置为默认初始值0
}
L.length = 0;
}
void PrintList(SqList L) {
for (int i = 0;i < Maxsize;i++) {
printf("%d ",L.data[i]);
}
printf("\n%d ", L.length);
}
int main() {
SqList L; //声明一个顺序表
InitList(L); //初始化顺序表
PrintList(L);
return 0;
}
印刷内容:
2.3. [注意] data[i]に初期値が代入されていない場合、メモリに残ったダーティデータが出力されます。
テストは以下の通りです。前のコードの初期化関数を次のように変更し、コンパイルしてテストすると、出力は次のようになります。
//基本操作——初始化一个顺序表
void InitList(SqList &L) {
//for (int i = 0;i < Maxsize;i++) {
// L.data[i] = 0; //将所有数据元素设置为默认初始值0
//}
L.length = 0;
}
出力内容の印刷:
[注意] 実際、上記の i<Maxsize で配列にアクセスする方法は不正ですが、最初の要素から最後の要素にアクセスするのではなく、現在格納されている実際の長さの最後の要素にアクセスする必要があるため、i<length でアクセスする必要があります。
2.4、その他の注意事項
1. 给各个数据元素设置默认值的步骤可以省略,因为按正常的访问方式的话,
其实不应该访问超过顺序表实际长度的数据元素。
这种访问方式也不够好,更好的做法是使用基本操作来访问各个数据元素。
3. Q:C语言不是会自动给int型的变量设置初始值为0吗?
A:设置初始值是编译器做的事情,换一个C语言的编译器可能不会帮你做初始化的工作。
所以当我们声明一个顺序表的时候,把它的length值设置为0这一步是必须做的。
4. Q:如果数组存满了怎么办?
A:直接放弃治疗,顺序表的表长一旦确定了就无法更改。(存储空间是静态的)
5. Q:如果刚开始就声明一块很大的存储空间,存在什么问题?
A:很浪费。
3. シーケンステーブルの実装:動的割り当て
3.1、動的割り当て
コードで動的割り当てを定義するためのシーケンス テーブルは次のとおりです。
#define InitSize 10 //定义顺序表的初始长度
typedef struct {
ElemType *data; //指示动态分配数组的指针
int MaxSize; //顺序表的最大容量
int length; //顺序表的当前长度
}SeqList; //顺序表的定义类型(动态分配方式)
3.2. 動的割り当ての鍵: メモリ空間の動的適用と解放 - malloc 関数と free 関数の使用
C 言語には、メモリ空間を動的に適用および解放するための malloc および free 関数が用意されています。malloc と free の 2 つの関数が含まれています。stdlib.hこのヘッダー ファイル内にあります。C++ では、new キーワードと delete キーワードを使用して上記の機能を実現します。
2.1 malloc関数
L.data=(ElemType*)malloc(sizeof(ElemType)*InitSize);
形式: L.data=(要素タイプ*)malloc(sizeof(ElemType)*InitSize);
機能: 連続したメモリ空間全体を割り当てます。次に、この記憶領域の開始アドレスへのポインタを返します。定義したデータ要素タイプへのポインターにキャストする必要があります。data は、この記憶領域の開始アドレスへのポインタです。
malloc 関数のパラメータ: 割り当てる連続メモリ空間の量を指定します。たとえば、上記は sizeof(ElemType) のサイズの InitSize 要素のメモリ空間を割り当てることです。
3.3. 動的割り当てシーケンステーブルのメモリ空間を拡張する
整数データを格納するための数列テーブルを動的に割り当て、数列テーブルを初期化し、数列テーブルのメモリ空間を5つ拡張して出力します。
#include<stdio.h>
#include<stdlib.h>
#define InitSize 10 //定义最大长度
typedef struct {
int *data; //指示动态分配数组的指针
int MaxSize; //顺序表的最大容量
int length; //顺序表的当前长度
}SeqList; //顺序表的定义类型(动态分配方式)
//基本操作——初始化一个顺序表
void InitList(SeqList &L) {
//用malloc函数申请一片连续的存储空间
L.data = (int *)malloc(sizeof(int)*InitSize);
L.length = 0;
L.MaxSize = InitSize;
}
//增加动态数组的长度
void IncreaseSize(SeqList &L,int len) {
int *p = L.data;
L.data = (int *)malloc(sizeof(int)*(L.MaxSize+len));//新申请一篇长度为Maxsize+len的区域
for (int i = 0;i < L.length;i++) {
L.data[i] = p[i];//将数据复制到新区域
}
L.MaxSize = L.MaxSize + len;
free(p); //free(p)用来释放p指向的内存空间,由于p是在函数内的局部指针变量,在函数执行完后,存放指针p的内存空间也会被自动释放。
}
void PrintList(SeqList L) {
for (int i = 0;i < L.MaxSize;i++) {
printf("%d ",L.data[i]);
}
printf("\n%d ", L.length);
}
int main() {
SeqList L; //声明一个顺序表
InitList(L); //初始化顺序表
IncreaseSize(L,5);
PrintList(L);
return 0;
}
VS: メモリ作成プロセスでコンパイルされた出力内容
:
第四に、シーケンステーブルの特徴
- ランダムアクセス: i 番目の要素は O(1) 時間で見つかります (コードは data[i-1] として実装され、静的割り当てと動的割り当ては同じです)。
- 高いストレージ密度: 各ノードはデータ要素のみを格納します (一方、チェーン ストレージは次のデータ要素へのポインタも格納します)。
- 容量を拡張するのは不便です(静的割り当て方法では容量を拡張できません。動的割り当て方法を使用した場合でも、長さを拡張するための時間計算量は比較的高くなります)。
- 挿入と削除の操作は不便で、多数の要素を移動する必要があります。
五、シーケンステーブルの基本操作
5.1、挿入操作(静的割り当て)
//基本操作:插入元素
void ListInsert(SqList &L, int i, int e) {
for (int j = L.length;j >= i;j--) {
L.data[j] = L.data[j - 1];
}
L.data[i - 1] = e;
L.length++;
}
コードの堅牢性を高めるには、変更後に次のようにします。
//基本操作:插入元素
bool ListInsert(SqList &L, int i, int e) {
if(i<1|| i>L.length+1){
//判断i的范围是否有效,无效返回false
return false;
}
if (L.length >= Maxsize) {
//当前存储空间已满,不能插入
return false;
}
for (int j = L.length;j >= i;j--) {
L.data[j] = L.data[j - 1];
}
L.data[i - 1] = e;
L.length++;
return true;
}
時間計算量分析: 最も深いループ L.data[j] = L.data[j - 1];
最良の O(1)、最悪の O(n)、平均 O(n)。
5.2. 削除操作
//基本操作:删除位序为i的元素
bool ListDelete(SqList &L, int i, int &e) {
if (i<1 || i>L.length) {
return false;
}
e = L.data[i - 1];
for (int j = i;j < L.length;j++) {
L.data[j - 1] = L.data[j];
}
L.length--;
return true;
}
時間計算量分析:
最良の O(1)、最悪の O(n)、平均 O(n)。
5.3. ビット単位の検索: 時間計算量 O(1)
//基本操作:按位查找
int GetElem(SqList L,int i) {
return L.data[i - 1];//动态和静态都是用这一条查找
}
5.4. 値による検索: 最高の時間計算量 O(1)、最低の O(n)、平均 O(n)
int LocateElem(SeqList L, int e) {
for (int i = 0;i < L.length;i++) {
if (L.data[i] == e) //基本数据类型如bool、double等都可以直接用==来比较但是结构体不可以,结构体需要写专门的判断语句来逐一判断其中的元素是否相等
return i + 1;
}
return 0;
}
6. テストコード
スペースの都合により、作成者はテスト コードを別のブログ投稿に置き、リンクは[データ構造 (2)] 線形テーブル | シーケンス テーブル | (完全な) テスト コード | C 言語/C++ を使用して、動的および静的シーケンス テーブルの定義、挿入、削除、検索、印刷などの基本操作を実装します。上記のすべての基本操作のテストを含め、すべてが VS コンパイラーでテストおよび実行されています。