Estructura de datos: análisis de tablas lineales

Tabla de contenido

1. Tabla de secuencia

1.1 Tabla de secuencia estática

ListaSeq.h

1.2 Tabla de secuencia dinámica

1.2.1 Características principales

1.2.2 Operaciones principales

1.2.3 Implementación del código

Dos, lista enlazada

2.1 Concepto

2.2 Características principales

2.3 tipos comunes

2.4 Ventajas y desventajas

ventaja

defecto

2.5 Implementación del código

ListaEnlaces.h

Lista de enlaces.cpp


Una lista lineal es una secuencia finita de n elementos de datos con las mismas propiedades. La tabla lineal es una estructura de datos ampliamente utilizada en la práctica, tabla lineal común: lista secuencial, lista vinculada, pila, cola, cadena ...

La tabla lineal es una estructura lineal en términos de estructura lógica , es decir, es una línea recta continua.

Sin embargo, la estructura física (almacenamiento real) no es necesariamente continua. Cuando la tabla lineal se almacena físicamente, generalmente se almacena en forma de matriz (lista secuencial) y estructura vinculada (lista vinculada).

1. Tabla de secuencia

La tabla de secuencia es una estructura lineal en la que los elementos de datos se almacenan secuencialmente en una unidad de almacenamiento con direcciones físicas continuas y, generalmente, se almacenan en una matriz.

Nota: Aquí se utilizan matrices y el almacenamiento no se puede omitir en el interior, pero se requiere un almacenamiento continuo.

125efb132d77475882b2a8575fac8588.png

Nota : Preste atención al uso de subíndices de matriz (a partir de 0), el contenido de la figura es un ejemplo de elementos, es decir, el contenido almacenado.

1.1 Tabla de secuencia estática

La tabla de secuencia estática se caracteriza por una longitud fija; una vez creada, su longitud no cambiará.

La ventaja es que el acceso a los elementos es rápido, puede acceder directamente a los elementos a través de índices y la complejidad del tiempo es O (1).

La desventaja es que la operación de insertar y eliminar elementos es lenta, es necesario mover los elementos y la complejidad del tiempo es O (n).

Nota: Cuando se utiliza una tabla de secuencia estática, es necesario estimar razonablemente la cantidad de elementos para evitar el desbordamiento o el desperdicio de demasiado espacio.

ListaSeq.h

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

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

Nota: Aquí solo se define y no se implementa la tabla de secuencia estática, porque es relativamente simple y la frecuencia de uso es baja.

1.2 Tabla de secuencia dinámica

A diferencia de la tabla de secuencia estática, la longitud de la tabla de secuencia dinámica se puede ajustar dinámicamente en tiempo de ejecución para acomodar operaciones como la inserción y eliminación de elementos.

En una tabla de secuencia dinámica, la asignación de memoria dinámica generalmente se usa para administrar el espacio de almacenamiento de la tabla de secuencia.

1.2.1 Características principales

  1. Longitud variable: la longitud de la tabla de secuencia dinámica se puede expandir o contraer dinámicamente según sea necesario. Cuando el número de elementos supera la capacidad actual, la memoria se puede ampliar automáticamente para dar cabida a más elementos.

  2. Gestión de memoria dinámica: utilice la asignación de memoria dinámica para gestionar dinámicamente el espacio de almacenamiento de la tabla de secuencia. Al solicitar y liberar memoria, la tabla de secuencia dinámica puede ajustar la capacidad según la demanda.

  3. Flexibilidad: debido a la longitud variable, la tabla de secuencia dinámica puede realizar de manera flexible operaciones como inserción, eliminación y modificación sin mover otros elementos.

1.2.2 Operaciones principales

  1. Cree una tabla de secuencia: asigne dinámicamente una cierta cantidad de espacio de memoria e inicialice los atributos relacionados de la tabla de secuencia.

  2. Insertar elemento: inserte un elemento en la posición especificada. Si la capacidad actual es insuficiente, es necesario ampliar el espacio de memoria.

  3. Eliminar elemento: elimina el elemento en la posición especificada. Si la capacidad es demasiado grande después de la eliminación, el espacio de memoria se puede reducir.

  4. Buscar elemento: busque según la posición del índice o el valor del elemento y devuelva el elemento correspondiente.

  5. Modificar elemento: modifica el valor del elemento en la posición especificada de acuerdo con la posición del índice o el valor del elemento.

  6. Obtener longitud: devuelve el número de elementos de la tabla de secuencia.

  7. Recorra la tabla de secuencia: genere todos los elementos de la tabla de secuencia en orden.

1.2.3 Implementación del código

ListaSeq.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();	//遍历顺序表
}

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

Dos, lista enlazada

2.1 Concepto

La lista vinculada (lista vinculada) es una estructura de almacenamiento no continua y no secuencial en una estructura física. El orden lógico de los elementos de datos se realiza a través del orden de vínculo de los punteros en la lista vinculada.

Una lista enlazada es una estructura de datos lineal común que consta de una serie de nodos, cada nodo contiene un elemento de datos y un puntero al siguiente nodo.

Los nodos adyacentes están conectados mediante punteros, en lugar de almacenarse continuamente en la memoria como una matriz.

2.2 Características principales

  1. Dinámica: la longitud de la lista vinculada puede crecer o reducirse dinámicamente sin preasignar un espacio de memoria fijo.
  2. Flexibilidad: dado que los nodos están conectados mediante punteros, los nodos se pueden insertar, eliminar y modificar fácilmente sin mover otros nodos.
  3. Eficiencia del almacenamiento: en comparación con las matrices, las listas vinculadas tienen una menor utilización del espacio de almacenamiento porque cada nodo requiere un puntero adicional para apuntar al siguiente nodo.

2.3 tipos comunes

Lista enlazada individualmente: cada nodo tiene un solo puntero que apunta al siguiente nodo. El puntero del último nodo es nullptr, que indica el final de la lista vinculada.

deaaa4d23d7640afb0596344fd1db1f4.png

lista enlazada individualmente
 

Lista doblemente enlazada: cada nodo tiene dos punteros, uno que apunta al nodo anterior y otro que apunta al siguiente nodo. De esta manera, la lista enlazada se puede recorrer bidireccionalmente, pero en comparación con la lista enlazada individualmente, la lista enlazada doblemente requiere más espacio de memoria para almacenar punteros adicionales.

5c6bd2b367da4adba8f5aff36bd4c6d2.png


lista doblemente enlazada

2.4 Ventajas y desventajas

ventaja

  1. Dinámica: la longitud de la lista vinculada puede crecer o reducirse dinámicamente, lo cual es adecuado para escenarios que requieren la inserción y eliminación frecuentes de nodos.
  2. Flexibilidad: los nodos de la lista vinculada se pueden insertar, eliminar y modificar fácilmente sin mover otros nodos, lo que hace que la operación sea más flexible.

defecto

  1. Baja eficiencia de acceso aleatorio: no se puede acceder rápidamente a los elementos de la lista vinculada a través de índices como una matriz. Es necesario atravesar la lista vinculada desde el nodo principal y la complejidad del tiempo es O (n).
  2. Sobrecarga de memoria adicional: cada nodo requiere punteros adicionales para conectarse a otros nodos, lo que agrega una cierta cantidad de sobrecarga de memoria.

2.5 Implementación del código

Lo que se implementa aquí es una lista de enlace único de nivel de entrada sin un nodo principal. Si puede comprender el código aquí, los lectores posteriores pueden completar la variante de la lista enlazada por sí mismos. El autor también publicará un artículo para presentar diferentes tipos. de listas enlazadas e implementarlas con código.

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

Lista de enlaces.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;
    }
}

explicar:

Crear una lista vinculada primero requiere un puntero principal, es decir, la clase LinkList mencionada anteriormente, que mantiene internamente un puntero de nodo Nodo, que siempre apunta al nodo principal o nullptr.

Porque si desea modificar la lista vinculada, debe usar su dirección para modificarla. Después de obtener el puntero del nodo principal, el nodo principal también almacena el puntero del siguiente nodo. Por analogía, puede encontrar el enlace completo enumerar y realizar varias operaciones en él. tipo de operación.

Los lectores pueden diseñar sus propias pruebas basadas en el código anterior, experimentar una lista vinculada unidireccional simple y expandir funciones sobre esta base.

¡Este es el final de este artículo y habrá artículos sobre varias listas vinculadas variantes en el futuro!

Supongo que te gusta

Origin blog.csdn.net/weixin_57082854/article/details/131676696
Recomendado
Clasificación