数据结构与算法面试高频(基础算法)

基础算法

1 实现一个vector⭐⭐⭐⭐

#include <iostream>
#include <stdexcept>

template <typename T>
class MyVector {
private:
    T* data;  // 指向动态分配数组的指针
    size_t size_;  // 当前元素的数量
    size_t capacity_;  // 当前数组的容量

    // 重新分配内存,扩大容量
    void reserve(size_t newCapacity) {
        if (newCapacity <= capacity_) return;

        T* newData = new T[newCapacity];
        for (size_t i = 0; i < size_; ++i) {
            newData[i] = data[i];
        }
        delete[] data;
        data = newData;
        capacity_ = newCapacity;
    }

public:
    // 默认构造函数
    MyVector() : data(nullptr), size_(0), capacity_(0) {}

    // 析构函数
    ~MyVector() {
        delete[] data;
    }

    // 获取当前元素的数量
    size_t size() const {
        return size_;
    }

    // 获取当前数组的容量
    size_t capacity() const {
        return capacity_;
    }

    // 判断向量是否为空
    bool empty() const {
        return size_ == 0;
    }

    // 在向量末尾添加一个元素
    void push_back(const T& value) {
        if (size_ == capacity_) {
            if (capacity_ == 0) {
                reserve(1);
            } else {
                reserve(2 * capacity_);
            }
        }
        data[size_++] = value;
    }

    // 移除向量末尾的元素
    void pop_back() {
        if (size_ > 0) {
            --size_;
        }
    }

    // 访问指定位置的元素
    T& operator[](size_t index) {
        if (index >= size_) {
            throw std::out_of_range("Index out of range");
        }
        return data[index];
    }

    const T& operator[](size_t index) const {
        if (index >= size_) {
            throw std::out_of_range("Index out of range");
        }
        return data[index];
    }
};

// 测试代码
int main() {
    MyVector<int> vec;
    vec.push_back(1);
    vec.push_back(2);
    vec.push_back(3);

    std::cout << "Size: " << vec.size() << std::endl;
    std::cout << "Capacity: " << vec.capacity() << std::endl;

    for (size_t i = 0; i < vec.size(); ++i) {
        std::cout << vec[i] << " ";
    }
    std::cout << std::endl;

    vec.pop_back();
    std::cout << "Size after pop_back: " << vec.size() << std::endl;

    return 0;
}


2 来手写一个链表⭐⭐⭐⭐

#include <iostream>

// 定义链表节点类
template <typename T>
class Node {
public:
    T data;         // 节点存储的数据
    Node<T>* next;  // 指向下一个节点的指针

    // 构造函数
    Node(T value) : data(value), next(nullptr) {}
};

// 定义链表类
template <typename T>
class LinkedList {
private:
    Node<T>* head;  // 链表头指针

public:
    // 构造函数,初始化头指针为 nullptr
    LinkedList() : head(nullptr) {}

    // 析构函数,释放链表中所有节点的内存
    ~LinkedList() {
        while (head != nullptr) {
            Node<T>* temp = head;
            head = head->next;
            delete temp;
        }
    }

    // 在链表头部插入一个新节点
    void insertAtHead(T value) {
        Node<T>* newNode = new Node<T>(value);
        newNode->next = head;
        head = newNode;
    }

    // 在链表尾部插入一个新节点
    void insertAtTail(T value) {
        Node<T>* newNode = new Node<T>(value);
        if (head == nullptr) {
            head = newNode;
            return;
        }
        Node<T>* temp = head;
        while (temp->next != nullptr) {
            temp = temp->next;
        }
        temp->next = newNode;
    }

    // 删除链表头部节点
    void deleteAtHead() {
        if (head == nullptr) {
            return;
        }
        Node<T>* temp = head;
        head = head->next;
        delete temp;
    }

    // 删除链表尾部节点
    void deleteAtTail() {
        if (head == nullptr) {
            return;
        }
        if (head->next == nullptr) {
            delete head;
            head = nullptr;
            return;
        }
        Node<T>* temp = head;
        while (temp->next->next != nullptr) {
            temp = temp->next;
        }
        delete temp->next;
        temp->next = nullptr;
    }

    // 打印链表中的所有元素
    void printList() {
        Node<T>* temp = head;
        while (temp != nullptr) {
            std::cout << temp->data << " ";
            temp = temp->next;
        }
        std::cout << std::endl;
    }
};

int main() {
    LinkedList<int> list;

    // 在链表尾部插入元素
    list.insertAtTail(1);
    list.insertAtTail(2);
    list.insertAtTail(3);

    // 打印链表
    std::cout << "After inserting at tail: ";
    list.printList();

    // 在链表头部插入元素
    list.insertAtHead(0);

    // 打印链表
    std::cout << "After inserting at head: ";
    list.printList();

    // 删除链表头部元素
    list.deleteAtHead();

    // 打印链表
    std::cout << "After deleting at head: ";
    list.printList();

    // 删除链表尾部元素
    list.deleteAtTail();

    // 打印链表
    std::cout << "After deleting at tail: ";
    list.printList();

    return 0;
}


  • Node 类:定义了链表的节点,包含一个数据成员 data 和一个指向下一个节点的指针 next。
  • head:链表的头指针,指向链表的第一个节点。
  • 构造函数:初始化头指针为 nullptr。
  • 析构函数:释放链表中所有节点的内存,防止内存泄漏。
  • insertAtHead:在链表头部插入一个新节点。
  • insertAtTail:在链表尾部插入一个新节点。
  • deleteAtHead:删除链表头部节点。
  • deleteAtTail:删除链表尾部节点。
  • printList:打印链表中的所有元素。
  • main 函数:创建一个链表对象,进行插入和删除操作,并打印链表的内容。

通过上述代码,你可以了解如何使用 C++ 实现一个简单的链表,并对链表进行基本的操作。

3 用数组或链表来实现一个栈⭐⭐⭐⭐⭐

实现的原理都是类似的,用游标变量来控制元素的位置。

栈只需要设置一个游标变量来控制栈顶元素的位置

大家一定要掌握,面试很容易手撕代码。

使用数组实现栈

#include <iostream>
#include <stdexcept>

template <typename T, size_t MAX_SIZE>
class ArrayStack {
private:
    T data[MAX_SIZE];
    int topIndex;

public:
    ArrayStack() : topIndex(-1) {}

    // 判断栈是否为空
    bool empty() const {
        return topIndex == -1;
    }

    // 判断栈是否已满
    bool full() const {
        return topIndex == static_cast<int>(MAX_SIZE - 1);
    }

    // 入栈操作
    void push(const T& value) {
        if (full()) {
            throw std::overflow_error("Stack overflow");
        }
        data[++topIndex] = value;
    }

    // 出栈操作
    void pop() {
        if (empty()) {
            throw std::underflow_error("Stack underflow");
        }
        --topIndex;
    }

    // 获取栈顶元素
    T top() const {
        if (empty()) {
            throw std::underflow_error("Stack is empty");
        }
        return data[topIndex];
    }
};


int main() {
    ArrayStack<int, 5> stack;
    try {
        stack.push(1);
        stack.push(2);
        stack.push(3);
        std::cout << "Top element: " << stack.top() << std::endl;
        stack.pop();
        std::cout << "Top element after pop: " << stack.top() << std::endl;
    } catch (const std::exception& e) {
        std::cerr << "Exception: " << e.what() << std::endl;
    }
    return 0;
}


  • ArrayStack 类使用一个固定大小的数组 data 来存储栈中的元素,topIndex 表示栈顶元素的索引。
  • empty 方法通过检查 topIndex 是否为 -1 来判断栈是否为空。
  • full 方法通过检查 topIndex 是否达到数组的最大索引来判断栈是否已满。
  • push 方法在栈未满时将元素添加到栈顶,并更新 topIndex。
  • pop 方法在栈非空时将 topIndex 减 1,实现出栈操作。
  • top 方法在栈非空时返回栈顶元素。

使用链表实现栈

#include <iostream>
#include <stdexcept>

template <typename T>
class Node {
public:
    T data;
    Node<T>* next;

    Node(const T& value) : data(value), next(nullptr) {}
};

template <typename T>
class LinkedStack {
private:
    Node<T>* topNode;

public:
    LinkedStack() : topNode(nullptr) {}

    ~LinkedStack() {
        while (!empty()) {
            pop();
        }
    }

    // 判断栈是否为空
    bool empty() const {
        return topNode == nullptr;
    }

    // 入栈操作
    void push(const T& value) {
        Node<T>* newNode = new Node<T>(value);
        newNode->next = topNode;
        topNode = newNode;
    }

    // 出栈操作
    void pop() {
        if (empty()) {
            throw std::underflow_error("Stack underflow");
        }
        Node<T>* temp = topNode;
        topNode = topNode->next;
        delete temp;
    }

    // 获取栈顶元素
    T top() const {
        if (empty()) {
            throw std::underflow_error("Stack is empty");
        }
        return topNode->data;
    }
};


int main() {
    LinkedStack<int> stack;
    try {
        stack.push(1);
        stack.push(2);
        stack.push(3);
        std::cout << "Top element: " << stack.top() << std::endl;
        stack.pop();
        std::cout << "Top element after pop: " << stack.top() << std::endl;
    } catch (const std::exception& e) {
        std::cerr << "Exception: " << e.what() << std::endl;
    }
    return 0;
}


  • Node 类表示链表中的节点,包含一个数据成员 data 和一个指向下一个节点的指针 next。
  • LinkedStack 类使用 topNode 指向栈顶节点。
  • empty 方法通过检查 topNode 是否为 nullptr 来判断栈是否为空。
  • push 方法创建一个新节点,将其插入到链表头部,成为新的栈顶节点。
  • pop 方法在栈非空时删除栈顶节点,并释放其内存。
  • top 方法在栈非空时返回栈顶节点的数据。
  • 析构函数在对象销毁时会依次调用 pop 方法,释放所有节点的内存。

4 用数组或链表来实现一个队列⭐⭐⭐⭐

使用数组实现队列

#include <iostream>
#include <stdexcept>

template <typename T, size_t MAX_SIZE>
class ArrayQueue {
private:
    T data[MAX_SIZE];
    int frontIndex;
    int rearIndex;
    int count;

public:
    ArrayQueue() : frontIndex(0), rearIndex(0), count(0) {}

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

    // 判断队列是否已满
    bool full() const {
        return count == MAX_SIZE;
    }

    // 入队操作
    void enqueue(const T& value) {
        if (full()) {
            throw std::overflow_error("Queue is full");
        }
        data[rearIndex] = value;
        rearIndex = (rearIndex + 1) % MAX_SIZE;
        ++count;
    }

    // 出队操作
    void dequeue() {
        if (empty()) {
            throw std::underflow_error("Queue is empty");
        }
        frontIndex = (frontIndex + 1) % MAX_SIZE;
        --count;
    }

    // 获取队首元素
    T front() const {
        if (empty()) {
            throw std::underflow_error("Queue is empty");
        }
        return data[frontIndex];
    }
};


int main() {
    ArrayQueue<int, 5> queue;
    try {
        queue.enqueue(1);
        queue.enqueue(2);
        queue.enqueue(3);
        std::cout << "Front element: " << queue.front() << std::endl;
        queue.dequeue();
        std::cout << "Front element after dequeue: " << queue.front() << std::endl;
    } catch (const std::exception& e) {
        std::cerr << "Exception: " << e.what() << std::endl;
    }
    return 0;
}


  • ArrayQueue 类使用一个固定大小的数组 data 来存储队列中的元素。
  • frontIndex 指向队首元素的位置,rearIndex 指向队尾元素的下一个位置,count 记录队列中元素的数量。
  • empty 方法通过检查 count 是否为 0 来判断队列是否为空。
  • full 方法通过检查 count 是否达到数组的最大容量来判断队列是否已满。
  • enqueue 方法在队列未满时将元素添加到队尾,并更新 rearIndex 和 count。使用取模运算 % 实现循环数组,以充分利用数组空间。
  • dequeue 方法在队列非空时更新 frontIndex 和 count,实现出队操作。
  • front 方法在队列非空时返回队首元素。

使用链表实现队列

#include <iostream>
#include <stdexcept>

template <typename T>
class Node {
public:
    T data;
    Node<T>* next;

    Node(const T& value) : data(value), next(nullptr) {}
};

template <typename T>
class LinkedQueue {
private:
    Node<T>* frontNode;
    Node<T>* rearNode;

public:
    LinkedQueue() : frontNode(nullptr), rearNode(nullptr) {}

    ~LinkedQueue() {
        while (!empty()) {
            dequeue();
        }
    }

    // 判断队列是否为空
    bool empty() const {
        return frontNode == nullptr;
    }

    // 入队操作
    void enqueue(const T& value) {
        Node<T>* newNode = new Node<T>(value);
        if (empty()) {
            frontNode = rearNode = newNode;
        } else {
            rearNode->next = newNode;
            rearNode = newNode;
        }
    }

    // 出队操作
    void dequeue() {
        if (empty()) {
            throw std::underflow_error("Queue is empty");
        }
        Node<T>* temp = frontNode;
        frontNode = frontNode->next;
        if (frontNode == nullptr) {
            rearNode = nullptr;
        }
        delete temp;
    }

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


int main() {
    LinkedQueue<int> queue;
    try {
        queue.enqueue(1);
        queue.enqueue(2);
        queue.enqueue(3);
        std::cout << "Front element: " << queue.front() << std::endl;
        queue.dequeue();
        std::cout << "Front element after dequeue: " << queue.front() << std::endl;
    } catch (const std::exception& e) {
        std::cerr << "Exception: " << e.what() << std::endl;
    }
    return 0;
}


  • Node 类表示链表中的节点,包含一个数据成员 data 和一个指向下一个节点的指针 next。
  • LinkedQueue 类使用 frontNode 指向队首节点,rearNode 指向队尾节点。
  • empty 方法通过检查 frontNode 是否为 nullptr 来判断队列是否为空。
  • enque

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

嵌入式/C++面试八股文 文章被收录于专栏

该专栏面向嵌入式开发工程师、C++开发工程师,包括C语言、C++,操作系统,ARM架构、RTOS、Linux基础、Linux驱动、Linux系统移植、计算机网络、数据结构与算法、数电基础、模电基础、5篇面试题目、HR面试常见问题汇总和嵌入式面试简历模板等文章。超全的嵌入式软件工程师面试题目和高频知识点总结! 另外,专栏分为两个部分,大家可以各取所好,为了有更好的阅读体验,后面会持续更新!!!

全部评论

相关推荐

02-08 20:56
已编辑
南京工业大学 Java
在等offer的比尔很洒脱:我也是在实习,项目先不说,感觉有点点小熟悉,但是我有点疑问,这第一个实习,公司真的让实习生去部署搭建和引入mq之类的吗,是不是有点过于信任了,我实习过的两个公司都是人家正式早搭好了,根本摸不到部署搭建的
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务