秋招突击——6/14——复习{(树形DP)树的最长路径}——新作{非递归求二叉树的深度、重复区间合并}

news2024/11/29 8:01:54

文章目录

    • 引言
    • 复习
      • 树形DP——树的最长路径
    • 新作
      • 使用dfs非递归计算二叉树的深度
      • 多个区间合并删除问题
        • 实现思路
        • 实现代码
        • 参考思路
    • 总结

引言

  • 这两天可能有点波动,但是算法题还是尽量保证复习和新作一块弄,数量上可能有所差别。

复习

树形DP——树的最长路径

  • 这道题是没有完全听完,但是到现在这个阶段,最起码得数组实现邻接链表做完。

无向图的一维数组表示邻接表实现

  • 首先说明一下,这里要使用邻接链表实现,这里是使用一维数组实现的邻接链表。
  • 同时这里是双向链表,所以,要加两次边,具体是实现如下

在这里插入图片描述

下面开始具体的分析

  • 树是一个确定的拓扑结构,每一个节点之间是是存在对应的父子关系,所以列觉路径可以更换为枚举中间节点,具体分析见下图,这是参考别人的,分析的很有道理

  • 通过红色节点的有三条路径

    • 红色路径,是一红色节点为根节点的最长的路径
    • 蓝色路径,是以红色节点为跟节点的最长路径和次长路径的和
    • 橙色路径,是红色节点的父节点的相同情况,具体见上图。
  • 相当于这道题,就是在遍历的过程中,计算对应的最长路径和子路径,然后在计算两者的和。

图片参考来源
在这里插入图片描述

动态规划分析

  • 这道题是两个动态规划,分别是动态规划计算最长的路径,次长的路径,而且是一个很明显的动态规划题目,就是子状态影响最终的状态。
  • 所以状态转移函数,就是计算的最长路径和次长路径,这里的动态规划,就是两个路径,f1[i]表示以节点i为根节点的最长路径,f2[i]表示以i为根节点的次长路径。具体如下

在这里插入图片描述
在这里插入图片描述

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;

const int N = 10010;
int h[N],e[2*N],ne[2*N],w[2*N],idx;
int f1[N],f2[N],res; // f1保存最长的转移路径,f2保存次长的转移路径
int n;

void add(int a,int b,int c){
    e[idx] = b;
    w[idx] = c;
    ne[idx] = h[a];
    h[a] = idx ++;
}

void dfs(int r,int father){
    // 这里r是对应的根节点,father是对应的父节点
    f1[r] = 0,f2[r] = 0;
    for (int i = h[r]; ~i; i = ne[i]) {
        int j = e[i];  // 确定子节点的编号
        if (j == father)    continue;
        dfs(j,r);
        if(f1[j] + w[i] >= f1[r]) f2[r] = f1[r],f1[r] = f1[j] + w[i];
        else if(f1[j] + w[i] > f2[r]) f2[r] = f1[j] + w[i];
    }
    res = max(res,f1[r] + f2[r]);
}

int main(){
    cin>>n;
    // 构建无向图
    memset(h,-1,sizeof(h));
    for (int i = 0; i < n -1; ++i) {
        int a,b,c;
        cin>>a>>b>>c;
        add(a,b,c),add(b,a,c);
    }

    // 遍历对应的无向图
    for (int i = h[1]; ~i; i = ne[i]) {
        cout<<1<<"  "<<e [i]<<endl;
    }

    cout<<res;
    return 0;
}
  • 这道题拖了那么久,乍一看,其实还是蛮简单的,思路只要清楚了,后续还是很好实现的,然后那个使用一维数组实现的邻接链表的,之前是没有做过,现在知道怎么用了,还是蛮简单的。

新作

使用dfs非递归计算二叉树的深度

  • dfs非递归二叉树高度,一开始写了个经典队列的bfs,意识到不对后开始改,最后没改完,就说了个暴力找到每个叶子的高度的思路。

  • 这个忘记的有点多,如果单纯使用栈来实现的话,就需要每一次入栈当前节点的子节点还有对应的深度,然后出栈,如果是叶子节点,就比较一下长度,如果不是,就继续做出栈和入栈的操作。

  • 这里实现的基本上和我写的比较类似

#include <iostream>
#include <stack>

using namespace std;

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

// 生成样例
TreeNode* createSampleTree1() {
    TreeNode* root = new TreeNode(1);
    root->left = new TreeNode(2);
    root->right = new TreeNode(3);
    root->left->left = new TreeNode(4);
    root->left->right = new TreeNode(5);
    root->right->right = new TreeNode(6);
    root->left->left->left = new TreeNode(7);
    return root;
}

int treeHeight(TreeNode* root){
    // 使用非递归的方式计算的树深度
    stack<pair<TreeNode *,int>> s;

    // 根节点入栈并重置深度
    s.push({root,1});
    int r = 0;
    // 出栈并遍历每一个节点的子节点
    while(!s.empty()){
        TreeNode* t = s.top().first;
        int l = s.top().second;
        s.pop();

        // 判定左子节点是否为空
        if (t->left)    s.push({t->left,l + 1});
        if (t->right)    s.push({t->right,l + 1});

        // 比较深度
        r = max(r,l);
    }
    return r;
}

int main(){
    TreeNode* root = createSampleTree1();
    cout<<treeHeight(root);
}
  • 参考方法
    • 这里指的学习的是一个auto的使用,通过下述方式可以直接进行遍历使用。
auto [node, depth] = stack.top();
#include <iostream>
#include <stack>
#include <algorithm> // for max

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

int treeHeight(TreeNode* root) {
    if (root == nullptr) return 0;

    std::stack<std::pair<TreeNode*, int>> stack;
    stack.push({root, 1});
    int maxHeight = 0;

    while (!stack.empty()) {
        auto [node, depth] = stack.top();
        stack.pop();
        if (node != nullptr) {
            maxHeight = std::max(maxHeight, depth);
            if (node->left != nullptr) stack.push({node->left, depth + 1});
            if (node->right != nullptr) stack.push({node->right, depth + 1});
        }
    }

    return maxHeight;
}

int main() {
    // 示例二叉树
    TreeNode* root = new TreeNode(1);
    root->left = new TreeNode(2);
    root->right = new TreeNode(3);
    root->left->left = new TreeNode(4);
    root->left->right = new TreeNode(5);

    std::cout << "树的高度是: " << treeHeight(root) << std::endl;

    // 清理内存
    delete root->left->left;
    delete root->left->right;
    delete root->left;
    delete root->right;
    delete root;

    return 0;
}

多个区间合并删除问题

  • 这个也是在网上搜索的,部分拼多多主管面可能问到的题目,所以这里做一下,具体题目描述如下
给定一个n×2的二维数组,数组的每一行代表一个区间,
如果一个区间被另一个区间包含就删掉该区间,返回剩
下的所有区间。
* 比如: [1 2][1 ,3]包含。
实现思路
  • 这里是使用自定义排序实现的,如果包含关系,就将之按照包含的关系进行排序,然后在进行从前往后逐步进行遍历,对于相同的数组直接去除。最后剩下的就是对应的元素。
  • 自定义排序实现贪婪算法!
  • 这个方法确实是有问题的,有一部分样例是没有考虑到,不过背一下这个模版的。
实现代码
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

using Interval = pair<int,int>;

vector<Interval> removeContainedIntervals(vector<Interval>& intervals){

    // 可以对区间进行排序,按照包含关系进行排序
    vector<Interval> res;
    sort(intervals.begin(),intervals.end(),[](auto a,auto b){
        if(a.first <= b.first && a.second >= b.second)    return 1;
        else return 0;
    });
    for (int i = 0;i < intervals.size();i ++) {
        Interval t = intervals[i];
        res.push_back(t);
        while((i + 1 < intervals.size())
            && t.first <= intervals[i+1].first
            && t.second >= intervals[i+1].second)
            i ++;

    }
    return res;
}

int main() {
    vector<vector<Interval>> samples = {
            {{1, 4}, {2, 3}, {1, 3}, {4, 6}, {5, 7}},
//            {{1, 5}, {2, 4}, {6, 8}, {7, 9}, {5, 10}},
//            {{1, 2}, {3, 4}, {2, 3}, {1, 5}, {6, 7}},
//            {{1, 2}, {2, 3}, {3, 4}, {4, 5}},
//            {{1, 3}, {2, 6}, {8, 10}, {15, 18}}
    };

    for (size_t i = 0; i < samples.size(); ++i) {
        vector<Interval> result = removeContainedIntervals(samples[i]);
        cout << "样例 " << i + 1 << " 剩余的区间为:" << endl;
        for (const auto& interval : result) {
            cout << "[" << interval.first << ", " << interval.second << "] ";
        }
        cout << endl;
    }

    return 0;
}
参考思路
  • 这里是一个贪心算法解决区间问题的模板,需要好好练习一下,和我的方法差不多,只不过他是针对单边进行排序的
  • 这里是使用标准区间进行比较的,会更加灵活方便一点,比我的方法要好很多。
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

// 定义一个区间类型
using Interval = pair<int, int>;

// 比较函数,用于排序区间
bool compareIntervals(const Interval &a, const Interval &b) {
    if (a.first == b.first) {
        return a.second < b.second;
    }
    return a.first < b.first;
}

vector<Interval> removeContainedIntervals(vector<Interval>& intervals) {
    // 如果区间列表为空,直接返回空列表
    if (intervals.empty()) {
        return {};
    }

    // 按照起始点排序,起始点相同则按照终止点排序
    sort(intervals.begin(), intervals.end(), compareIntervals);

    vector<Interval> result;
    Interval last = intervals[0]; // 初始化第一个区间作为比较基准

    for (size_t i = 1; i < intervals.size(); ++i) {
        // 如果当前区间不被上一个区间包含
        if (intervals[i].first > last.first && intervals[i].second > last.second) {
            result.push_back(last); // 保留上一个区间
            last = intervals[i];    // 更新比较基准
        } else {
            // 如果当前区间的终止点更长,更新比较基准
            if (intervals[i].second > last.second) {
                last = intervals[i];
            }
        }
    }

    // 最后一个区间也需要保留
    result.push_back(last);

    return result;
}

int main() {
    vector<vector<Interval>> samples = {
        {{1, 4}, {2, 3}, {1, 3}, {4, 6}, {5, 7}},
        {{1, 5}, {2, 4}, {6, 8}, {7, 9}, {5, 10}},
        {{1, 2}, {3, 4}, {2, 3}, {1, 5}, {6, 7}},
        {{1, 2}, {2, 3}, {3, 4}, {4, 5}},
        {{1, 3}, {2, 6}, {8, 10}, {15, 18}}
    };

    for (size_t i = 0; i < samples.size(); ++i) {
        vector<Interval> result = removeContainedIntervals(samples[i]);
        cout << "样例 " << i + 1 << " 剩余的区间为:" << endl;
        for (const auto& interval : result) {
            cout << "[" << interval.first << ", " << interval.second << "] ";
        }
        cout << endl;
    }

    return 0;
}

总结

  • 复习的那个最长路径,还是蛮简单的,今天终于看懂了,继续加油吧。明天可以快速写一下。
  • 今天算是做了两道新的题目,dfs获取树的深度那道题,是自己做出来的,然后区间合并的那道题,是参考别人的,不过看了题解,加油吧!明天把题目再过一遍!

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

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

相关文章

Vue使用vue-esign实现在线签名

Vue在线签名 一、目的二、样式三、代码1、依赖2、代码2.1 在线签名组件2.2父组件 一、目的 又来了一个问题&#xff0c;直接让我在线签名&#xff08;还不能存储base64&#xff09;&#xff0c;并且还得上传&#xff0c;我直接***违禁词。 二、样式 初始样式 点击前往组件&am…

el-tabl 表格行列转换(表头在左数据在右)

1 效果展示 1 空数据 2 有数据 2 完成代码 2.1 SchedulingTable.vue <template><div class="schedulingTable"><el-row :gutter="1" class="row-center"><el-col :span="3"><el-tag type="&quo…

4机器学习期末复习

在机器学习中&#xff0c;数据清洗与转换包括哪些内容&#xff1f; 对数据进行初步的预处理&#xff0c;需要将其转换为一种适合机器学习模型的表示形式对许多模型类型来说&#xff0c;这种表示就是包含数值数据的向量或者矩阵&#xff1a; 1&#xff09;将类别数据编码成为对…

C++ //CCF-CSP计算机软件能力认证 202406-1 矩阵重塑(其一)

CCF-CSP计算机软件能力认证 202406-1 矩阵重塑&#xff08;其一&#xff09; 题目背景 矩阵&#xff08;二维&#xff09;的重塑&#xff08;reshape&#xff09;操作是指改变矩阵的行数和列数&#xff0c;同时保持矩阵中元素的总数不变。 题目描述 矩阵的重塑操作可以具体…

自动控制理论---离散傅里叶变换(DFT)进行信号谱分析

1、实验设备 PC计算机1台&#xff0c;MATLAB软件1套。 2、实验目的&#xff1a; 学习使用离散傅里叶变换&#xff08;DFT&#xff09;进行信号谱分析的方法。选择合适的变换区间长度N&#xff0c;对给定信号进行谱分析&#xff0c;并绘制幅频特性和相频曲线。 3、实验原理说…

Python武器库开发-武器库篇之SMB服务暴力破解(五十五)

Python武器库开发-武器库篇之SMB服务暴力破解(五十五) SMB服务&#xff08;Server Message Block&#xff09;是一种用于文件共享、打印机共享和其他资源共享的网络协议。它最初由IBM开发&#xff0c;后来被微软广泛采用。 SMB服务允许多台计算机在网络上共享文件和资源&…

使用 3D 图形 API 在 C# 中将 PLY 转换为 OBJ

OBJ和PLY是一些广泛使用的 3D 文件格式&#xff0c;易于编写和读取。这篇博文演示了如何以编程方式在 C# 中将 PLY 转换为 OBJ。此外&#xff0c;它还介绍了一种用于 3D 文件格式转换的在线3D 转换器。是的&#xff0c;Aspose.3D for .NET为程序员和非程序员提供了此功能来执行…

C# Winform Chart图表使用和详解

Chart控件是微软自带的一种图形可视化组件&#xff0c;能展示种类丰富的图表形式。如曲线图&#xff0c;折线图&#xff0c;饼状图&#xff0c;环形图&#xff0c;柱状图&#xff0c;曲线面积图。 实例代码链接&#xff1a;https://download.csdn.net/download/lvxingzhe3/8943…

网络安全 - DNS劫持原理 + 实验

DNS 劫持 什么是 DNS 为什么需要 DNS D N S \color{cyan}{DNS} DNS&#xff08;Domain Name System&#xff09;即域名系统。我们常说的 DNS 是域名解析协议。 DNS 协议提供域名到 IP 地址之间的解析服务。计算机既可以被赋予 IP 地址&#xff0c;也可以被赋予主机名和域名。用…

java第二十三课 —— 继承

面向对象的三大特征 继承 继承可以解决代码复用&#xff0c;让我们的编程更加靠近人类思维&#xff0c;当多个类存在相同的属性&#xff08;变量&#xff09;和方法时&#xff0c;可以从这些类中抽象出父类&#xff0c;在父类中定义这些相同的属性和方法&#xff0c;所有的子…

Anaconda环境安装失败的解决方案

链接步骤的补充。 为了运行marlib&#xff0c;需要一个全新的Anaconda环境。但是&#xff0c;不想把文件安装在C盘&#xff0c;会造成空间不足。于是试着在.condarc文件里面改动了路径&#xff0c;具体如图。 上图中&#xff0c;在defaults前面添加了D盘的路径作为安装路径。 …

增材制造引领模具创新之路

随着科技的快速发展和制造业的不断转型升级&#xff0c;增材制造&#xff08;也称为3D打印&#xff09;技术正逐渐展现出其在模具智造中的巨大潜力和优势。增材制造以其独特的加工方式和设计理念&#xff0c;为模具行业带来了革命性的变革&#xff0c;为传统制造业注入了新的活…

【odoo | XML-RPC】odoo外部API解读,实现跨系统间的通讯!

概要 文章注意对官方的XML-RPC进行解读实操&#xff0c;以python为例&#xff0c;给大家介绍其使用方式和调用方法。 内容 什么是odoo的外部API? Odoo 的外部 API 是一种允许外部应用程序与 Odoo 实例进行交互的接口。通过 API&#xff0c;可以执行各种操作&#xff0c;例如…

如果给电商系统颜值搞排名,我觉得淘宝千牛系统是天花板了。

淘宝的商家操作界面-千牛系统经过多年的迭代&#xff0c;无论从颜值上、功能上还是用户体验上都是行业天花板的存在&#xff0c;我截图软件上的一些图给大家分享下。

helm升级部署时出现升级挂起状态处理

问题 在使用helm 升级命令时&#xff0c;升级命令如下&#xff1a; helm upgrade -i -f ./values-prod.yaml myapp ./ -n myns --create-namespace中途因为网络原因&#xff0c;再次运行上面升级命令时出现&#xff0c;如下错误&#xff1a; Error: UPGRADE FAILED: another …

linux centos 安装docker

本章教程介绍如何在Linux CentOS中安装Docker服务。 1. 更新系统软件包 首先,确保你的系统软件包是最新的: sudo yum update -y2. 安装必要的依赖包 安装一些必要的依赖包: sudo yum install -y yum-utils device-mapper-persistent-data lvm23. 添加 Docker 的官方仓库…

Redis订阅发布你会用了吗?

点击下方“JavaEdge”&#xff0c;选择“设为星标” 第一时间关注技术干货&#xff01; 免责声明~ 任何文章不要过度深思&#xff01; 万事万物都经不起审视&#xff0c;因为世上没有同样的成长环境&#xff0c;也没有同样的认知水平&#xff0c;更「没有适用于所有人的解决方案…

[数据集][目标检测]变电站火灾检测电力场景烟雾明火检测数据集VOC+YOLO格式140张2类别真实场景非PS合成

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;140 标注数量(xml文件个数)&#xff1a;140 标注数量(txt文件个数)&#xff1a;140 标注类别…

Prometheus的四种指标类型

目录 一、Counter 计数器 1.1Counter 是计数器类型 1.2Counter 类型示例 二、Gauge 仪表盘 2.1Gauge是测量器类型 2.2Gauge 类型示例 三、Histogram 累积直方图 3.1Histogram 作用及特点 3.2使用 histogram 柱状图 四、Summary 摘要 一、Counter 计数器 1.1Counter …