数据结构与算法-要点整理

知识导图:

请添加图片描述

一、数据结构

    包含:线性表(数组、队列、链表、栈)、散列表、树(二叉树、多路查找树)、图

1.线性表

    数据之间就是“一对一“的逻辑关系。
    线性表存储数据的实现方案有两种,分别是顺序存储结构链式存储结构
    包含:数组、队列、链表、栈。

1.1 数组:

    连续内存存储。简单,此处不多介绍。

1.2 队列:

可以通过数组+前后索引实现,也可以通过链表+前后指针实现。

queue:
1)通过数组+前后索引实现

#include <iostream>
#include <stdexcept>

template <typename T, int capacity>
class Queue {
private:
    T arr[capacity];
    int front;  // 队头索引
    int rear;   // 队尾索引
    int size;   // 当前队列中的元素数量

public:
    Queue() : front(0), rear(0), size(0) {}

    // 判断队列是否为空
    bool isEmpty() const {
        return size == 0;
    }

    // 判断队列是否已满
    bool isFull() const {
        return size == capacity;
    }

    // 入队操作
    void enqueue(const T& value) {
        if (isFull()) {
            throw std::overflow_error("Queue is full");
        }
        arr[rear] = value;
        rear = (rear + 1) % capacity;  // 循环更新队尾索引
        ++size;
    }

    // 出队操作
    T dequeue() {
        if (isEmpty()) {
            throw std::underflow_error("Queue is empty");
        }
        T value = arr[front];
        front = (front + 1) % capacity;  // 循环更新队头索引
        --size;
        return value;
    }

    // 获取队头元素
    T getFront() const {
        if (isEmpty()) {
            throw std::underflow_error("Queue is empty");
        }
        return arr[front];
    }

    // 获取队列中的元素数量
    int getSize() const {
        return size;
    }
};

int main() {
    Queue<int, 5> queue;

    try {
        // 入队操作
        queue.enqueue(1);
        queue.enqueue(2);
        queue.enqueue(3);

        // 输出队头元素
        std::cout << "Front element: " << queue.getFront() << std::endl;

        // 出队操作
        std::cout << "Dequeued element: " << queue.dequeue() << std::endl;

        // 再次输出队头元素
        std::cout << "Front element after dequeue: " << queue.getFront() << std::endl;

        // 输出队列中的元素数量
        std::cout << "Queue size: " << queue.getSize() << std::endl;
    } catch (const std::exception& e) {
        std::cerr << "Exception: " << e.what() << std::endl;
    }

    return 0;
}

2)通过链表+前后指针实现

#include <iostream>
#include <stdexcept>

// 定义链表节点结构体
template <typename T>
struct Node {
    T data;
    Node<T>* next;
    Node(const T& value) : data(value), next(nullptr) {}
};

// 定义队列类
template <typename T>
class Queue {
private:
    Node<T>* front;  // 队头指针
    Node<T>* rear;   // 队尾指针
    int size;        // 当前队列中的元素数量

public:
    // 构造函数
    Queue() : front(nullptr), rear(nullptr), size(0) {}

    // 析构函数
    ~Queue() {
        while (!isEmpty()) {
            dequeue();
        }
    }

    // 判断队列是否为空
    bool isEmpty() const {
        return size == 0;
    }

    // 获取队列中的元素数量
    int getSize() const {
        return size;
    }

    // 入队操作
    void enqueue(const T& value) {
        Node<T>* newNode = new Node<T>(value);
        if (isEmpty()) {
            front = rear = newNode;
        } else {
            rear->next = newNode;
            rear = newNode;
        }
        ++size;
    }

    // 出队操作
    T dequeue() {
        if (isEmpty()) {
            throw std::underflow_error("Queue is empty");
        }
        T value = front->data;
        Node<T>* temp = front;
        front = front->next;
        if (front == nullptr) {
            rear = nullptr;
        }
        delete temp;
        --size;
        return value;
    }

    // 获取队头元素
    T getFront() const {
        if (isEmpty()) {
            throw std::underflow_error("Queue is empty");
        }
        return front->data;
    }
};

int main() {
    Queue<int> queue;

    try {
        // 入队操作
        queue.enqueue(1);
        queue.enqueue(2);
        queue.enqueue(3);

        // 输出队头元素
        std::cout << "Front element: " << queue.getFront() << std::endl;

        // 出队操作
        std::cout << "Dequeued element: " << queue.dequeue() << std::endl;

        // 再次输出队头元素
        std::cout << "Front element after dequeue: " << queue.getFront() << std::endl;

        // 输出队列中的元素数量
        std::cout << "Queue size: " << queue.getSize() << std::endl;
    } catch (const std::exception& e) {
        std::cerr << "Exception: " << e.what() << std::endl;
    }

    return 0;
}

双端队列Deque:
    允许你从队列的两端进行元素的插入和删除操作,既可以在头部进行操作,也可以在尾部进行操作。

1)通过数组+前后索引实现

#include <iostream>
#include <stdexcept>

template <typename T, int capacity>
class ArrayDeque {
private:
    T arr[capacity];
    int front;
    int rear;
    int size;

public:
    ArrayDeque() : front(0), rear(0), size(0) {}

    // 判断队列是否为空
    bool isEmpty() const {
        return size == 0;
    }

    // 判断队列是否已满
    bool isFull() const {
        return size == capacity;
    }

    // 在队头插入元素
    void insertFront(const T& value) {
        if (isFull()) {
            throw std::overflow_error("Deque is full");
        }
        front = (front - 1 + capacity) % capacity;
        arr[front] = value;
        ++size;
    }

    // 在队尾插入元素
    void insertRear(const T& value) {
        if (isFull()) {
            throw std::overflow_error("Deque is full");
        }
        arr[rear] = value;
        rear = (rear + 1) % capacity;
        ++size;
    }

    // 从队头删除元素
    T deleteFront() {
        if (isEmpty()) {
            throw std::underflow_error("Deque is empty");
        }
        T value = arr[front];
        front = (front + 1) % capacity;
        --size;
        return value;
    }

    // 从队尾删除元素
    T deleteRear() {
        if (isEmpty()) {
            throw std::underflow_error("Deque is empty");
        }
        rear = (rear - 1 + capacity) % capacity;
        T value = arr[rear];
        --size;
        return value;
    }

    // 获取队头元素
    T getFront() const {
        if (isEmpty()) {
            throw std::underflow_error("Deque is empty");
        }
        return arr[front];
    }

    // 获取队尾元素
    T getRear() const {
        if (isEmpty()) {
            throw std::underflow_error("Deque is empty");
        }
        return arr[(rear - 1 + capacity) % capacity];
    }

    // 获取队列中的元素数量
    int getSize() const {
        return size;
    }
};

int main() {
    ArrayDeque<int, 5> deque;

    try {
        deque.insertFront(1);
        deque.insertRear(2);
        std::cout << "Front element: " << deque.getFront() << std::endl;
        std::cout << "Rear element: " << deque.getRear() << std::endl;

        deque.deleteFront();
        std::cout << "Front element after deleteFront: " << deque.getFront() << std::endl;

        deque.deleteRear();
        std::cout << "Is deque empty after deletions? " << (deque.isEmpty() ? "Yes" : "No") << std::endl;
    } catch (const std::exception& e) {
        std::cerr << "Exception: " << e.what() << std::endl;
    }

    return 0;
}

2)通过链表+前后指针实现

#include <iostream>
#include <stdexcept>

// 定义链表节点结构体
template <typename T>
struct Node {
    T data;
    Node<T>* prev;
    Node<T>* next;
    Node(const T& value) : data(value), prev(nullptr), next(nullptr) {}
};

template <typename T>
class LinkedDeque {
private:
    Node<T>* front;
    Node<T>* rear;
    int size;

public:
    LinkedDeque() : front(nullptr), rear(nullptr), size(0) {}

    ~LinkedDeque() {
        while (!isEmpty()) {
            deleteFront();
        }
    }

    // 判断队列是否为空
    bool isEmpty() const {
        return size == 0;
    }

    // 在队头插入元素
    void insertFront(const T& value) {
        Node<T>* newNode = new Node<T>(value);
        if (isEmpty()) {
            front = rear = newNode;
        } else {
            newNode->next = front;
            front->prev = newNode;
            front = newNode;
        }
        ++size;
    }

    // 在队尾插入元素
    void insertRear(const T& value) {
        Node<T>* newNode = new Node<T>(value);
        if (isEmpty()) {
            front = rear = newNode;
        } else {
            newNode->prev = rear;
            rear->next = newNode;
            rear = newNode;
        }
        ++size;
    }

    // 从队头删除元素
    T deleteFront() {
        if (isEmpty()) {
            throw std::underflow_error("Deque is empty");
        }
        T value = front->data;
        Node<T>* temp = front;
        front = front->next;
        if (front == nullptr) {
            rear = nullptr;
        } else {
            front->prev = nullptr;
        }
        delete temp;
        --size;
        return value;
    }

    // 从队尾删除元素
    T deleteRear() {
        if (isEmpty()) {
            throw std::underflow_error("Deque is empty");
        }
        T value = rear->data;
        Node<T>* temp = rear;
        rear = rear->prev;
        if (rear == nullptr) {
            front = nullptr;
        } else {
            rear->next = nullptr;
        }
        delete temp;
        --size;
        return value;
    }

    // 获取队头元素
    T getFront() const {
        if (isEmpty()) {
            throw std::underflow_error("Deque is empty");
        }
        return front->data;
    }

    // 获取队尾元素
    T getRear() const {
        if (isEmpty()) {
            throw std::underflow_error("Deque is empty");
        }
        return rear->data;
    }

    // 获取队列中的元素数量
    int getSize() const {
        return size;
    }
};

int main() {
    LinkedDeque<int> deque;

    try {
        deque.insertFront(1);
        deque.insertRear(2);
        std::cout << "Front element: " << deque.getFront() << std::endl;
        std::cout << "Rear element: " << deque.getRear() << std::endl;

        deque.deleteFront();
        std::cout << "Front element after deleteFront: " << deque.getFront() << std::endl;

        deque.deleteRear();
        std::cout << "Is deque empty after deletions? " << (deque.isEmpty() ? "Yes" : "No") << std::endl;
    } catch (const std::exception& e) {
        std::cerr << "Exception: " << e.what() << std::endl;
    }

    return 0;
}
1.3 链表:

    和顺序表不同,使用链表存储数据,不强制要求数据在内存中集中存储,各个元素可以分散存储在内存中。链表存储数据间逻辑关系的实现方案是:为每一个元素配置一个指针,每个元素的指针都指向自己的直接后继元素。

单向链表:
在这里插入图片描述

#include <iostream>

struct ListNode {
    int val;
    ListNode* next;
    ListNode(int x) : val(x), next(nullptr) {}
};

class LinkedList {
private:
    ListNode* head;

public:
    LinkedList() : head(nullptr) {}

    void insertAtHead(int val) {
        ListNode* newNode = new ListNode(val);
        newNode->next = head;
        head = newNode;
    }

    void insertAtTail(int val) {
        ListNode* newNode = new ListNode(val);
        if (head == nullptr) {
            head = newNode;
            return;
        }
        ListNode* curr = head;
        while (curr->next!= nullptr) {
            curr = curr->next;
        }
        curr->next = newNode;
    }

    void deleteAtHead() {
        if (head == nullptr) {
            std::cout << "链表为空,无法删除" << std::endl;
            return;
        }
        ListNode* temp = head;
        head = head->next;
        delete temp;
    }

    void deleteAtTail() {
        if (head == nullptr) {
            std::cout << "链表为空,无法删除" << std::endl;
            return;
        }
        if (head->next == nullptr) {
            delete head;
            head = nullptr;
            return;
        }
        ListNode* curr = head;
        while (curr->next->next!= nullptr) {
            curr = curr->next;
        }
        delete curr->next;
        curr->next = nullptr;
    }

    void printList() {
        ListNode* curr = head;
        while (curr!= nullptr) {
            std::cout << curr->val << " ";
            curr = curr->next;
        }
        std::cout << std::endl;
    }

    // 查找元素是否存在
    bool search(int val) {
        ListNode* curr = head;
        while (curr!= nullptr) {
            if (curr->val == val) {
                return true;
            }
            curr = curr->next;
        }
        return false;
    }
};

int main() {
    LinkedList list;
    list.insertAtHead(3);
    list.insertAtHead(2);
    list.insertAtHead(1);
    std::cout << "插入头部后的链表: ";
    list.printList();

    list.insertAtTail(4);
    list.insertAtTail(5);
    std::cout << "插入尾部后的链表: ";
    list.printList();

    list.deleteAtHead();
    std::cout << "删除头部节点后的链表: ";
    list.printList();

    list.deleteAtTail();
    std::cout << "删除尾部节点后的链表: ";
    list.printList();

    if (list.search(3)) {
        std::cout << "元素 3 存在于链表中" << std::endl;
    } else {
        std::cout << "元素 3 不存在于链表中" << std::endl;
    }

    return 0;
}

双向链表:

    “双向”指的是各节点之间的逻辑关系是双向的,头指针通常只设置一个。
在这里插入图片描述

#include <iostream>

// 定义双向链表节点结构体
struct ListNode {
    int val;
    ListNode* prev;
    ListNode* next;
    ListNode(int x) : val(x), prev(nullptr), next(nullptr) {}
};

// 双向链表类
class DoublyLinkedList {
private:
    ListNode* head;
    ListNode* tail;

public:
    DoublyLinkedList() : head(nullptr), tail(nullptr) {}

    // 插入节点到链表头部
    void insertAtHead(int val) {
        ListNode* newNode = new ListNode(val);
        if (head == nullptr) {
            head = newNode;
            tail = newNode;
        } else {
            newNode->next = head;
            head->prev = newNode;
            head = newNode;
        }
    }

    // 插入节点到链表尾部
    void insertAtTail(int val) {
        ListNode* newNode = new ListNode(val);
        if (tail == nullptr) {
            head = newNode;
            tail = newNode;
        } else {
            newNode->prev = tail;
            tail->next = newNode;
            tail = newNode;
        }
    }

    // 删除头部节点
    void deleteAtHead() {
        if (head == nullptr) {
            std::cout << "链表为空,无法删除" << std::endl;
            return;
        }
        ListNode* temp = head;
        if (head == tail) {
            head = nullptr;
            tail = nullptr;
        } else {
            head = head->next;
            head->prev = nullptr;
        }
        delete temp;
    }

    // 删除尾部节点
    void deleteAtTail() {
        if (tail == nullptr) {
            std::cout << "链表为空,无法删除" << std::endl;
            return;
        }
        ListNode* temp = tail;
        if (head == tail) {
            head = nullptr;
            tail = nullptr;
        } else {
            tail = tail->prev;
            tail->next = nullptr;
        }
        delete temp;
    }

    // 打印链表元素(从头到尾)
    void printList() {
        ListNode* curr = head;
        while (curr!= nullptr) {
            std::cout << curr->val << " ";
            curr = curr->next;
        }
        std::cout << std::endl;
    }

    // 打印链表元素(从尾到头)
    void printListReverse() {
        ListNode* curr = tail;
        while (curr!= nullptr) {
            std::cout << curr->val << " ";
            curr = curr->prev;
        }
        std::cout << std::endl;
    }
};

int main() {
    DoublyLinkedList list;
    list.insertAtHead(3);
    list.insertAtHead(2);
    list.insertAtHead(1);
    std::cout << "插入头部后的链表: ";
    list.printList();

    list.insertAtTail(4);
    list.insertAtTail(5);
    std::cout << "插入尾部后的链表: ";
    list.printList();

    list.deleteAtHead();
    std::cout << "删除头部节点后的链表: ";
    list.printList();

    list.deleteAtTail();
    std::cout << "删除尾部节点后的链表: ";
    list.printList();

    std::cout << "从尾到头打印链表: ";
    list.printListReverse();

    return 0;
}

单向循环链表与双向循环链表:
    单链表通过首尾连接可以构成单向循环链表:
在这里插入图片描述
    双向链表也可以进行首尾连接,构成双向循环链表:
在这里插入图片描述

静态链表:
    用静态链表存储数据,数据全部存储在数组中(和顺序表一样),但存储位置是随机的,数据之间"一对一"的逻辑关系通过一个整形变量(称为"游标",和指针功能类似)维持(和链表类似)。
在静态链表中,数组的每个元素代表一个链表节点,每个节点通常包含两部分:

  1. 数据域:存储节点的数据。
  2. 游标域(或指针域):存储下一个节点在数组中的索引,而不是像动态链表那样存储一个物理地址。

在这里插入图片描述

1.4 栈

    栈是一种后进先出的数据结构。

#include <iostream>
#include <vector>

class ArrayStack {
private:
    std::vector<int> data;  // 存储元素的数组
public:
    // 入栈操作
    void push(int value) {
//使用 data.push_back(value) 将元素添加到 data 的末尾,实现入栈操作。
        data.push_back(value);
    }

    // 出栈操作
    int pop() {
        if (isEmpty()) {
            throw s
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

浅笑一斤

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值