数据结构 - 线性表笔记

This post is not yet available in English. Showing the original version.

February 26, 2025

Table of Contents
Table of Contents

数据结构-线性表笔记

普通的数组与出入栈在STL很好用,但是涉及链表进行指针操作较为薄弱,因此记录一下链表的操作。


链表的创建

单向链表的创建

struct Node // 创建单向链表
{
    int data; //数据
    Node *next; //指向下一元素
};

单向链表的操作的时间复杂度,插入和删除的时间复杂度为O(n)(n为链表长度),查找的时间复杂度为O(n)(n为链表长度)


双向链表的创建

struct Node // 创建双向链表
{
    int data;
    Node *next; // 指向下一元素
    Node *prev;// 指向上一元素
};

双向链表的操作的时间复杂度与单向链表类似,只是在插入和删除时需要更改指针的指向,所以时间复杂度为O(n)(n为链表长度)


块状链表,循环链表的创建

都与基本链表的创建有关。


链表的基本操作

链表的插入

(这里以单向链表为例)

void insert(Node *&head, int value)
{
    Node *newNode = new Node(); // 创建一个新的结点
    newNode->data = value;    // 赋值
    newNode->next = nullptr;  // 初始化next指针
    if (head == nullptr)
    {
        head = newNode;
    } // 如果链表为空,直接插入
    else
    {
        Node *temp = head;
        while (temp->next != nullptr) // 遍历链表,找到最后一个结点
        {
            temp = temp->next;
        }
        temp->next = newNode;
    }
}

逻辑很简单。

链表的结点删除

void deleteData(Node *&head, int value)
{
    Node *temp = head;
    Node *prev = nullptr;
    if (temp != nullptr && temp->data == value)
    {
        head = temp->next;
        delete temp;
        return;
    } // 删除头结点
    while (temp != nullptr && temp->data != value)
    {
        prev = temp;
        temp = temp->next;
    } // 找到要删除的结点,遍历
    if (temp == nullptr)
    {
        return;
    } // 没找到要删除的结点,没有值对应的链表节点
    prev->next = temp->next;
    delete temp;
}

首先最重要的,head的函数引用为&head!因为head很有可能也是删除结点!

否则退出函数后head并没有进行改变操作,将会造成RE即运行时错误。

链表的显示输出

很简单,从头开始while循环输出值即可。

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

也是从头遍历到尾部,时间复杂度为O(n)。

链表的操作实战

实战题目

题目源于洛谷数据结构题单【数据结构1-1】线性表p1160队列安排


非AC代码

采用基础的遍历来解决问题,在数据较小的时候可以满足结果,而数据较大时候因为O(n^2)的时间复杂度而导致TLE,解决方法见注释。

点击查看代码
#include <iostream>
using namespace std;
//面对大数据来写直接插入排序,时间复杂度为O(n^2)。
//插入排序与删除的优点是不需要额外的空间,缺点是查找时间复杂度较高。
//优化的话可以考虑利用哈希表,查找时间复杂度为O(1),空间复杂度为O(n)
//另外一种方法是利用二叉搜索树,查找时间复杂度为O(nlogn),空间复杂度为O(n)
struct node
{
    int id;
    node *prev;
    node *next;
};
void insert(node *&head, int k, int i, bool left);
void deleteValue(node *&head, int id);
void printList(node *head);
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n, m;
    cin >> n;
    node *head = new node();
    head->id = 1;
    head->next = nullptr;
    head->prev = nullptr;
    // 重整顺序
    for (int i = 1; i < n; i++)
    {
        int k, p;
        cin >> k >> p;
        bool left = (p == 0 ? true : false);
        insert(head, k, i, left);
        // printList(head);
    }
    cin >> m;
    for (int i = 0; i < m; i++)
    {
        int id;
        cin >> id;
        deleteValue(head, id);
        // printList(head);
    }
    printList(head);
} // 链表手写数据结构版本
void insert(node *&head, int k, int i, bool left)
{
    node *newNode = new node();
    newNode->id = i + 1;
    newNode->prev = nullptr;
    newNode->next = nullptr;
    node *temp = head;
    if (!left)
    {
        while (temp->id != k)
        {
            temp = temp->next;
        }
        if (temp->next != nullptr)
        {
            temp->next->prev = newNode;
        }
        newNode->next = temp->next;
        temp->next = newNode;
        newNode->prev = temp;
    }
    else
    {
        while (temp->id != k)
        {
            temp = temp->next;
        }
        if (temp->prev == nullptr)
        {
            head = newNode;
        }
        else
        {
            temp->prev->next = newNode;
        }
        newNode->prev = temp->prev;
        temp->prev = newNode;
        newNode->next = temp;
    }
}
void deleteValue(node *&head, int id)
{
    node *temp = head;
    node *prior = nullptr;
    if (temp != nullptr && temp->id == id)
    {
        head = temp->next;
        head->prev = nullptr; // 这里可要可不要
        delete temp;
        return;
    } // 删除头节点
    while (temp != nullptr && temp->id != id)
    {
        prior = temp;
        temp = temp->next;
    }
    if (temp == nullptr)
    {
        return;
    }
    prior->next = temp->next;
    prior->prev = temp->prev;
    if (temp->next != nullptr)
        temp->next->prev = prior;
    delete temp;
}
void printList(node *head)
{
    node *temp = head;
    while (temp != nullptr)
    {
        cout << temp->id << " ";
        temp = temp->next;
    }
    cout << endl;
}

附经典解法哈希+双向链表AC代码。

优化代码,哈希将查找与删除的时间复杂度优化为O(1),用空间换时间

点击查看代码
#include <iostream>
#include <unordered_map>
using namespace std;

struct node {
    int id;
    node *prev;
    node *next;
};

void insert(node *&head, int k_id, int new_id, bool left, unordered_map<int, node*> &nodes) {
    node *target = nodes[k_id];
    node *newNode = new node();
    newNode->id = new_id;
    newNode->prev = nullptr;
    newNode->next = nullptr;
    nodes[new_id] = newNode;

    if (!left) { // 插入到右侧
        newNode->prev = target;
        newNode->next = target->next;
        if (target->next != nullptr) {
            target->next->prev = newNode;
        }
        target->next = newNode;
    } else { // 插入到左侧
        newNode->next = target;
        newNode->prev = target->prev;
        if (target->prev != nullptr) {
            target->prev->next = newNode;
        } else {
            head = newNode; // 更新头节点
        }
        target->prev = newNode;
    }
}

void deleteNode(node *&head, int id, unordered_map<int, node*> &nodes) {
    if (nodes.find(id) == nodes.end()) return;
    node *target = nodes[id];

    // 更新前驱节点
    if (target->prev != nullptr) {
        target->prev->next = target->next;
    } else {
        head = target->next; // 删除的是头节点
    }

    // 更新后继节点
    if (target->next != nullptr) {
        target->next->prev = target->prev;
    }

    nodes.erase(id);
    delete target;
}

void printList(node *head) {
    node *temp = head;
    while (temp != nullptr) {
        cout << temp->id << " ";
        cout << temp->id << " ";
        temp = temp->next;
    }
    cout << endl;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    int n, m;
    cin >> n;
    node *head = new node();
    head->id = 1;
    head->prev = head->next = nullptr;
    unordered_map<int, node*> nodes;
    nodes[1] = head;

    for (int i = 1; i < n; ++i) {
        int k, p;
        cin >> k >> p;
        insert(head, k, i + 1, p == 0, nodes);
    }

    cin >> m;
    while (m--) {
        int id;
        cin >> id;
        deleteNode(head, id, nodes);
    }

    printList(head);

    // 释放内存
    node *current = head;
    while (current) {
        node *next = current->next;
        delete current;
        current = next;
    }

    return 0;
}