C language data structure-dynamic sequence table


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;

Insert image description here

2.2 Dynamic sequence table

Use dynamically allocated array storage

//动态顺序表
typedef struct SeqList
{
    
    
	SLDataType* a;	//指向动态数组的指针
	int size;		//有效数据个数
	int capacity;	//空间容量
};

Insert image description here

3 Implementation of dynamic sequence table

3.0 complete code

The author's comments section contains draft notes and unclear
Seqlist.hparts

#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.cpart

#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.cpart

#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
Insert image description here

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.cto implement the interface function, and test.cthe test code
SeqList.hrefers 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.hvoid SLInit(SL* ps) ;SelList.cvoid SLInit(SL* ps) { }
test.cvoid SLInit(SL* ps);
Insert image description here

//动态顺序表
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 a ifjudgment 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 is size, 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

Insert image description here

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++;
}

Insert image description here

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;
}

Insert image description here

(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--;
}

Insert image description here

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++;
}

Insert image description here

(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--;
}

Insert image description here

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

Insert image description here

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.

Guess you like

Origin blog.csdn.net/m0_73678713/article/details/134699023