C++面试:二叉树和红黑树

目录

二叉树

1. 二叉树的定义

2. 二叉树的遍历

3. 二叉树的应用

4. 实现细节

5. C++中的实现

面试准备

红黑树

红黑树的原理

红黑树的用途

示例代码

面试准备

1. 红黑树的工作原理及其规则

2. 红黑树的优势及与其他二叉搜索树(如AVL树)的比较

3. 红黑树操作的时间复杂度

4. 红黑树的基本操作编写代码

代码

红黑树节点定义和基本结构

辅助函数实现

插入操作和违规修正

遍历函数

测试用例


二叉树

1. 二叉树的定义

  • 基本概念:二叉树是一种树形结构,其中每个节点最多有两个子节点,分别称为“左子节点”和“右子节点”。
  • 特殊类型
    • 完全二叉树:除了最后一层外,每一层都被完全填满,且所有节点都尽可能地向左对齐。
    • 满二叉树:所有非叶子节点都有两个子节点,所有叶子节点都在同一层级。
    • 平衡二叉树(AVL树):任何节点的两个子树的高度差不超过1。

2. 二叉树的遍历

  • 前序遍历(根-左-右):先访问根节点,然后递归地对左子树进行前序遍历,最后对右子树进行前序遍历。
  • 中序遍历(左-根-右):先递归地对左子树进行中序遍历,然后访问根节点,最后对右子树进行中序遍历。
  • 后序遍历(左-右-根):先递归地对左子树进行后序遍历,然后对右子树进行后序遍历,最后访问根节点。
  • 层次遍历(广度优先遍历):按照树的层次从上到下,从左到右遍历所有节点。

3. 二叉树的应用

  • 二叉搜索树(BST):一种特殊的二叉树,其中每个节点的值都大于其左子树上任何节点的值,且小于其右子树上任何节点的值。
  • :特殊的完全二叉树,用于实现优先队列。
  • 哈夫曼编码树:用于数据压缩的二叉树。

4. 实现细节

  • 节点结构:通常包含数据部分和指向左、右子节点的指针。
  • 操作函数:包括创建、遍历(递归或迭代)、插入、删除、查找等。

5. C++中的实现

        在C++中,二叉树可以通过结构体或类来实现。例如:

 

struct TreeNode {int val;TreeNode *left;TreeNode *right;TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};

面试准备

  • 理解不同遍历方法的应用场景
  • 熟练实现二叉树的基本操作,如插入、删除、查找等。
  • 解决实际问题:例如,如何在二叉搜索树中找到第K小的元素,如何判断一棵树是否为平衡二叉树等。
  • 掌握递归和非递归遍历方法,以及它们的时间和空间复杂度。

红黑树

红黑树的原理

        红黑树的关键特性是保持树的平衡,这通过以下规则实现:

  1. 节点颜色:每个节点被涂成红色或黑色。
  2. 根节点规则:根节点总是黑色的。
  3. 红色节点规则:红色节点的子节点必须是黑色的(即不允许有连续的红色节点)。
  4. 黑色高度规则:从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。
  5. 新插入节点规则:新插入的节点是红色的。

        通过这些规则,红黑树保持大致的平衡,从而在插入、删除和查找操作中提供接近O(log n)的最坏情况时间复杂度。

红黑树的用途

红黑树在许多高级数据结构中都有应用,如:

  • 关联数组:在诸如C++ STL中的mapset中被用作底层结构。

        在C++的标准模板库(STL)中,mapset通常是基于红黑树实现的。这些数据结构提供了高效的查找、插入和删除操作。

        map:

#include <iostream>
#include <map>
using namespace std;int main() {map<int, string> exampleMap;// 插入键值对exampleMap[1] = "Tencent";exampleMap[2] = "Alibaba";exampleMap[3] = "ByteDance";// 遍历mapfor (auto &pair : exampleMap) {cout << pair.first << " => " << pair.second << endl;}return 0;
}

        set:

#include <iostream>
#include <set>
using namespace std;int main() {set<int> exampleSet;// 插入元素exampleSet.insert(10);exampleSet.insert(30);exampleSet.insert(20);exampleSet.insert(5);// 遍历setfor (int element : exampleSet) {cout << element << endl;}return 0;
}
  • 优先队列:可以构建以支持快速的元素插入和删除。

        虽然C++ STL中的priority_queue通常是基于二叉堆实现的,红黑树也可以用来实现一个功能类似的结构,支持快速元素插入和删除。

        注意:这里提供的是一个简化的示例,仅用于演示概念。

#include <iostream>
#include <set>
using namespace std;class PriorityQueue {set<int> pq;public:void insert(int value) {pq.insert(value);}int top() {if (!pq.empty())return *pq.begin();return -1; // 或抛出异常}void pop() {if (!pq.empty())pq.erase(pq.begin());}bool isEmpty() {return pq.empty();}
};int main() {PriorityQueue pq;pq.insert(10);pq.insert(5);pq.insert(20);while (!pq.isEmpty()) {cout << pq.top() << endl;pq.pop();}return 0;
}
  • CPU调度系统:用于维护任务的优先级队列。

        在CPU调度系统中,红黑树可以用来维护一个任务的优先级队列。以下示例展示了一个简化的任务调度系统,其中任务按优先级排序。

#include <iostream>
#include <set>
using namespace std;struct Task {int priority;string taskName;bool operator<(const Task& t) const {return priority < t.priority;}
};int main() {set<Task> schedule;// 添加任务schedule.insert({1, "Task A"});schedule.insert({3, "Task C"});schedule.insert({2, "Task B"});// 按优先级执行任务for (const auto& task : schedule) {cout << "执行任务: " << task.taskName << " (优先级: " << task.priority << ")" << endl;}return 0;
}

示例代码

        以下是C++中红黑树节点的基本结构示例:

enum Color { RED, BLACK };struct Node {int data;bool color;Node *left, *right, *parent;// ConstructorNode(int data) : data(data) {parent = left = right = nullptr;color = RED;}
};class RedBlackTree {
private:Node *root;protected:void rotateLeft(Node *&, Node *&);void rotateRight(Node *&, Node *&);void fixViolation(Node *&, Node *&);public:RedBlackTree() { root = nullptr; }void insert(const int &n);// ... 其他函数,例如遍历和删除
};

        在这个结构中,每个节点都有一个数据值、一个颜色标记(红色或黑色)、指向左子节点、右子节点和父节点的指针。红黑树类包含用于维护树平衡的旋转和修复函数。

        这只是一个非常简化的示例,红黑树的完整实现需要包括详细的插入、删除和平衡调整逻辑。

面试准备

在面试中,你可能需要:

  • 解释红黑树的工作原理及其各个规则。
  • 讨论红黑树的优势及其与其他二叉搜索树(如AVL树)的区别。
  • 分析红黑树操作的时间复杂度。
  • 可能需要对红黑树的基本操作编写代码(特别是插入和删除)。

1. 红黑树的工作原理及其规则

红黑树是一种自平衡二叉搜索树,它通过以下规则确保高效的操作时间复杂度(主要是插入和删除):

  • 节点颜色:每个节点要么是红色,要么是黑色。
  • 根节点:根节点总是黑色的。
  • 红色节点规则:红色节点的两个子节点都是黑色的(即红色节点不相邻)。
  • 每个节点到叶子节点的路径上黑色节点的数量相同:从任一节点到其每个叶子节点的所有路径上的黑色节点数目相同。
  • 新插入节点为红色:新插入的节点是红色的(除非它是根节点)。

2. 红黑树的优势及与其他二叉搜索树(如AVL树)的比较

红黑树的主要优势在于它提供了一种平衡插入、删除和查找操作的高效方式。与AVL树相比,红黑树在插入和删除操作中更加高效,因为它们需要更少的重新平衡操作。

  • 插入和删除:红黑树在插入和删除操作时通常只需要O(log n)时间,并且不需要像AVL树那样频繁的旋转来维持平衡。
  • 查找操作:AVL树由于是更加严格平衡的,对于查找密集型应用可能稍微优于红黑树。

3. 红黑树操作的时间复杂度

红黑树的所有基本操作(插入、删除、查找)的时间复杂度都是O(log n)。由于树是大致平衡的,所以这些操作的最坏情况性能也是很好的。

  • 查找:由于红黑树是二叉搜索树,查找操作的时间复杂度为O(log n)。
  • 插入和删除:尽管插入和删除可能需要额外的颜色更改和旋转来保持红黑树的性质,但这些操作的时间复杂度依然是O(log n)。

4. 红黑树的基本操作编写代码

编写红黑树的代码需要注意其性质的维持。插入和删除操作特别复杂,因为它们可能会破坏红黑树的性质,所以需要通过旋转和重新着色来修复。由于代码实现较长,我之前的回答已经提供了插入操作的一个示例。删除操作的代码更加复杂,但基本思路是在删除节点后通过一系列的旋转和颜色更改来保持树的平衡和性质。

代码

红黑树节点定义和基本结构

        首先,定义红黑树的节点结构和基本的红黑树类:       

 

#include <iostream>
using namespace std;enum Color { RED, BLACK };struct Node {int data;bool color;Node *left, *right, *parent;Node(int data) : data(data) {parent = left = right = nullptr;color = RED;}
};class RedBlackTree {
private:Node *root;protected:void rotateLeft(Node *&root, Node *&pt);void rotateRight(Node *&root, Node *&pt);void fixViolation(Node *&root, Node *&pt);public:RedBlackTree() { root = nullptr; }void insert(const int &n);void inorder();void levelOrder();
};

辅助函数实现

        接下来,实现旋转和修正违规的辅助函数:

void RedBlackTree::rotateLeft(Node *&root, Node *&pt) {Node *pt_right = pt->right;pt->right = pt_right->left;if (pt->right != nullptr)pt->right->parent = pt;pt_right->parent = pt->parent;if (pt->parent == nullptr)root = pt_right;else if (pt == pt->parent->left)pt->parent->left = pt_right;elsept->parent->right = pt_right;pt_right->left = pt;pt->parent = pt_right;
}void RedBlackTree::rotateRight(Node *&root, Node *&pt) {Node *pt_left = pt->left;pt->left = pt_left->right;if (pt->left != nullptr)pt->left->parent = pt;pt_left->parent = pt->parent;if (pt->parent == nullptr)root = pt_left;else if (pt == pt->parent->right)pt->parent->right = pt_left;elsept->parent->left = pt_left;pt_left->right = pt;pt->parent = pt_left;
}

插入操作和违规修正

        插入操作和违规修正是红黑树最核心的部分:

void RedBlackTree::fixViolation(Node *&root, Node *&pt) {Node *parent_pt = nullptr;Node *grand_parent_pt = nullptr;while ((pt != root) && (pt->color != BLACK) && (pt->parent->color == RED)) {parent_pt = pt->parent;grand_parent_pt = pt->parent->parent;/* Case : AParent of pt is left child of Grand-parent of pt */if (parent_pt == grand_parent_pt->left) {Node *uncle_pt = grand_parent_pt->right;/* Case : 1The uncle of pt is also redOnly Recoloring required */if (uncle_pt != nullptr && uncle_pt->color == RED) {grand_parent_pt->color = RED;parent_pt->color = BLACK;uncle_pt->color = BLACK;pt = grand_parent_pt;} else {/* Case : 2pt is right child of its parentLeft-rotation required */if (pt == parent_pt->right) {rotateLeft(root, parent_pt);pt = parent_pt;parent_pt = pt->parent;}/* Case : 3pt is left child of its parentRight-rotation required */rotateRight(root, grand_parent_pt);swap(parent_pt->color, grand_parent_pt->color);pt = parent_pt;}}/* Case : BParent of pt is right child of Grand-parent of pt */else {Node *uncle_pt = grand_parent_pt->left;/* Case : 1The uncle of pt is also redOnly Recoloring required */if ((uncle_pt != nullptr) && (uncle_pt->color == RED)) {grand_parent_pt->color = RED;parent_pt->color = BLACK;uncle_pt->color = BLACK;pt = grand_parent_pt;} else {/* Case : 2pt is left child of its parentRight-rotation required */if (pt == parent_pt->left) {rotateRight(root, parent_pt);pt = parent_pt;parent_pt = pt->parent;}/* Case : 3pt is right child of its parentLeft-rotation required */rotateLeft(root, grand_parent_pt);swap(parent_pt->color, grand_parent_pt->color);pt = parent_pt;}}}root->color = BLACK;
}void RedBlackTree::insert(const int &data) {Node *pt = new Node(data);// Do a normal BST insertroot = BSTInsert(root, pt);// fix Red Black Tree violationsfixViolation(root, pt);
}Node* BSTInsert(Node* root, Node* pt) {/* If the tree is empty, return a new node */if (root == nullptr)return pt;/* Otherwise, recur down the tree */if (pt->data < root->data) {root->left = BSTInsert(root->left, pt);root->left->parent = root;} else if (pt->data > root->data) {root->right = BSTInsert(root->right, pt);root->right->parent = root;}/* return the (unchanged) node pointer */return root;
}

遍历函数

        遍历函数用于验证树的结构:

void inorderHelper(Node *root) {if (root == nullptr)return;inorderHelper(root->left);cout << root->data << " ";inorderHelper(root->right);
}void RedBlackTree::inorder() { inorderHelper(root); }void levelOrderHelper(Node *root) {if (root == nullptr)return;std::queue<Node *> q;q.push(root);while (!q.empty()) {Node *temp = q.front();cout << temp->data << " ";q.pop();if (temp->left != nullptr)q.push(temp->left);if (temp->right != nullptr)q.push(temp->right);}
}void RedBlackTree::levelOrder() { levelOrderHelper(root); }

测试用例

        最后,定义一些测试用例以验证红黑树的功能:

int main() {RedBlackTree tree;tree.insert(7);tree.insert(6);tree.insert(5);tree.insert(4);tree.insert(3);tree.insert(2);tree.insert(1);cout << "Inorder Traversal of Created Tree\n";tree.inorder();cout << "\n\nLevel Order Traversal of Created Tree\n";tree.levelOrder();return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/651547.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

尚无忧球馆助教系统源码,助教小程序源码,助教源码,陪练系统源码

特色功能&#xff1a; 不同助教服务类型选择 助教申请&#xff0c;接单&#xff0c;陪练师入住&#xff0c;赚取外快 线下场馆入住 设置自己服务 城市代理 分销商入住 优惠券 技术栈&#xff1a;前端uniapp后端thinkphp 独立全开源

.NET高级面试指南专题一【委托和事件】

在C#中&#xff0c;委托&#xff08;Delegate&#xff09;和事件&#xff08;Event&#xff09;是两个重要的概念&#xff0c;它们通常用于实现事件驱动编程和回调机制。 委托定义&#xff1a; 委托是一个类&#xff0c;它定义了方法的类型&#xff0c;使得可以将方法当作另一个…

pyqt添加菜单栏

参考&#xff1a; https://blog.csdn.net/seniorwizard/article/details/109820641 import sys from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5.QtCore import Qt from PyQt5.QtWidgets import (QApplication, QMainWindow, QMenuBar, QMenu, QAction, QPlainTextEdi…

HarmonyOS鸿蒙ArkTS,封装http网络请求

HarmonyOS鸿蒙ArkTS&#xff0c;封装http网络请求 前提&#xff1a; 要想使用http请求&#xff0c;系统必须要具备ohos.permission.INTERNET权限&#xff0c;在model.json5文件中的module模块下添加如下请求权限&#xff1a; 在module.json5文件中 配置 "requestPermi…

踩坑(6)Redisson调用unlockAsync方法释放锁失败

问题描述 通过redisson的lockAsync异步方法获取到锁之后&#xff0c;再业务执行完成后调用lock.unlockAsync()无法释放当前锁&#xff0c;导致后续的方法被阻塞 public void asyncLock() {RLock lock redissonClient.getLock("asyncLock");RFuture<Void> fut…

LeetCode 热题 100 | 矩阵

目录 1 73. 矩阵置零 2 54. 螺旋矩阵 3 48. 旋转图像 4 240. 搜索二维矩阵 II 菜鸟做题第二周&#xff0c;语言是 C 1 73. 矩阵置零 解题思路&#xff1a; 遍历矩阵&#xff0c;寻找等于 0 的元素&#xff0c;记录对应的行和列将被记录的行的元素全部置 0将被记录的…

k8s 进阶实战笔记 | Scheduler 调度策略总结

文章目录 Scheduler 调度策略总结调度原理和过程调度策略nodeSelect亲和性和反亲和性NodeAffinify亲和验证PodAffinity 亲和验证PodAntiAffinity 反亲和验证污点与容忍跳过 Scheduler 调度策略 调度策略场景总结 Scheduler 调度策略总结 调度原理和过程 Scheduler 一直监听着…

k8s Ingress部署应用

Deployment(部署) Deployment处于master节点&#xff0c;master会选择合适的work节点创建Container Container会被包含在Pod中 通过k8s部署一个nginx kubectl create deployment my-nginx --imagenginx创建一个tomcat 镜像 创建一个tomcat-svc.yaml文件 创建service类型…

【心得】java JNDI配合RMI实现注入个人笔记

目录 JNDI RMI 基本概念 RMI 基本逻辑 恶意利用 JNDI注入RMI实现攻击 JNDI Java Naming and Directory Interface Java 命令和目录接口 让配置参数 和 代码 解耦的规范或者思想 低耦合 高内聚 Name 命名 java对象 通过 命名 绑定到 容器环境 java对象和一个特定的…

Java技术栈 —— Kafka入门(一)

Java技术栈 —— Kafka入门&#xff08;一&#xff09; 一、什么是Kafka&#xff1f;二、如何安装Kafka&#xff1f; 一、什么是Kafka&#xff1f; Kafka是一种消息队列&#xff0c;通过订阅-发布机制可以实现解耦合&#xff0c;用异步处理代替同步处理[1] 。&#xff08;理解这…

REVIT二次开发万能刷

将这两个参数赋予其他参数 步骤2 将来做个可以调控的版本 using System; using System.Collections.Generic; using System.Lin

如何把word檔案自動化

1.安裝 pip install jojo-office ( 安裝這個) 2.讀取表格內容與文章內容&#xff0c;範例如下 import office from docx import Document replace{“{user_name}”:“陳彥甫”, “{tele}”:“0912882881”} docDocument(“template.docx”) tabledoc.tables[0] print(“ii:”…

《Python 简易速速上手小册》第7章:高级 Python 编程(基于最新版 Python3.12 编写)

注意&#xff1a;本《Python 简易速速上手小册》 核心目的在于让零基础新手「快速构建 Python 知识体系」 文章目录 <mark >注意&#xff1a;本《Python 简易速速上手小册》<mark >核心目的在于让零基础新手「快速构建 Python 知识体系」 7.1 装饰器和闭包7.1.1 装…

华为机考入门python3--(4)牛客4-字符串分隔

分类&#xff1a;字符串 知识点&#xff1a; 复制符号* 复制3个0 0*3 000 字符串截取 截取第i位到j-1位 str[i:j] 题目来自【牛客】 input_str input().strip()# 先补齐 if len(input_str) % 8 ! 0: input_str 0 * (8 - len(input_str) % 8) # 每8个分 out…

R语言【taxlist】——indended_list():将层级结构输出为缩进列表

Package taxlist version 0.2.4 Description 将 taxlist 对象打印分类层次结构&#xff08;等级和父子关系&#xff09;输出为缩进列表。 Usage indented_list(object, ...)## S4 method for signature taxlist indented_list(object,filter,keep_children TRUE,keep_parent…

uniapp微信小程序-请求二次封装(直接可用)

一、请求封装优点 代码重用性&#xff1a;通过封装请求&#xff0c;你可以在整个项目中重用相同的请求逻辑。这样一来&#xff0c;如果 API 发生变化或者需要进行优化&#xff0c;你只需在一个地方修改代码&#xff0c;而不是在每个使用这个请求的地方都进行修改。 可维护性&a…

JAVA学习笔记三

1.java执行流程分析 2.什么是编译 javac Hello.java 1.有了java源文件&#xff0c;通过编译器将其编译成JVM可以识别的字节码文件 2.在该源文件目录下&#xff0c;通过javac编译工具对Hello.java文件进行编译 3.如果程序没有错误&#xff0c;没有任何提示&#xff0c;但在…

【Spring连载】使用Spring访问 Apache Kafka(十八)----非阻塞重试

【Spring连载】使用Spring访问 Apache Kafka&#xff08;十八&#xff09;----非阻塞重试Non-Blocking Retries 一、这种模式是如何运作的How The Pattern Works二、回退延迟精度Back Off Delay Precision概述和保证Overview and Guarantees 三、配置Configuration四、Programm…

Linux下的进程操作

进程概念 ps -elf&#xff1a;查看操作系统的所有进程&#xff08;Linux命令&#xff09; ctrl z&#xff1a;把进程切换到后台 crtl c&#xff1a;结束进程 fg&#xff1a;把进程切换到前台 获取进程进程号和父进程号 函数原型&#xff1a; pid_t getpid(void); //pid_t…

文件上传之大文件分块上传进度控制处理

在分块上传内容结束以后的事件监听&#xff0c;我们会实现 unlinkSync 删除临时文件操作&#xff0c;那么试想一下&#xff0c;在这个事件监听中&#xff0c;我们是否可以通过totalChunks以及currentChunk获取当前上传的进度情况呢&#xff1f; 后端 upload上传接口&#xff…