[Data structure] Realization of sequence table (linear table)

Table of contents

         1. What is a sequence table?

         Second, the dynamic realization of the sequence table

               1. Sequence table initialization

               2. Sequence table printing

               3. Sequence table check space

               4. Sequence table end insertion

               5. Delete at the end of the sequence table

               6. Sequence header plug

               7. Delete sequence header

               8. Insert at the specified position in the sequence table

               9. Delete the specified position in the sequence table

               10. Sequence table lookup

               11. Sequence table destruction

         3. Source code

               1、SeqList.h

               2、SeqList.c

               3、test.c

 


 

1. What is a sequence table?

The sequence table is a linear structure in which data elements are sequentially stored in a storage unit with continuous physical addresses , and is generally stored in an array. Add, delete, check and modify data on the array.

The sequence table is structured as follows:

 

Sequence tables are generally divided into two types:

1. Static sequence table: use a fixed-length array to store elements.

#define Max 10 
typedef int SLDataType;
typedef struct SeqList
{
	SLDataType data[Max];  //定长数组
	size_t size;  //有效数据的个数
}SeqList;

2. Dynamic sequence table: use dynamically developed array storage.

typedef int SLDataType;
typedef struct SeqList
{
	SLDataType* a;      // 指向动态开辟数组的指针
	size_t size;        // 有效数据个数
	size_t capicity;    // 容量空间的大小
}SeqList;

This article mainly uses the dynamic sequence table , because the static sequence table has set the size of the array space in advance, if the required space size is larger than the preset size, overflow will occur at this time, and it is inconvenient to use, so in A dynamic sequence table is used here to open up as much array space as possible.

Second, the dynamic realization of the sequence table

  1. Sequence table initialization

//初始化
void SLInit(SL* ps)
{
	assert(ps);
	ps->a = NULL;
	ps->size = 0;
	ps->capacity = 0;
}

You can open up space for the sequence table during initialization, and of course you can directly set the pointer a to NULL, and set the size and capacity to 0 as well.

  2. Sequence table printing

//打印
void SLPrint(SL* ps)
{
	assert(ps);
	for (int i = 0; i < ps->size; i++)
	{
		printf("%d ", ps->a[i]);
	}
	printf("\n");
}

  3. Sequence table check space

When inserting and deleting the sequence table, the size of the sequence table space will be involved. When inserting, you must first judge whether the space is full. If it is full, you need to continue to open up space before you can insert.

void SLCheckCapacity(SL* ps)
{
	assert(ps);
	//扩容
	if (ps->size == ps->capacity)
	{
		int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
		SLDataType* tmp = (SLDataType*)realloc(ps->a, newCapacity * sizeof(SLDataType));
		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}
		ps->a = tmp;
		ps->capacity = newCapacity;
	}
}

The reason why the check is encapsulated as a function to check the space here is for the convenience of later use when inserting.

When this sequence table was first created, its capacity was 0. At this time, we will open up space for 4 elements. When the capacity is full, we will directly double the capacity. When we pass in a null pointer in the realloc function, the realloc function at this time is equivalent to a malloc function, so it is also possible to use the realloc function directly here.

  4. Sequence table end insertion

//尾插 O(1)
void SLPushBack(SL* ps, SLDataType x)
{
	assert(ps);
	SLCheckCapacity(ps);
	ps->a[ps->size] = x;
	ps->size++;
}

First of all, we need to check the size of the array space. If the capacity is not enough, we can insert it directly. If the capacity is full, we need to expand the capacity first, and then insert. The function of SLCheckCapacity() is to check whether the capacity is full, and if it is full, the capacity will be expanded immediately.

  

  5. Delete at the end of the sequence table

//尾删
void SLPopBack(SL* ps)
{
	assert(ps);
	///暴力检查
	assert(ps->size > 0);
	ps->size--;
}

Tail deletion actually only uses size--, but one problem to note is that when size = 0, the sequence table itself is empty, so it cannot be reduced any more to prevent out-of-bounds access, so two assertions are added here You can prevent this from happening.

  6. Sequence header plug

//头插 O(N)
void SLPushFront(SL* ps, SLDataType x)
{
	assert(ps);
	//检查容量够不够
	SLCheckCapacity(ps);

	//挪动数据
	int end = ps->size - 1;
	while (end >= 0)
	{
		ps->a[end + 1] = ps->a[end];
		end--;
	}
	ps->a[0] = x;
	ps->size++;
}

First of all, the head plug must also check the size of the capacity to ensure that the capacity is sufficient, and then move the stored data backward by one bit, vacating the first position, and finally insert a new element.

  7. Delete sequence header

//头删
void SLPopFront(SL* ps)
{
	assert(ps);
	assert(ps->size > 0);
	//挪动数据
	int begin = 1;
	while (begin < ps->size)
	{
		ps->a[begin - 1] = ps->a[begin];
		begin++;
	}
	//暴力检查
	ps->size--;
}

The general logic of head deletion and tail deletion is the same. We first need to check whether the size is 0. In the next operation, we only need to let the subsequent data overwrite the previous data, and finally complete the effect of header deletion.

  8. Insert at the specified position in the sequence table 

Inserting at any position is like an upgraded version of the head plug and tail plug, except that there is an additional parameter pos. If pos is at the beginning position, it is equivalent to a head plug. If pos is at the end, it is equivalent to a tail plug.

//在pos的位置插入数据
void SLInsert(SL* ps, int pos, SLDataType x)
{
	assert(ps);
	assert(pos >= 0);
	assert(pos <= ps->size);
	//检查容量够不够
	SLCheckCapacity(ps);
	int end = ps->size - 1;
	while (end >= pos)
	{
		ps->a[end + 1] = ps->a[end];
		end--;
	}
	ps->a[pos] = x;
	ps->size++;
}

Assuming that the subscript of the insertion position is pos, then we only need to move all the data from the pos position to the end backwards as a whole, then empty the position of pos, and then insert a new element at the position of pos, and finally size++.

Note: assert(pos >= 0); assert(pos <= ps->size); These two lines of code are to limit the inserted element to be between 0 and size, not in other places, to ensure that the element is between the elements are stored continuously.

 

  9. Delete the specified position in the sequence table 

//在pos的位置删除数据
void SLErase(SL* ps, int pos)
{
	assert(ps);
	assert(pos >= 0);
	assert(pos < ps->size);
	//挪动数据覆盖
	int begin = pos + 1;
	while (begin < ps->size)
	{
		ps->a[begin - 1] = ps->a[begin];
		begin++;
	}
	ps->size--;
}

First, you need to assert whether the size is 0. Then the data behind pos is moved forward as a whole, covering the data at the original position of pos, and finally size-.

  10. Sequence table lookup

int SLFind(SL* ps, SLDataType x, int begin)
{
	assert(ps);

	for (int i = begin; i < ps->size; ++i)
	{
		if (ps->a[i] == x)
		{
			return i;
		}
	}
	return -1;
}

This function interface is very simple, just traverse the array once, return the subscript of the element if found, and return -1 if not found.

  11. Sequence table destruction

//销毁
void SLDestory(SL* ps)
{
	assert(ps);
	//if (ps->a != NULL)
	if (ps->a)
	{
		free(ps->a);
		ps->a = NULL;
		ps->capacity = 0;
		ps->size = 0;
	}
}

When we started, the dynamically opened space was applied in the heap area, and we must release it after we use it up to prevent memory leaks.

3. Source code

  1、SeqList.h

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

//动态的顺序表 - 按需扩空间
#define N  10
typedef int SLDataType;

typedef struct SeqList
{
	SLDataType* a;
	int size;//记录存储多少个有效数据
	int capacity;//空间容量大小
}SL;

void SLPrint(SL* ps);//打印
void SLInit(SL* ps);//初始化
void SLDestory(SL* ps);//销毁
void SLCheckCapacity(SL* ps);//检查空间大小

//尾插
void SLPushBack(SL* ps, SLDataType x);
//尾删
void SLPopBack(SL* ps);

//头插
void SLPushFront(SL* ps, SLDataType x);
//头删
void SLPopFront(SL* ps);

//中间的插入
//在pos位置插入数据
void SLInsert(SL* ps, int pos, SLDataType x);
//中间的删除
//删除pos位置的数据
void SLErase(SL* ps, int pos);

//查找
int SLFind(SL* ps, SLDataType x);

  2、SeqList.c

#include "SeqList.h"

//打印
void SLPrint(SL* ps)
{
	assert(ps);
	for (int i = 0; i < ps->size; i++)
	{
		printf("%d ", ps->a[i]);
	}
	printf("\n");
}


//初始化
void SLInit(SL* ps)
{
	assert(ps);
	ps->a = NULL;
	ps->size = 0;
	ps->capacity = 0;
}

//销毁
void SLDestory(SL* ps)
{
	assert(ps);
	//if (ps->a != NULL)
	if (ps->a)
	{
		free(ps->a);
		ps->a = NULL;
		ps->capacity = 0;
		ps->size = 0;
	}
}

void SLCheckCapacity(SL* ps)
{
	assert(ps);
	//扩容
	if (ps->size == ps->capacity)
	{
		int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
		SLDataType* tmp = (SLDataType*)realloc(ps->a, newCapacity * sizeof(SLDataType));
		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}
		ps->a = tmp;
		ps->capacity = newCapacity;
	}
}
//尾插 O(1)
void SLPushBack(SL* ps, SLDataType x)
{
	assert(ps);
	SLCheckCapacity(ps);
	ps->a[ps->size] = x;
	ps->size++;
}
//尾删
void SLPopBack(SL* ps)
{
	assert(ps);
	//暴力检查
	assert(ps->size > 0);
	ps->size--;
}

//头插 O(N)
void SLPushFront(SL* ps, SLDataType x)
{
	assert(ps);
	//检查容量够不够
	SLCheckCapacity(ps);

	//挪动数据
	int end = ps->size - 1;
	while (end >= 0)
	{
		ps->a[end + 1] = ps->a[end];
		end--;
	}
	ps->a[0] = x;
	ps->size++;
}
//头删
void SLPopFront(SL* ps)
{
	assert(ps);
	assert(ps->size > 0);
	//挪动数据
	int begin = 1;
	while (begin < ps->size)
	{
    	ps->a[begin - 1] = ps->a[begin];
		begin++;
	}
	//暴力检查
	ps->size--;
}

//在pos的位置插入数据
void SLInsert(SL* ps, int pos, SLDataType x)
{
	assert(ps);
	assert(pos >= 0);
	assert(pos <= ps->size);
	//检查容量够不够
	SLCheckCapacity(ps);
	int end = ps->size - 1;
	while (end >= pos)
	{
		ps->a[end + 1] = ps->a[end];
		end--;
	}
	ps->a[pos] = x;
	ps->size++;
}
//在pos的位置删除数据
void SLErase(SL* ps, int pos)
{
	assert(ps);
	assert(pos >= 0);
	assert(pos < ps->size);
	//挪动数据覆盖
	int begin = pos + 1;
	while (begin < ps->size)
	{
		ps->a[begin - 1] = ps->a[begin];
		begin++;
	}
	ps->size--;
}

//查找
int SLFind(SL* ps, SLDataType x)
{
	assert(ps);
	for (int i = 0; i < ps->size; ++i)
	{
		if (ps->a[i] == x)
		{
            //找到
			return i;
		}
	}
    //没找到
	return -1;
}

  3、test.c

#define _CRT_SECURE_NO_WARNINGS 1	
#include"SeqList.h"
void menu()
{
	printf("*******************************\n");
	printf("******  0.退出   1.打印  ******\n");
	printf("******  2.尾插   3.尾删  ******\n");
	printf("******  4.头插   5.头删  ******\n");
	printf("******  6.任意插 7.任意删******\n");
	printf("******  8.在pos位置插入  ******\n");
	printf("******  9.删除pos元素值  ******\n");
	printf("*******************************\n");
}

int main()
{
	int input = 1;
	SL s;
	SLInit(&s);	//创建一个顺序表
	while (input)
	{
		int pos = 0;
		SLDatatype x = 0;
		SLDatatype y = 0;
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 0:
			printf("退出顺序表!\n");
			SLDestroy(&s);
			break;
		case 1:
			SLPrint(&s);
			break;
		case 2:
			printf("请输入一个值:>");
			scanf("%d", &x);
			SLPushBack(&s, x);
			break;
		case 3:
			SLPopBack(&s);
			break;
		case 4:
			printf("请输入一个值:>");
			scanf("%d", &x);
			SLPushFront(&s, x);
			break;
		case 5:
			SLPopFront(&s);
			break;
		case 6:
			printf("请输入下标和目标值:>");
			scanf("%d %d", &pos, &x);
			SLInsert(&s, pos, x);
			break;
		case 7:
			printf("请输入下标:>");
			scanf("%d", &pos);
			SLErase(&s, pos);
			break;
		case 8:
			printf("请输入要插入元素值和目标值:>");
			scanf("%d %d", &y, &x);
			SLInsert(&s, SeqListFind(&s, y), x);
			break;
		case 9:
			printf("请输入要删除元素值:>");
			scanf("%d", &y);
			SLErase(&s, SeqListFind(&s, y));
			break;
		default:
			printf("选择错误,请重新选择!\n");
			break;
		}
	}
	return 0;
}


 If there are deficiencies in this article, you are welcome to comment below, and I will correct it as soon as possible.

  Old irons, remember to like and pay attention!!!

Guess you like

Origin blog.csdn.net/m0_63198468/article/details/128462011