[Data structure] Take you to easily figure out the sequence table (with source code included)

insert image description here

Junxi_'s personal homepage

Be diligent and encourage the years to wait for no one

C/C++ game development


Hello, Mina-san, this is Junxi_, and today we are officially starting to open a new pit! In the next month, I will gradually introduce you to the knowledge of elementary data structures. If you are majoring in computer science, the importance of data structures is self-evident, even in your job interviews in the future. One of the content that interviewers like to investigate most, if you want to have a systematic and in-depth understanding of this part of the content, then don't miss the content of this column! ! I will try my best to get you started with easy-to-understand words!
Well, without further ado, let's start today's study!

foreword

ps: The incomprehensible point in the sequence table should be the use of malloc and realloc. This is the knowledge about dynamic memory management in C language. Our main purpose is to introduce the sequence table, so we won’t make any further descriptions. If you are interested, I will Will also update the blog and post the link here!

1. Linear table

We know that the sequence table is the most basic linear table, so what is a linear table?

1. The concept of linear table

  • A linear list is a finite sequence of n data elements with the same properties. Linear table is a data structure widely used in practice, common linear table: sequential list, linked list, stack, queue, string...
  • A linear table is logically a linear structure, that is to say, a continuous straight line. However, the physical structure is not necessarily continuous. When the linear table is physically stored, it is usually stored in the form of an array and a chain structure.
    insert image description here

2. Sequence table

2.1 Concept and structure

  • 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.
  • Note: What we need to know here is that the data stored in the sequential table is continuous and stored sequentially in the memory, which is the biggest difference from the linked list!

2.2 Classification of sequence table

  • There are generally two forms of
  • 1. Static sequence table: use a fixed-length array to store elements.
// 静态顺序表
#define N 1000
typedef int SLDataType;

struct SeqList
{
    
    
	SLDataType a[N];
	int size;
};
  • The static sequence table is only suitable for scenarios where you know how much data needs to be stored. The fixed-length array of the static sequence table causes N to be fixed, and the space is wasted if it is opened too much, and it is not enough if it is opened too little. Therefore, in reality, the second type of sequence table is basically used - the dynamic sequence table, and the size of the space is dynamically allocated according to the needs.
  • 2. Dynamic sequence table
// 动态顺序表
typedef int SLDataType;//重命名数据类型,方便以后直接修改

typedef struct SeqList
{
    
    
	SLDataType* a;//指向动态开辟的数组
	int size;        // 存储有效数据个数
	int capacity;    // 容量空间大小
}SL;
  • 顾名思义,动态顺序表使用动态开辟的数组储存,这样更方便我们实时根据数据的多少调整数组的大小。。
  • Here are a few important points to remind of the above code:
  • 1. We have used two typedefs in the code above, but they have different uses
    • The first typedef is actually for our convenience, because we don’t know what type of data our sequence table is used to store, so we define a SLDataType here, and use it uniformly in the following code Instead, in this way, if we want to change the stored data type in the future, we only need to change here, for example, we want to store double type data now
typedef double SLDataType;
    • The second typedef is to rename this structure to facilitate our subsequent use, and the next structure can directly write 'SL'.

Next, let's implement the various functions and usage methods of the dynamic sequence table


2.3 Realization of dynamic sequence table

Initialization of the sequence table

  • 我们确定了顺序表内的基本内容,我们得把顺序表先初始化一下,不然连空间都还没开辟咋使用呢?
//初始化顺序表
void SLInit(SL* ps)
{
    
    
    ps->a = (SLDataType*)malloc(sizeof(SLDataType) * 4);//申请四个字节的空间,如果不够再进行增容
    if (ps->a == NULL)
    {
    
    
        perror("malloc failed");
        exit (-1);
    }
    ps->size = 0;
    ps->capacity = 4;
}
  • We have applied for four bytes of space for our sequence table through malloc, and we have not yet filled in the data at this time, so the size is initialized to 0, and the capacity space is 4.
  • But we just applied for space, and we don't know whether it is successful, so we judge whether our operation of opening up memory is successful by distinguishing whether the dynamic array is NULL and reporting an error through perror.

Expansion of the sequence table

  • 我们初始化了一个顺序表,但是我们并不知道我们申请的空间是否够用,如果不够用,我们就得扩容一下。因此我们这里的逻辑应该是先判断容量是否够用,够用就不用做多余的操作,如果不够用,就申请扩容
//检查空间,如果顺序表满了,就进行增容操作
void SLCheckCapacity(SL* ps)
{
    
    
    assert(ps);//断言防止传入ps为空
    //判断一下是否空间满了,如果满了就把容量增加到原来的2倍
    if (ps->size == ps->capacity)//有效数据是否占满所有空间
    {
    
    
        SLDataType* tmp = (SLDataType*)realloc(ps->a, ps->capacity * 2 * (sizeof(SLDataType)));//利用realloc扩容
        if (tmp == NULL)
        {
    
    
            perror("realloc failed");
            exit(-1);
        }
        ps->a = tmp;
        ps->capacity *= 2;
    }
}
  • Here we use realloc to expand the space to twice the original size. When we judge the expansion is successful through perror, we will assign the expansion to our dynamic array. Since the expansion is twice the original size, the capacity space here will also become the original 2 times.

Sequence table destruction

  • When we finish using the sequence table, since we use malloc to open up the memory, we must destroy it and release it
void SLDestroy(SL* ps)
{
    
    
    free(ps->a);//释放malloc申请的空间并且将a指向NULL
    ps->a = NULL;
    ps->capacity = ps->size = 0;

}
  • 在销毁并释放内存的同时,我们也需要把size和capacity初始化一下。

Printing of sequence table

  • When we insert or delete data, we need to print the sequence table to see if our operation is successful
void SLPrint(SL* ps)
{
    
    
    int i =0;
    for (i = 0; i < ps->size; i++)
        printf("%d ", ps->a[i]);//把有效数据通过循环都打印一下
    printf("\n");
}
  • If the previous dishes are all appetizers, we will enter today's dinner, which is also the most difficult part of the sequence table, which is what we call additions, deletions, checks, and changes.

Tail insertion and deletion of sequence table

  • What is tail insertion and tail deletion?
  • **所谓尾插就是从顺序表的末尾插入数据,而尾删就是把最后一个数据给删除**
//顺序表尾插
void SLPushBack(SL* ps, SLDataType x)
{
    
    
    assert(ps);
    SLCheckCapacity(ps);//先判断以下顺序表是否已满,未满才能插入数据
    ps->a[ps->size] = x;//把数组a中size位置的数据赋值为x,size位置即最后一位
    ps->size++;//插入数据成功,有效数据+1
}
  • Among them, when tail-deleting, we need to check whether it is out of bounds. For example, if you have only two data and you tail-delete 10 times, then your valid data will become negative at this time. Have you crossed the bounds at this time?
  • There are two ways to check for crossing the line. I would like to call it the soft check and the brute force check. What is going on here?
//顺序表尾删
void SLPopBack(SL* ps)
{
    
    

    assert(ps);
    温柔的检查
    //if (ps->size == 0)
    //    return;
    //暴力检查
    assert(ps->size > 0);
    ps->a[ps->size - 1] = 0;//把最后一个数据抹掉
    ps->size--;//有效数据-1

}
  • What is gentle inspection?
  • When our size has been deleted (size == 0), it means that all valid data has been deleted, and then we can return directly. At this time, the program will neither go down and lead to crossing the boundary, nor will it report an error.
  • And the brute force law is like your strict father
  • We directly assert through assert, if it finds that you have crossed the boundary, it will directly pull out the seven wolves (report an error) and warn you where you are wrong, as shown below
    insert image description here
    . . . Misplaced, as follows:
    insert image description here

Head insertion and head deletion of sequence table

  • 与尾插和尾删类似,顺序表的头插与头删就是在开头进行插入与删除数据操作
/顺序表的头插
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++;

}
  • In the process of head insertion, we need to pay attention to that, to insert a piece of data in the head, we need to move all the data in the sequence table backward one bit to make way for the head insertion.
//顺序表的头删
void SLPopFront(SL* ps)
{
    
    
    assert(ps);
    int begin = 0;
    while (begin < ps->size-1)
    {
    
    
        ps->a[begin] = ps->a[begin + 1];
        begin++;
    }
    ps->size--;
}
  • In header deletion, similar to header insertion, we need to move all data forward by one bit to make up for the vacancy of header deletion, and finally reduce the valid data by one bit.

Deletion and insertion at any position in the sequence table

//在顺序表中pos位置插入
void SLInsert(SL* ps, int pos, SLDataType x)
{
    
    
    assert(ps);
    SLCheckCapacity(ps);//检查容量空间大小
    assert(pos >= 0 && pos <= ps->size);//判断pos的合法化,避免插入到非法坐标中
    int end = ps->size - 1;
    while (end >= pos)//把pos位置后的数据都朝后挪动一位,给插入的数据腾位置
    {
    
    
        ps->a[end + 1] = ps->a[end];
        end--;
    }
    ps->a[pos] = x;//插入数据
    ps->size++;//有效数字+1

}
  • 这里与头插类似,只不过我们需要挪动的数据变成了pos位置后的数据,它们都需要朝后挪动一位
//在顺序表中删除pos位置的值
void SLErase(SL* ps, int pos)
{
    
    
    assert(ps);
    assert(pos >= 0 && pos <= ps->size);
    int begin = pos;
    while (begin < ps->size-1)
    {
    
    
        ps->a[begin] = ps->a[begin + 1];//pos后的数据都朝前挪动一位
        begin++;
    }
    ps->size--;//有效数据-1
}
  • 与头删类似,pos后面的数据都要朝前挪动一位来填补删除pos位置上数据的空缺

Search and modify data in sequence table

  • Lookup of data in sequence table
//查找顺序表中是否有某个数,如果有就返回该数的下标

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;
}
  • Find whether there is a certain data by traversing the form of the array, return the location of the data if it is found, and return -1 if it is not found.
  • Modifications in the sequence table
  • To modify, we need to know which location data to modify and how much we want to modify the data at that location
void SLModify(SL* ps, int pos, SLDataType x)
{
    
    
    assert(ps);

    assert(pos >= 0 && pos < ps->size);//判断要修改数据的坐标是否合法

    ps->a[pos] = x;//修改数据
}


2.4 Test our dynamic sequence table

  • Well, the basic functions of our sequence table have been implemented through code, let's select a few important functions and test them now
int main()
{
    
    
    SL s1;
    SLInit(&s1);
    SLPushBack(&s1, 1);
    SLInsert(&s1, 1, 50);
    SLPushFront(&s1, 20);
    SLPrint(&s1);
  
    int ret=SLFind(&s1, 50);
    if(ret!=-1)
    printf("找到了,下标是%d\n", ret);
    SLErase(&s1, 0);
    SLPopBack(&s1);
   
    SLPrint(&s1);
    SLModify(&s1, 0, 666);
    SLPrint(&s1);

    
    return 0;
}

insert image description here

  • Carefully compare the functions we want to achieve, and find that there is no problem with Queshi

2.5 Advantages and disadvantages of sequence tables

  • Existing problems:
  • 1. Insertion and deletion in the middle/head, the time complexity is O(N)
  • 2. To increase capacity, you need to apply for new space, copy data, and release old space. There will be a lot of consumption. (especially remote expansion)
  • 3. The increase in capacity is generally a 2-fold increase, and there is bound to be a certain amount of space waste.
    For example, the current capacity is 100, and the capacity is increased to 200 when it is full. We continue to insert 5 data, and no data will be inserted later, so 95 data spaces will be wasted
  • advantage
  • 1. Tail deletion and tail insertion are fast enough
  • 2. Random access and modification can be realized through subscripts (this is very important)

Summarize

  • Today's content is over here, and we will take you to realize the writing of the sequence table in detail. Due to space reasons, we will talk about the error-prone points and various details in the compilation of the sequence table in the next article. At the same time, we will also talk about a few related interview questions to deepen everyone's understanding of these error-prone points.
  • ps: In fact, it’s too late to stay up late to write a blog, and the blogger can’t stand it anymore. For the sake of the blogger’s hard work, do you really not want to leave your three companies? ! !
  • Well, if you have any questions, please ask me in the comment area or private message, see you next time!

It is not easy for a new blogger to create. If you feel that the content of the article is helpful to you, you may wish to click on this new blogger before leaving. Your support is my motivation to update! ! !

**(Ke Li asks you to support the blogger three times in a row!!! Click the comment below to like and collect to help Ke Li)**

insert image description here

Guess you like

Origin blog.csdn.net/syf666250/article/details/131950016