Article directory
1. The concept of linear table
Linear table is the most basic, simplest, and most commonly used data structure. A linear list is a type of data structure. A linear list is a finite sequence of n data elements with the same characteristics.
A linear table is logically a linear structure, which is a continuous straight line. However, it is not necessarily linear sequence physically. Linear tables are physically stored, usually in the form of arrays and linked structures.
2. Classification of sequence tables
2.1 Static sequence table
Concept: Use fixed-length arrays to store elements
Disadvantages of the static sequence table: less space is not enough, and too much space is wasted.
//静态顺序表
typedef int SLDataType;
#define N 7
typedef struct SeqList
{
SLDataType a[N]; //定长数组
int size; //有效数据个数
}SL;
2.2 Dynamic sequence table
Use dynamically allocated array storage
//动态顺序表
typedef struct SeqList
{
SLDataType* a; //指向动态数组的指针
int size; //有效数据个数
int capacity; //空间容量
};
3 Implementation of dynamic sequence table
3.0 complete code
The author's comments section contains draft notes and unclear
Seqlist.h
parts
#pragma once //防止头文件被多次包含
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>
//动态顺序表
typedef int SLDataType;
typedef struct SeqList
{
SLDataType* a; //动态指针数组
int size; // 顺序表中有效的个数
int capacity; //顺序表当前的空间大小
}SL; //简化结构体名称
//初始化顺序表与销毁
void SLInit(SL* ps);
void SLDestory(SL* ps);
//头插 尾插 尾删 头删
void SLPushBack(SL* ps, SLDataType x);
void SLPushFront(SL* ps, SLDataType x);
void SLPopBack(SL* ps);
void SLPopFront(SL* ps);
//打印SL
void SLprint(SL* ps);
//判断SL是否为空
bool SLIsEmpty(SL* ps);
//在指定位置之前插入数据
void SLInsert(SL* ps, int pos, SLDataType x);
//删除指定位置的数据
void SLErase(SL* ps, int pos);
//查找数据
bool SLFind(SL* ps, SLDataType x);
SeqList.c
part
#include"SeqList.h"
void SLInit(SL* ps) //传过来顺序表,对它进行初始化
{
ps->a = NULL;
ps->size = ps->capacity = 0;
}
void SLDestory(SL* ps)
{
if(ps->a)
free(ps->a);
ps->a = NULL;
ps->size = ps->capacity = 0;
}
void SLCheckCapacity(SL* ps)
{
if (ps->size == ps->capacity)
{
//空间已经满了,需要扩容
int newCapcity = ps->capacity == 0 ? 2 : 2 * ps->capacity;
SLDataType* tmp = (SLDataType*)realloc(ps->a, newCapcity * sizeof(SLDataType));
if (tmp == NULL)
{
perror("realloc fail.\n");
//return 1;//这个会有弱警告,但不妨碍.warning C4098: “SLCheckCapacity”:“void”函数返回值.因此所以用exit(1);退出
exit(1);
}
ps->a = tmp;
ps->capacity = newCapcity;
}
}
void SLPushBack(SL* ps, SLDataType x)
{
//暴力的方式
assert(ps); //assert(ps != NUll);简化一下,()内放ps就行了
//if (ps == NULL)
//{
// return;
//}
//(1)空间足够,在后面尾插
//(2)空间不够,需要扩容
SLCheckCapacity(ps);
//直接插入数据
ps->a[ps->size++] = x;
}
//头插
void SLPushFront(SL* ps, SLDataType x)
{
assert(ps);
//判断空间是否足够,不够就扩容
SLCheckCapacity(ps);
//数据后移
for (size_t i = ps->size; i > 0; i--)
{
ps->a[i] = ps->a[i - 1];
}
ps->a[0] = x;
ps->size++;
}
//尾删
void SLPopBack(SL* ps)
{
assert(ps);
assert(!SLIsEmpty(ps));//非空
ps->size--;//ps->a[ps->size-1]=0;这样显然没有必要,直接让他有效个数减一就行了,即ps->size--;
}
//头删
void SLPopFront(SL* ps)
{
assert(ps);
assert(!SLIsEmpty(ps));
//让后面的数据往前挪动一位
for (size_t i = 0; i < ps->size-1; i++)
{
ps->a[i] = ps->a[i + 1];
}
ps->size--;
}
//打印
void SLprint(SL* ps)
{
for (size_t i = 0; i < ps->size; i++)
{
printf("%d ", ps->a[i]);
}
printf("\n");
}
//判断SL是否为空
bool SLIsEmpty(SL* ps)
{
assert(ps);
return ps->size == 0;
}
//在指定位置之前插入数据
void SLInsert(SL* ps, int pos, SLDataType x) //对pos限制范围
{
assert(ps);
// 对pos限制范围 边界0是可以的
assert(pos >= 0 && pos<=ps->size);
//扩容
SLCheckCapacity(ps);
//把pos位置及以后的数据往后挪动一位
方法一
//for (size_t i = ps->size; i > pos; i--)
//{
// ps->a[i] = ps->a[i - 1];
//}
方法一
for (size_t i = ps->size-1; i > pos-1; i--)
{
ps->a[i+1] = ps->a[i];
}
ps->a[pos] = x;
ps->size++;
}
//删除指定位置的数据
void SLErase(SL* ps, int pos)
{
assert(ps);
assert(!SLIsEmpty(ps));
assert(pos >= 0 && pos < ps->size);
for (size_t i = pos; i < ps->size-1; i++)
{
//最后一个进来的i是ps->size-2, ps->a[ps->size-2]=ps->a[ps->size-1]
ps->a[i] = ps->a[i + 1];
}
ps->size--;
}
//查找数据
bool SLFind(SL* ps, SLDataType x)
{
assert(ps);
for (size_t i = 0; i < ps->size; i++)
{
if (ps->a[i] == x)
{
//找到了
return true;
}
}
}
test.c
part
#include"SeqList.h"
void SLtest()
{
SL sl;
SLInit(&sl);
//一下顺序表的操作
SLPushBack(&sl, 1);
SLPushBack(&sl, 2);
SLPushBack(&sl, 3);
SLPushBack(&sl, 4);//1,2,3,4
SLprint(&sl);
//头插
SLPushFront(&sl, 5);//5,1,2,3,4
SLPushFront(&sl, 6);//6,5,1,2,3,4
SLPushFront(&sl, 7);//7,6,5,1,2,3,4
SLprint(&sl);
//尾删
SLPopBack(&sl); //7,6,5,1,2,3
SLprint(&sl);
SLPopBack(&sl); //7,6,5,1,2
SLprint(&sl);
//头删
SLPopFront(&sl); //6,5,1,2
SLprint(&sl);
SLPopFront(&sl); //5,1,2
SLprint(&sl);
//SLPopFront(&sl); //1,2
//SLprint(&sl);
//SLPopFront(&sl); //2
//SLprint(&sl);
到这里就没有了
//SLPopFront(&sl); //NULL
//SLprint(&sl);
测试bool SLIsEmpty(SL* ps) 出现报错 assert(!SLIsEmpty(ps));成功
//SLPopFront(&sl); //1,2
//SLprint(&sl); //Assertion failed: !SLIsEmpty(ps),
//在指定位置之前插入数据
//SLInsert(&sl, 100, 12);//报错,范围太大
SLInsert(&sl, 2, 12); //5, 1, 12,2
SLprint(&sl);
SLInsert(&sl, sl.size, 9);//5, 1, 12,2,9
SLprint(&sl);
//删除指定位置的数据 pos指的是索引
//SLErase(&sl, 20);//报错,assert成功
//SLprint(&sl);
SLErase(&sl, 0);//1, 12,2,9
SLprint(&sl);
SLErase(&sl, 0);//12,2,9
SLprint(&sl);
SLErase(&sl, 2);//12,2
SLprint(&sl);
bool RetFind = SLFind(&sl, 2);
if (RetFind)
{
printf("找到了\n");
}
else
printf("没找到\n");
//以上顺序表的操作
SLDestory(&sl);
}
int main()
{
SLtest();
return 0;
}
This is the complete code, let’s briefly analyze it below
3.1 Solution
The underlying structure of the sequence table is an array, which encapsulates the array and implements commonly used interfaces such as addition, deletion, modification, and query.
(1) We put the add, delete, modify, check interface in the header file SeqList.h
, put the source file SeqList.c
to implement the interface function, and test.c
the test code
SeqList.h
refers to the header file we need,
#include<stdio.h>
, #include<assert.h>
, #include<stdlib.h>
, (2) We declare the function #include<stdbool.h>
again , and define the function (3) In the test , we use pointers to call members, so the address is passed in the function declaration, for exampleSeqList.h
void SLInit(SL* ps) ;
SelList.c
void SLInit(SL* ps) { }
test.c
void SLInit(SL* ps);
//动态顺序表
typedef int SLDataType; //SL = struct SeqList 方便使用
typedef struct SeqList
{
SLDataType* a;
int size; // 顺序表中有效的个数
int capacity; //顺序表当前的空间大小
}SL; //简化结构体名称,方便后续书写与修改
3.2 Initialization sequence table and destruction
(1) Initialization sequence table
ps->a=NULL empty the pointer array
ps->size = ps->capacity = 0 valid number, empty space
void SLInit(SL* ps) //传过来顺序表,对它进行初始化
{
ps->a = NULL;
ps->size = ps->capacity = 0;
}
(2) Destruction sequence table
It will be destroyed after initialization. When using
free()
the dynamic array opened by release, first ensure that the current array a is not empty, so aif
judgment is made.
If the space is not released, wild pointers will occur.
void SLDestory(SL* ps)
{
if(ps->a)
free(ps->a);
ps->a = NULL;
ps->size = ps->capacity = 0;
}
3.3 Check expansion
Determine whether the sequence table has enough space. If there is enough space, insert it directly, otherwise expand it. Every time you add data, the space will increase. If you expand it frequently, it will reduce the performance of the program. So some people proposed to use 1.5 times or 2 times the original data. Expansion.
We use realloc to expand the capacity. We can adjust the size and expand the sequence table. The data type is (SLDataType*). The second parameter of realloc issize
, we take the space * data type size, newCapcity * sizeof (SLDataType)
newCapcity to determine whether the capacity is is 0, if it is 0, initialize it to 2, so as to avoid that after 0 is multiplied by 2, it will still be 0.
Determine whether the expansion can be successful, and use tmp to receive the created content. Determine whether tmp is empty, and then you can receive it.ps->a = tmp;ps->capacity = newCapcity;
void SLCheckCapacity(SL* ps)
{
if (ps->size == ps->capacity)
{
//空间已经满了,需要扩容
//三目表达式,如果capacity为0,令它空间为2,不够的话,2倍递增
int newCapcity = ps->capacity == 0 ? 2 : 2 * ps->capacity;
SLDataType* tmp = (SLDataType*)realloc(ps->a, newCapcity * sizeof(SLDataType));
if (tmp == NULL)
{
perror("realloc fail.\n");
exit(1);//给状态码1,退出
}
ps->a = tmp;
ps->capacity = newCapcity;
}
}
3.4 Tail plug and head plug
(1) Tail plug
To determine whether ps is empty, it will be broken if NULL is passed in. We use assert, which is more violent and concise.
if(ps->NULL){ return; } if judgment is not concise.
void SLPushBack(SL* ps, SLDataType x)
{
//暴力的方式
assert(ps); //assert(ps != NUll);简化一下,()内放ps就行了
//扩容
SLCheckCapacity(ps);
//直接插入数据
//ps->a[ps->size] = x;
//size++;
ps->a[ps->size++] = x;//上面两行代码使用后置++,简化
}
(2) Head plug
The for loop implements data shifting starting from subscript 1, inserting subscript 0 into x,
ps->a[0] = x;
remember size++, otherwise the subsequent data will be elements with subscript 1.ps->size++;
void SLPushFront(SL* ps, SLDataType x)
{
assert(ps);
//判断空间是否足够,不够就扩容
SLCheckCapacity(ps);
//数据后移
for (size_t i = ps->size; i > 0; i--)
{
ps->a[i] = ps->a[i - 1];
}
ps->a[0] = x;
ps->size++;
}
3.5 Whether the sequence table is empty
If the sequence table is empty and there is no valid data in the sequence table, then the value of the valid data size is 0, and return ps->size == 0;
'0' or '1' is returned.
bool SLIsEmpty(SL* ps)
{
assert(ps);
return ps->size == 0;
}
3.6 Tail deletion and head deletion
(1) Tail deletion
void SLPopBack(SL* ps)
{
assert(ps);
assert(!SLIsEmpty(ps));//非空
ps->size--;//这样显然没有必要,直接让他有效个数减一就行了 ps->a[ps->size - 1] = 0;
}
(2) Header deletion
Using for direct coverage
void SLPopFront(SL* ps)
{
assert(ps);
assert(!SLIsEmpty(ps));//不能一直删
//让后面的数据往前挪动一位
for (size_t i = 0; i < ps->size-1; i++)//最后一个有效数据的下标是ps->size-1
{
ps->a[i] = ps->a[i + 1];
}
ps->size--;
}
3.7 Print data
This function has been used before. Due to the order of ideas, I wrote the additions and deletions of the beginning and the end in advance. The implementation of this function is also relatively simple, and it can also be viewed through debugging. Therefore, the author puts it later in the order.
void SLprint(SL* ps)
{
for (size_t i = 0; i < ps->size; i++)
{
printf("%d ", ps->a[i]);
}
printf("\n");
}
3.8 Fixed-point insertion and deletion
(1) Fixed-point insertion
void SLInsert(SL* ps, int pos, SLDataType x) //对pos限制范围
{
assert(ps);
// 对pos限制范围 边界0是可以的
assert(pos >= 0 && pos<=ps->size);
//扩容
SLCheckCapacity(ps);
//把pos位置及以后的数据往后挪动一位
方法一
//for (size_t i = ps->size; i > pos; i--)
//{
// ps->a[i] = ps->a[i - 1];
//}
方法二
for (size_t i = ps->size-1; i > pos-1; i--)
{
ps->a[i+1] = ps->a[i];
}
ps->a[pos] = x;
ps->size++;
}
(2) Fixed-point deletion
void SLErase(SL* ps, int pos)
{
assert(ps);
assert(!SLIsEmpty(ps));
assert(pos >= 0 && pos < ps->size);
for (size_t i = pos; i < ps->size-1; i++)
{
//最后一个进来的i是ps->size-2, ps->a[ps->size-2]=ps->a[ps->size-1]
ps->a[i] = ps->a[i + 1];
}
ps->size--;
}
3.9 Find data
bool SLFind(SL* ps, SLDataType x)
{
assert(ps);
for (size_t i = 0; i < ps->size; i++)
{
if (ps->a[i] == x)
{
//找到了
return true;
}
}
}
3.10 Debugging
You must debug it yourself!!!
You must debug it yourself!
You must debug it yourself!!! Debug and print it repeatedly
in the function. In the complete code of directory 3.0, post an alarm picture here, and everyone should debug it.test.c
test.c
I finally learned it. I watched the video three times. The first time I was confused, the second time I understood it, and the third time I wrote notes based on the video.
Then I wrote it again by hand, but there were constant mistakes, so I continued to debug it based on the courseware.