Data structure: Linear table parsing

Table of contents

1. Sequence table

1.1 Static sequence table

SeqList.h

1.2 Dynamic Sequence Table

1.2.1 Main features

1.2.2 Main Operations

1.2.3 Code implementation

Two, linked list

2.1 Concept

2.2 Main features

2.3 Common types

2.4 Advantages and disadvantages

advantage

shortcoming

2.5 Code implementation

LinkList.h

LinkList.cpp


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

The linear table is a linear structure in terms of logical structure , that is to say, it is a continuous straight line.

However, the physical structure (actual storage) is not necessarily continuous. When the linear table is physically stored, it is usually stored in the form of an array (sequential list) and a linked structure (linked list).

1. 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.

Note: Arrays are used here, and storage cannot be skipped inside, but continuous storage is required.

125efb132d77475882b2a8575fac8588.png

Note : Pay attention to the use of array subscripts (starting from 0), the content in the figure is an example of elements, that is, the stored content!

1.1 Static sequence table

The static sequence table is characterized by a fixed length, once created, its length will not change.

The advantage is that accessing elements is fast, you can directly access elements through indexes, and the time complexity is O(1).

The disadvantage is that the operation of inserting and deleting elements is slow, and elements need to be moved, and the time complexity is O(n).

Note: When using a static sequence table, it is necessary to reasonably estimate the number of elements to avoid overflow or waste too much space.

SeqList.h

#pragma one //预处理指令,用于确保头文件在编译过程中只被包含一次,防止重复定义和潜在的编译错误
constexpr int MAX_SIZE = 100; //声明一个编译时常量用于确定数组长度
typedef int SqDataType;	//便于对数据类型的更改(后续可使用模板优化)

class SeqList{
private:
	SqDataType _data[MAX_SIZE];	//静态数组
   	size_t _size;				//数据元素数量
}

Note: Here, only the static sequence table is defined and not implemented, because it is relatively simple and the frequency of use is low.

1.2 Dynamic Sequence Table

Different from the static sequence table, the length of the dynamic sequence table can be dynamically adjusted at runtime to accommodate operations such as insertion and deletion of elements.

In a dynamic sequence table, dynamic memory allocation is usually used to manage the storage space of the sequence table

1.2.1 Main features

  1. Variable length: The length of the dynamic sequence table can be dynamically expanded or contracted as needed. When the number of elements exceeds the current capacity, the memory can be automatically expanded to accommodate more elements.

  2. Dynamic memory management: use dynamic memory allocation to dynamically manage the storage space of the sequence table. By applying for and releasing memory, the dynamic sequence table can adjust the capacity according to the demand.

  3. Flexibility: Due to the variable length, the dynamic sequence table can flexibly perform operations such as insertion, deletion, and modification without moving other elements.

1.2.2 Main Operations

  1. Create a sequence table: dynamically allocate a certain amount of memory space, and initialize the related attributes of the sequence table.

  2. Insert element: Insert an element at the specified position. If the current capacity is insufficient, the memory space needs to be expanded.

  3. Delete element: Delete the element at the specified position. If the capacity is too large after deletion, the memory space can be shrunk.

  4. Find element: Search according to the index position or element value, and return the corresponding element.

  5. Modify element: Modify the element value at the specified position according to the index position or element value.

  6. Get Length: Returns the number of elements in the sequence table.

  7. Traverse the sequence table: output all elements in the sequence table in order.

1.2.3 Code implementation

SeqList.h

#pragma one
#include<iostream>

typedef int SqDataType;
class SeqList {
private:
    SqDataType* _array;  // 存储数据的数组指针
    size_t _size;    // 当前元素个数
    size_t _capacity;  // 当前容量
public:
    SqList();	//构造函数
    ~SqList();	//析构函数
    void checkCapacity();	//检查容量
    void push_back(SqDataType value);	//尾部插入
    void push_front(SqDataType value);	//头部插入
    void pop_back();	//尾部删除
    void pop_front();	//头部删除
    SqDataType get(size_t index); 	//按索引位置查找
    size_t getSize();	//获取长度
    void print();	//遍历顺序表
}

SeqList.cpp

#include"SeqList.h"

SeqList::SeqList()
{
        _array = nullptr;	//对成员属性进行初始化
        _size = 0;
        _capacity = 0;
}

SeqList::~SeqList()
{
    if(_array != nullptr)
    	delete[] _array;
}

//由于头插尾插都需要检查容量,便提取出此函数用于检查容量
void checkCapacity()
{
    //对初次插入和后续插入进行扩容处理
    //因为初次进入_size和_capacity都为0,判断为满,进行扩容
    if (_size == _capacity) {
        if (_capacity == 0)
            _capacity = 1;
        else
            _capacity *= 2;

        //开辟新空间并进行数据移动
        int* newArray = new SqDataType[_capacity];
        for (int i = 0; i < _size; i++) {
            newArray[i] = _array[i];
        }

        //将旧空间进行释放,避免内存泄漏
        delete[] _array;
        _array = newArray;
    }    
}

//尾部插入数据
void push_back(int value) {
	checkCapacity();

    //此处使用_size++需要读者结合数组下标特性进行理解
    _array[_size++] = value;
}

//尾部删除
void pop_back()
{
    //只有在有容量的情况下才能进行删除
    if (_size > 0)
        _size--;
}

//头部插入
void push_front(SqDataType value)
{
	checkCapacity();
    
    //在头部插入数据需要将后续数据全部向后挪动
    for(size_t index = size; index > 0; index--)
    	_array[index] = _array[index - 1];
    
    //挪动数据后将数据插入头部更新size
    _array[0] = value;
    _size++;
}

//头部删除
void pop_front()
{
    if(_size != 0)
    {
        for(size_t index = 0; index < _size - 1; index++)
            _array[index] = _array[index + 1];
        
        _size--;
    }
}

//按索引查找
SqDataType get(size_t index)
{
    if(index >= 0 && index < _size)
        return _array[index];
    else
        throw std::out_of_range("Index out of range.");
}

//获取容量
size_t getSize()
{
    return _size;
}

//便利打印顺序表
void Print()
{
    for(size_t index = 0; index < _size; index++)
        std::cout << _array[index] << " ";
    std::cout << std::endl;
}

Two, linked list

2.1 Concept

Linked List (Linked List) is a non-continuous and non-sequential storage structure in physical structure. The logical order of data elements is realized through the link order of pointers in the linked list.

A linked list is a common linear data structure that consists of a series of nodes, each node containing a data element and a pointer to the next node.

Adjacent nodes are connected by pointers, rather than being continuously stored in memory like an array.

2.2 Main features

  1. Dynamics: The length of the linked list can grow or shrink dynamically without pre-allocating a fixed memory space.
  2. Flexibility: Since nodes are connected by pointers, nodes can be easily inserted, deleted, and modified without moving other nodes.
  3. Storage efficiency: Compared with arrays, linked lists have lower storage space utilization because each node requires an additional pointer to point to the next node.

2.3 Common types

Singly Linked List: Each node has only one pointer, pointing to the next node. The pointer of the last node is nullptr, indicating the end of the linked list.

deaaa4d23d7640afb0596344fd1db1f4.png

singly linked list
 

Doubly Linked List: Each node has two pointers, one pointing to the previous node and one pointing to the next node. In this way, the linked list can be traversed bidirectionally, but compared with the singly linked list, the doubly linked list requires more memory space to store additional pointers.

5c6bd2b367da4adba8f5aff36bd4c6d2.png

doubly
linked list

2.4 Advantages and disadvantages

advantage

  1. Dynamics: The length of the linked list can grow or shrink dynamically, which is suitable for scenarios that require frequent insertion and deletion of nodes.
  2. Flexibility: The nodes of the linked list can be easily inserted, deleted and modified without moving other nodes, making the operation more flexible.

shortcoming

  1. Low random access efficiency: Elements in the linked list cannot be quickly accessed through indexes like an array. It is necessary to traverse the linked list from the head node, and the time complexity is O(n).
  2. Additional memory overhead: Each node requires additional pointers to connect to other nodes, which adds a certain amount of memory overhead.

2.5 Code implementation

What is implemented here is an entry-level single-linked list without a leading node. If you can understand the code here, subsequent readers can complete the variant of the linked list by themselves. The author will also publish an article to introduce different types of linked lists and implement them with code.

LinkList.h

#ifndef LINKEDLIST_H
#define LINKEDLIST_H

//节点类
class Node {
public:
    int data;
    Node* next;
    //初始化节点
    explicit Node(int value);
};

//链表类
class LinkedList {
private:
    Node* head;

public:
    //初始化链表
    LinkedList();
    //尾插节点
    void push_back(int value);
    //尾删节点
    void pop_back();
    //头插节点
    void push_front(int value);
    //头删节点
    void pop_front();
    //遍历打印顺序表
    void printList();
    //析构函数
    ~LinkedList();
};

#endif

LinkList.cpp


#include "LinkList.h"
#include <iostream>

Node::Node(int value) {
    data = value;
    next = nullptr;
}

LinkedList::LinkedList() {
    head = nullptr;
}

void LinkedList::push_back(int value) {
    Node* newNode = new Node(value);

    //遍历寻求到最后节点,再添加节点即可
    if (head == nullptr) {
        head = newNode;
    } else {
        Node* current = head;
        while (current->next != nullptr) {
            current = current->next;
        }
        current->next = newNode;
    }
}

void LinkedList::push_front(int value) {
    Node* newNode = new Node(value);

    //区别在于是否需要修改头指针
    if (head == nullptr) {
        head = newNode;
    } else {
        newNode->next = head;
        head = newNode;
    }
}

void LinkedList::printList() {
    Node* current = head;
    while (current != nullptr) {
        std::cout << current->data << " ";
        current = current->next;
    }
    std::cout << std::endl;
}

void LinkedList::pop_back() {
    if (head != nullptr) {
        Node* current = head;
        Node* pre = nullptr;

        // 处理只有一个节点的情况
        if (head->next == nullptr) {
            delete head;
            head = nullptr;
        } else {
            // 遍历链表找到最后一个节点
            while (current->next != nullptr) {
                pre = current;
                current = current->next;
            }

            // 删除最后一个节点
            pre->next = nullptr;
            delete current;
        }
    }
}

void LinkedList::pop_front() {
    //无节点不能删除
    //同时需要判断是否只有一个节点,需要特殊处理
    if(head != nullptr)
    {
        if(head->next != nullptr)
        {
            Node* temp = head;
            head = temp->next;
            delete temp;
        }
        else
        {
            Node* temp = head;
            head = nullptr;
            delete temp;
        }
    }
}

LinkedList::~LinkedList() {
    //遍历链表中所有节点,逐个进行释放内存
    Node* current = head;
    Node* pre = nullptr;
    while(current != nullptr)
    {
        pre = current;
        current = current->next;
        delete pre;
    }
}

explain:

Creating a linked list first requires a head pointer, that is, the above-mentioned LinkList class, which internally maintains a Node node pointer, which always points to the head node or nullptr.

Because if you want to modify the linked list, you need to use its address to modify it. After getting the pointer of the head node, the head node also stores the pointer of the next node. By analogy, you can find the entire linked list and perform various operations on it. kind of operation.

Readers can design their own tests based on the above code, experience a simple one-way linked list, and expand functions on this basis.

This is the end of this article, and there will be articles about various variant linked lists in the future!

Guess you like

Origin blog.csdn.net/weixin_57082854/article/details/131676696