Data Structure—Dynamic Order List

First, the concept of the sequence table

*(1)* The sequence table is a linear structure in which data elements are sequentially stored in a segment of storage units with consecutive physical addresses. Generally, array storage is used. Implement various functions on arrays

*(2)* Here we note that the sequence table has the word sequence because the biggest difference between it and the array is that the data elements are stored in sequence (that is, they are stored in sequence).
The sequence table can be divided into static sequence table and dynamic sequence table.

2. Static sequence table (skipable)

We first create an array and use several interface functions to complete the sequence table. The difference from the past is that the naming style of interface functions follows STL. The code is as follows

#define MAX 100
typedef int SLDataType;
typedef struct Seqlist
{
SLDataType a[MAX];
Int size
}SL;

This is the beginning of the creation of static arrays, and the originally complex noun Seqlist is written as SL through typedef (redefinition). At the same time, SLDataType better implements the trouble of replacing variables of other types later. Today, we focus on the realization of dynamic sequence table.

3. The dynamic idea of ​​dynamic sequence table

When the dynamic sequence table and the static sequence table are initially established, we will create a pointer a to point to this array because we need to meet the needs of re-expansion, and create a capacity to indicate how much space the array can actually store.

code show as below

typedef struct Seqlist
{
	SLDataType* a;
	int size;// Indicates how many "pieces" of data are stored in the array
	int capacity; // How much space can the array actually store data?
}SL;

Fourth, the initialization sequence table

When using the sequence table, we generally initialize it first to avoid unnecessary trouble later. code show as below

void SeqlistInit(SL* ps)
{
	ps->a = NULL;
	ps->size = ps->capacity = 0;// initialization
}

Fifth, check the expansion

(1) This is a dynamic sequence table, so we need to expand it when its data is full. This program will also be used in other interface functions later, so we write it out separately. At this point, note that since we have initialized capacity to 0 for the first time, when expanding the capacity again, newcapacity cannot be directly *0. code show as below

void SeqlistCheckCapacity(SL* ps)
{
	if (ps->size == ps->capacity)
	{
		int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;// Expansion
		SLDataType* tmp = (SLDataType*)realloc(ps->a, newcapacity * sizeof(SLDataType));// realloc counts bytes
		if (tmp == NULL)
		{
			printf("realloc fail\n");
			exit(-1);// Here return is not good, exit directly terminates the program
		}

		ps->a = tmp;
		ps->capacity = newcapacity;
	}
}

(2) In this code, we first judge whether the capacity is 0, and assign it a value if it is 0. We assign 4 to it here. We choose to multiply the capacity by 2 times (it doesn't matter how much to multiply by 1.5 times, but 2 times is how to say it, it is very suitable for such a value, if it is smaller, the expansion will be frequent, and if it is expanded, it will cause a waste of space).
Some people will say that there is a problem with ps->a being NULL. In fact, it doesn't matter, don't worry, if the first parameter of the realloc function is empty, the realloc function is the same as malloc.
(3) If the development fails here, we use exit(-1) to end, and many times we write code to use return-1 to terminate. And here, if the development fails, we cannot proceed with the rest of the content, so here we use exit instead of return.

6. Realization of various functions

6.1 Tail insertion (deletion) sequence table

1. Tail insertion function: The first thing we need to do is to check whether the space is full. If it is full, we will expand the capacity. Here we first refer to the previous capacity expansion function.
Insert the X we want to insert into the array. Since size is the number of Yuan Xu, it is also the position of the element after the last element at the tail. So you can directly ++, the code is as follows:

void SeqlistPushBack(SL* ps, SLDataType x) // tail plug
{
	 SeqlistCheckCapacity(ps);
	     ps->a[ps->size] = x;
	     ps->size++; 
}

2. Tail deletion function: After the tail insertion function is successfully implemented, we can implement the tail deletion function. In the tail deletion function, we can change the last number to 0, and then reduce the size. The reduction before or after does not affect the actual result. And there are two methods, the code is as follows:

void SeqlistPopBack(SL* ps)
{
	
	/*  if (ps->size > 0)
	  {
		//ps->a[ps->size - 1] = 0; No problem with this
		ps->size--;
	   }
	*/  // method one

	assert(ps->size > 0); //If it is true, continue, if it is false, that is, <=0, it will terminate the program and indicate that the assertion failed.
	ps->size--;
}

6.2 Header insert (delete) function

1. Header function: As shown in the figure, we need to move the elements in the array to one position in turn, as shown in the figure (forgive me. The drawing is a bit abstract)

Here end points to the position of size-1, size is the number of array elements, minus one is to point to the last position, the code is implemented as follows

void SeqlistPushFront(SL* ps, SLDataType x)
{
	   SeqlistCheckCapacity(ps);
	    // mobile data
	     int end = ps->size - 1;
	     while (end >= 0)
	     {
		    ps->a[end + 1] = ps->a[end];
		    --end; // Front and rear can be
	     }
	     ps->a[0] = x;
	     ps->size++;
    }

2. Header delete function: move the next data forward, the code is as follows:

void SeqlistPopFront(SL* ps)
{
	assert(ps->size > 0);
	int begin = 1;               //begin = 0
	while (begin < ps->size)     // size - 1
	{
		ps->a[begin - 1] = ps->a[begin];  // begin begin + 1
		++begin;
	}
	ps->size--;
	// mobile data
}

6.3 Insert data

The first thing we should consider when inserting data is whether the array space is full and out of bounds. There are two methods as above. We still use the assertion method, as shown in the figure.

code show as below:

void SeqlistInsert(SL* ps, int pos, SLDataType x)
{
	/*   if (pos > ps->size || pos < 0)  out of bounds
	    {
		   printf("pos invalid\n");
		   return;
	    }
	*/      //two methods
	assert(pos >= 0 && pos <= ps -> size);

	SeqlistCheckCapacity(ps); // Insert considering expansion
	int end = ps->size - 1;
	// mobile data
	while (end >= pos)
	{
		ps->a[end + 1] = ps->a[end];
		--end;
	}
	ps->a[pos] = x;
	ps->size++;
}

In fact, we can apply it to the head and tail of your array after we complete the insertion implementation, so we can change the code to this:

void SeqlistPushFront(SL* ps, SLDataType x)
{
	/*    SeqlistCheckCapacity(ps);
	    // mobile data
	     int end = ps->size - 1;
	     while (end >= 0)
	     {
		    ps->a[end + 1] = ps->a[end];
		    --end; // Front and rear can be
	     }
	     ps->a[0] = x;
	     ps->size++;
	*/
	SeqlistInsert(ps, 0, x);
}

Like this, yes, that's right.

6.4 Delete

The idea of ​​deletion is similar to the previous one, as shown in the figure

The implementation of the code is as follows

void SeqlistErase(SL* ps, int pos)
{
	assert(pos >= 0 && pos < ps->size);

	int begin = pos + 1;
	while (begin < ps->size)
	{
		ps->a[begin - 1] = ps->a[begin];
		++begin;    
	}
	ps->size--;
}

6.5 Find

This is relatively easy, let's go directly to the code:

int SeqlistFind(SL* ps, SLDataType x)
{
	for (int i = 0; i < ps->size; i++)
	{
		if (ps->a[i] == x)
		{
			return i;
		}
	}
	return -1;
}

6.6 Printing

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

7. Complete code

In this way, we have basically implemented all the code, the following is the complete code:
test.c

#include "Seqlist.h"

void TestSeqlist1()
{
	SL s1;
	SeqlistInit(&s1);
    SeqlistPushBack(&s1, 1);
	SeqlistPushBack(&s1, 2);
	SeqlistPushBack(&s1, 3);
	SeqlistPushBack(&s1, 4);
	SeqlistPushBack(&s1, 5);
	SeqlistPrint(&s1);

	SeqlistPopBack(&s1);
	SeqlistPopBack(&s1);
	SeqlistPrint(&s1);

	SeqlistDestory(&s1);
}

void TestSeqlist2()
{
	SL s1;
	SeqlistInit(&s1);
	SeqlistPushBack(&s1, 1);
	SeqlistPushBack(&s1, 2);
	SeqlistPushBack(&s1, 3);
	SeqlistPushBack(&s1, 4);
	SeqlistPushBack(&s1, 5);
	SeqlistPrint(&s1);

	SeqlistPushFront(&s1, 10);
	SeqlistPushFront(&s1, 20);
	SeqlistPushFront(&s1, 30);
	SeqlistPrint(&s1);

	SeqlistPopFront(&s1);
	SeqlistPopFront(&s1);
	SeqlistPrint(&s1);

	SeqlistDestory(&s1);
}

void TestSeqlist3()
{
	SL s1;
	SeqlistInit(&s1);
	SeqlistPushBack(&s1, 1);
	SeqlistPushBack(&s1, 2);
	SeqlistPushBack(&s1, 3);
	SeqlistPushBack(&s1, 4);
	SeqlistPushBack(&s1, 5);
	SeqlistPrint(&s1);

	SeqlistInsert(&s1, 2, 30);
	SeqlistPrint(&s1);

	int pos = SeqlistFind(&s1, 4);
	if (pos != -1)
	{
		SeqlistInsert(&s1, pos, 40);
	}
	SeqlistPrint(&s1);

	SeqlistDestory(&s1);
}

void TestSeqlist4()
{
	SL s1;
	SeqlistInit(&s1);
	SeqlistPushBack(&s1, 1);
	SeqlistPushBack(&s1, 2);
	SeqlistPushBack(&s1, 3);
	SeqlistPushBack(&s1, 4);
	SeqlistPushBack(&s1, 5);
	SeqlistPrint(&s1);

	int pos = SeqlistFind(&s1, 3);
	if (pos != -1)
	{
		SeqlistErase(&s1, pos);
	}
	SeqlistPrint(&s1);
	SeqlistDestory(&s1);
}

enum
{
	PushFront = 1,
	PopFront,
	PushBack,
	PopBack,
	Insert,
	Erase,
	Find,
	Print
};

// menu
void Menu()
{
	printf("*********************************\n");
	printf("*****    Please select your action:>   *****\n");
	printf("*****  1,head plug    2. Head delete   *****\n");
	printf("*****  3,tail plug    4. Tail deletion   *****\n");
	printf("*****  5,insert    6. Delete   *****\n");
	printf("*****  7,find    8. Print   *****\n");
	printf("*****      -1,quit         *****\n");
	printf("*********************************\n");
}

int main()
{
	//TestSeqlist1();
	//TestSeqlist2();
	//TestSeqlist3();
	SL s1;
	SeqlistInit(&s1); // initialization
	int input = 0;
	int x;
	int pos = 0;
	while (input != -1)
	{
		Menu();
		scanf("%d", &input);
		switch (input)
		{
		case PushFront:
			printf("Please select the data you want to insert,-1 Finish\n");
			do
			{
				scanf("%d", &x);
				if (x != -1)
				{
					SeqlistPushFront(&s1, x);
				}
			} while (x != -1);
			break;

		case PopFront:
			SeqlistPopFront(&s1);
			break;

		case PushBack:
			printf("Please select the data you want to insert,-1 Finish\n");
			do
			{
				scanf("%d", &x);
				if (x != -1)
				{
					SeqlistPushBack(&s1, x);
				}
			} while (x != -1);
			break;

		case PopBack:
			SeqlistPopBack(&s1);
			break;

		case Insert:
			printf("Please select the data you want to insert and where to insert it,-1 Finish\n");
			do
			{
				scanf("%d %d", &pos, &x);
				if (x != -1)
				{
					SeqlistInsert(&s1, pos, x);
				}
			} while (x != -1);
			break;

		case Erase:
			printf("Please select the data you want to delete,-1 Finish\n");
			do
			{
				scanf("%d", &x);
				if (x != -1)
				{
					SeqlistErase(&s1, x);
				}
			} while (x != -1);
			break;

		case Find:
			printf("Please select the data you are looking for,-1 Finish\n");
			do
			{
				scanf("%d", &x);
				if (x != -1)
				{
					printf("found, subscripted as%d\n", SeqlistFind(&s1, x));
				}
				else
				{
					printf("did not find\n");
				}
			} while (x != -1);
			break;

		case Print:
			SeqlistPrint(&s1);
			break;
		default:
			printf("No such option, please select again\n");
			break;
		}
	}
	SeqlistDestory(&s1);
	TestSeqlist4();
	return 0;
}

SeqList.c

#include "Seqlist.h"

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

void SeqlistInit(SL* ps)
{
	ps->a = NULL;
	ps->size = ps->capacity = 0;// initialization
}

void SeqlistDestory(SL* ps)
{
	free(ps->a);
	ps->a = NULL;
	ps->capacity = ps->size = 0;
}

void SeqlistCheckCapacity(SL* ps)
{
	if (ps->size == ps->capacity)
	{
		int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;// Expansion
		SLDataType* tmp = (SLDataType*)realloc(ps->a, newcapacity * sizeof(SLDataType));// realloc counts bytes
		if (tmp == NULL)
		{
			printf("realloc fail\n");
			exit(-1);// Here return is not good, exit directly terminates the program
		}

		ps->a = tmp;
		ps->capacity = newcapacity;
	}
}

void SeqlistPushBack(SL* ps, SLDataType x) // tail plug
{
	/*   SeqlistCheckCapacity(ps);
	     ps->a[ps->size] = x;
	     ps->size++; 
	*/
	SeqlistInsert(ps, ps->size, x);
}

void SeqlistPopBack(SL* ps)
{
	
	/*  if (ps->size > 0)
	  {
		//ps->a[ps->size - 1] = 0; No problem with this
		ps->size--;
	   }
	*/  // method one

	assert(ps->size > 0); //If it is true, continue, if it is false, that is, <=0, it will terminate the program and indicate that the assertion failed.
	ps->size--;
}

void SeqlistPushFront(SL* ps, SLDataType x)
{
	/*    SeqlistCheckCapacity(ps);
	    // mobile data
	     int end = ps->size - 1;
	     while (end >= 0)
	     {
		    ps->a[end + 1] = ps->a[end];
		    --end; // Front and rear can be
	     }
	     ps->a[0] = x;
	     ps->size++;
	*/
	SeqlistInsert(ps, 0, x);
}

void SeqlistPopFront(SL* ps)
{
	assert(ps->size > 0);
	int begin = 1;               //begin = 0
	while (begin < ps->size)     // size - 1
	{
		ps->a[begin - 1] = ps->a[begin];  // begin begin + 1
		++begin;
	}
	ps->size--;
	// mobile data
}

int SeqlistFind(SL* ps, SLDataType x)
{
	for (int i = 0; i < ps->size; i++)
	{
		if (ps->a[i] == x)
		{
			return i;
		}
	}
	return -1;
}

void SeqlistInsert(SL* ps, int pos, SLDataType x)
{
	/*   if (pos > ps->size || pos < 0)  out of bounds
	    {
		   printf("pos invalid\n");
		   return;
	    }
	*/      //two methods
	assert(pos >= 0 && pos <= ps -> size);

	SeqlistCheckCapacity(ps); // Insert considering expansion
	int end = ps->size - 1;
	// mobile data
	while (end >= pos)
	{
		ps->a[end + 1] = ps->a[end];
		--end;
	}
	ps->a[pos] = x;
	ps->size++;
}

void SeqlistErase(SL* ps, int pos)
{
	assert(pos >= 0 && pos < ps->size);

	int begin = pos + 1;
	while (begin < ps->size)
	{
		ps->a[begin - 1] = ps->a[begin];
		++begin;    
	}
	ps->size--;
}

SeqList.h

#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
//  #define N 1000
typedef int SLDataType; 

// dynamic sequence table
typedef struct Seqlist
{
	SLDataType* a;
	int size;// Indicates how many "pieces" of data are stored in the array
	int capacity; // How much space can the array actually store data?
}SL;

// Static feature: don't allow insertion if full Disadvantage: can't determine how much fits
// If N is too small, it is not enough, and N is wasted.
// 
//Interface functions -- naming style follows STL

void SeqlistPrint(SL* ps);// print order table
void SeqlistInit(SL* ps);// initialization sequence table
void SeqlistDestory(SL* ps);// destroy the sequence table
void SeqlistCheckCapacity(SL* ps);// Check data augmentation
void SeqlistPushBack(SL* ps, SLDataType x); //tail plug
void SeqlistPopBack(SL* ps); //tail deletion
void SeqlistPushFront(SL* ps, SLDataType x); // head plug
void SeqlistPopFront(SL* ps); // header deletion 
int SeqlistFind(SL* ps, SLDataType x);// Find the position of this value, find the return X subscript position, and not return -1
void SeqlistInsert(SL* ps, int pos, SLDataType x);//Insert at the specified pos subscript position
void SeqlistErase(SL* ps, int pos);//Delete data at pos location

8. Summary

The introduction to the sequence table is here. It can be said that although the sequential table has its own advantages, this feature brings trouble to it, such as O(N), the inefficiency in the process of addition and deletion, and so on.

In the future articles, I have also prepared a lot of my own original articles and opinions for you (there are many things I don't understand, please let me know in the comment area). If you are looking forward to it, please vote for me a lot (bushi). If you make a mistake, please give me three clicks if you are looking forward to it.

Tags: C Algorithm data structure

Posted by echoofavalon on Wed, 12 Oct 2022 23:37:50 +0300