C++数据结构和算法——数据结构篇

0 阅读3分钟

一开始说了,数据结构可以直接概括成两部分:数组和链表,我就从这个思路 真对数组,链表,然后再结合递归来实现树,还有队列,栈可以使用STL模版中相关的库函数来实现,当然还有hashmap。

数组vector的⼏种常⻅操作 :
创建和构造 :
静态数组初始化:

int array[N][N] = {{1,2,34,5},{7,8,40,10},{2323,12,24}}

vector的几种操作方法:

std::vector<int> vec(10, 0); // 创建⼀个⼤⼩为10,所有元素初始化为0的vector
//从⼀个vector 拷⻉到另⼀个vector : 拷⻉构造函数
std::vector<int> vec1 = {1, 2, 3};
std::vector<int> vec2(vec1);
//元素访问的两种⽅式:
int value = vec.at(0);
int value = vec[0];
//front 和 back:访问第⼀个和最后⼀个元素。
int first = vec.front();
int last = vec.back();
//修改元素 
vec[0] = 10;
//尾部插⼊⼀个元素:
vec.push_back(10);
vec.pop_back();
//移除指定位置的元素
vec.erase(vec.begin() + 1); // 移除索引1的元素
//遍历数组
for (auto it = vec.begin(); it != vec.end(); ++it) {
 std::cout << *it << " ";
}
//swap()交换数据
std::vector<int> vec1 = {1, 2, 3};
std::vector<int> vec2 = {4, 5, 6};
vec1.swap(vec2);

链表头结点的定义:

struct ListNode {
 int val; // 数据域
 ListNode* next; // 指针域,指向下⼀个节点
 ListNode(int x) : val(x), next(nullptr) {} // 构造函数
};

头插法创建链表

// 使⽤头插法创建链表
ListNode* createLinkedList(const vector<int>& values) {
 ListNode* head = nullptr; // 初始化头节点为nullptr
 for (int value : values) {
 ListNode* newNode = new ListNode(value); // 创建新节点
 newNode->next = head; // 新节点的next指向当前头节点
 head = newNode; // 更新头节点为新节点
 }
 return head; // 返回头节点
}

用尾差法来创建链表

// 使⽤尾插法创建链表
ListNode* createLinkedList(const vector<int>& values) {
 ListNode* head = nullptr; // 初始化头节点为nullptr
 ListNode* tail = nullptr; // 初始化尾节点为nullptr
 for (int value : values) {
 ListNode* newNode = new ListNode(value); // 创建新节点
 if (tail == nullptr) {
 head = newNode; // 如果链表为空,新节点成为头节点
 } else {
 tail->next = newNode; // 否则,将新节点连接到尾节点的后⾯
 }
 tail = newNode; // 更新尾节点为新节点
 }
 return head; // 返回头节点
}

要记得成对的思维,创建和内存回收要成对出现。

// 释放链表内存
void freeLinkedList(ListNode* head) {
 while (head != nullptr) {
 ListNode* temp = head;
 head = head->next;
 delete temp;
 }
}

访问链表的中点:
如何访问链表的中点:使⽤快慢指针法来找到链表的中点。快指针每次移动两步,慢指针每次移动
⼀步。当快指针到达链表末尾时,慢指针将指向链表的中点

class Solution {
public:
 ListNode* middleNode(ListNode* head) {
 ListNode* slow = head; // 慢指针
 ListNode* fast = head; // 快指针
 while (fast != nullptr && fast->next != nullptr) {
 slow = slow->next; // 慢指针每次移动⼀步
 fast = fast->next->next; // 快指针每次移动两步
 }
 return slow; // 当快指针到达末尾时,慢指针指向中点
 }
};

创建一棵树

定义一个TreeNOde:
    class TreeNode {
public:
    int val;
    vector<TreeNode*> children;
 
    TreeNode(int x) : val(x) {}
};
 
构建树结构:
 
TreeNode* buildTree(vector<int>& values) {
    if (values.empty()) return nullptr;
 
    queue<TreeNode*> q;
    TreeNode* root = new TreeNode(values[0]);
    q.push(root);
 
    int i = 1;
    while (!q.empty() && i < values.size()) {
        TreeNode* node = q.front();
        q.pop();
 
        if (i < values.size() && values[i] != -1) {
            node->children.push_back(new TreeNode(values[i]));
            q.push(node->children.back());
        }
        i++;
    }
 
    return root;
}

树的遍历
DFS:前序遍历: 根——左——右

void preorderTraversal(TreeNode* root) {
    if (!root) return;
    cout << root->val << " "; // 访问根节点
    preorderTraversal(root->left); // 遍历左子树
    preorderTraversal(root->right); // 遍历右子树
}

非递归实现,使用栈来实现递归的功能

void preorderTraversal(TreeNode* root) {
    if (!root) return;
    stack<TreeNode*> st;
    st.push(root);
    while (!st.empty()) {
        TreeNode* node = st.top();
        st.pop();
        cout << node->val << " "; // 访问根节点
        if (node->right) st.push(node->right); // 右子树先入栈
        if (node->left) st.push(node->left); // 左子树后入栈
    }
}

广度优先遍历,BFS的实现

#include <iostream>
#include <queue>
#include <vector>
using namespace std;
 
void bfs(vector<vector<int>>& adj, int start) {
    int n = adj.size();
    vector<bool> visited(n, false);
    queue<int> q;
    q.push(start);
    visited[start] = true;
 
    while (!q.empty()) {
        int u = q.front();
        q.pop();
        cout << u << " ";
 
        for (int v : adj[u]) {
            if (!visited[v]) {
                visited[v] = true;
                q.push(v);
            }
        }
    }
}
 
int main() {
    vector<vector<int>> adj = {
        {1, 2}, {0, 2, 3}, {0, 1, 4}, {1, 4}, {2, 3}
    };
    bfs(adj, 0);
    return 0;
}

visited 数组的重要性
防止无限循环:在图中可能存在环,如果没有 visited 数组,算法可能会陷入无限循环。
提高效率:避免重复访问同一个节点,减少不必要的计算。
希望这些信息能帮助你更好地理解 visited 数组在广度优先遍历中的作用。