IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 【PCL自学:Recognition 1】基于对应分组算法的三维物体识别 -> 正文阅读

[数据结构与算法]【PCL自学:Recognition 1】基于对应分组算法的三维物体识别

一、初识Recognition点云识别模块

??本章节旨在解释如何基于pcl_recognition模块执行3D对象识别。pcl_segmentation库包含了将点云分割成不同簇的算法。这些算法最适合处理由许多在空间上相互隔离的区域内的点云。在这种情况下,通常使用集群将点云分解为其组成部分,然后可以独立处理这些部分。有关该模块包含的所有类和方法的解释可以参考【PCL官网:点云识别模块介绍】。
??本章节解释了如何使用对应分组算法(Correspondence Grouping algorithms),以便将3D描述符匹配阶段后获得的点对点通信集聚到当前场景中出现的模型实例中。对于每个簇,代表场景中一个可能的模型实例,对应分组算法还输出识别该模型在当前场景中6DOF姿态估计的变换矩阵。

二、基于对应分组算法识别的实例代码及分析

??在开始学习之前,可以从GitHub下载本章中使用的PCD数据集(milk.pcd和 milk_cartoon_all_small_clorox.pcd),并将文件放在已经创建好的测试文件夹中。
??另外,将以下代码复制并粘贴到编辑器中,并将其保存为ence_group .cpp(或者在这里下载源文件)。
??以下代码中的中文注释请仔细查看以便于理解整个程序内容。

#include <pcl/io/pcd_io.h>								// IO头文件用于传输pcd格式文件
#include <pcl/point_cloud.h>							// 点云头文件
#include <pcl/correspondence.h>							// 点,描述符等之间的匹配头文件
#include <pcl/features/normal_3d_omp.h>					// OMP加速计算点云法向
#include <pcl/features/shot_omp.h>						// 估计包含点和法线的给定点云数据集的方向直方图(SHOT)描述符的签名。
#include <pcl/features/board.h>
#include <pcl/filters/uniform_sampling.h>				// 均匀采样头文件
#include <pcl/recognition/cg/hough_3d.h>				// 三维霍夫变换头文件
#include <pcl/recognition/cg/geometric_consistency.h>	// 几何一致性头文件
#include <pcl/visualization/pcl_visualizer.h>			// 可视化头文件
#include <pcl/kdtree/kdtree_flann.h>					// kd树flann快速近邻搜索头文件
#include <pcl/kdtree/impl/kdtree_flann.hpp>
#include <pcl/common/transforms.h>
#include <pcl/console/parse.h>							// 命令行解析头文件

typedef pcl::PointXYZRGBA PointType; 					// 定义点类型为XYZRGBA
typedef pcl::Normal NormalType;							// 定义法向类型
typedef pcl::ReferenceFrame RFType;						// 定义参考系类型
typedef pcl::SHOT352 DescriptorType;					// 定义描述子类型为352字节SHOT

std::string model_filename_;							// 模型文件名,即milk_.pcd
std::string scene_filename_;							// 场景文件名,即milk_cartoon_all_small_clorox.pcd

// 算法相关参数初始化
bool show_keypoints_ (false);							// 不显示关键点
bool show_correspondences_ (false);						// 不显示描述符连线点
bool use_cloud_resolution_ (false);						// 不采用点云分辨率
bool use_hough_ (true);									// 采用霍夫变换
float model_ss_ (0.01f);								// 模型均匀采样半径(默认值0.01)
float scene_ss_ (0.03f);								// 场景均匀采样半径(默认值0.03)
float rf_rad_ (0.015f);									// 参考系半径(默认为0.015)
float descr_rad_ (0.02f);								// 描述符半径
float cg_size_ (0.01f);									// 聚类尺寸
float cg_thresh_ (5.0f);								// 聚类阈值
void
 showHelp (char *filename)
 {
   std::cout << std::endl;
   std::cout << "***************************************************************************" << std::endl;
   std::cout << "*                                                                         *" << std::endl;
   std::cout << "*             Correspondence Grouping Tutorial - Usage Guide              *" << std::endl;
   std::cout << "*                                                                         *" << std::endl;
   std::cout << "***************************************************************************" << std::endl << std::endl;
   std::cout << "Usage: " << filename << " model_filename.pcd scene_filename.pcd [Options]" << std::endl << std::endl;
   std::cout << "Options:" << std::endl;
   std::cout << "     -h:                     Show this help." << std::endl;
   std::cout << "     -k:                     Show used keypoints." << std::endl;
   std::cout << "     -c:                     Show used correspondences." << std::endl;
   std::cout << "     -r:                     Compute the model cloud resolution and multiply" << std::endl;
   std::cout << "                             each radius given by that value." << std::endl;
   std::cout << "     --algorithm (Hough|GC): Clustering algorithm used (default Hough)." << std::endl;
   std::cout << "     --model_ss val:         Model uniform sampling radius (default 0.01)" << std::endl;
   std::cout << "     --scene_ss val:         Scene uniform sampling radius (default 0.03)" << std::endl;
   std::cout << "     --rf_rad val:           Reference frame radius (default 0.015)" << std::endl;
   std::cout << "     --descr_rad val:        Descriptor radius (default 0.02)" << std::endl;
   std::cout << "     --cg_size val:          Cluster size (default 0.01)" << std::endl;
   std::cout << "     --cg_thresh val:        Clustering threshold (default 5)" << std::endl << std::endl;
 }
 
 void
 parseCommandLine (int argc, char *argv[]) // 解析命令行参数
 {
   //Show help
   if (pcl::console::find_switch (argc, argv, "-h"))
   {
     showHelp (argv[0]);
     exit (0);
   }
 
   //Model & scene filenames
   std::vector<int> filenames;
   filenames = pcl::console::parse_file_extension_argument (argc, argv, ".pcd");
   if (filenames.size () != 2)
   {
     std::cout << "Filenames missing.\n";
     showHelp (argv[0]);
     exit (-1);
   }
 
   model_filename_ = argv[filenames[0]];
   scene_filename_ = argv[filenames[1]];
 
   //-k显示用于计算对应的关键点,叠加到PCL可视化工具中,例如文末图片的蓝色点。
   //-c画了一条线连接每一对模型-场景的对应点的聚类过程。例如文末绿色线
   //-r估计模型点云的空间分辨率,然后将半径作为参数考虑,就像它们是以云分辨率的单位给出的一样;因此,当使用相同的命令行和不同的点云使用本例时,可以实现某种分辨率不变性。
   if (pcl::console::find_switch (argc, argv, "-k"))
   {
    show_keypoints_ = true;
   }
   if (pcl::console::find_switch (argc, argv, "-c"))
   {
     show_correspondences_ = true;
   }
   if (pcl::console::find_switch (argc, argv, "-r"))
   {
     use_cloud_resolution_ = true;
   }
 
   std::string used_algorithm;
   
// 可以通过命令行开关选择两种对应的聚类算法--algorithm (Hough|GC)
   if (pcl::console::parse_argument (argc, argv, "--algorithm", used_algorithm) != -1)
  {
  // Hough 聚类方法,可参考论文《Object recognition in 3D scenes with occlusions and clutter by Hough voting.》
    if (used_algorithm.compare ("Hough") == 0)
    {
      use_hough_ = true;
    }
  // GC 聚类方法是一种几何一致性聚类算法,增强了对应量之间的简单几何约束。具体可参考论文《基于局部表面补丁的距离图像三维自由形状目标识别》
    else if (used_algorithm.compare ("GC") == 0)
    {
      use_hough_ = false;
    }
    else
    {
      std::cout << "Wrong algorithm name.\n";
      showHelp (argv[0]);
      exit (-1);
    }
  }

  // 设置命令行通用参数
  pcl::console::parse_argument (argc, argv, "--model_ss", model_ss_);
  pcl::console::parse_argument (argc, argv, "--scene_ss", scene_ss_);
  pcl::console::parse_argument (argc, argv, "--rf_rad", rf_rad_);
  pcl::console::parse_argument (argc, argv, "--descr_rad", descr_rad_);
  pcl::console::parse_argument (argc, argv, "--cg_size", cg_size_);
  pcl::console::parse_argument (argc, argv, "--cg_thresh", cg_thresh_);
}

// 计算点云分辨率
// 对给定的点云执行空间分辨率计算,计算平均每个点与其最近邻居之间的距离。
double
computeCloudResolution (const pcl::PointCloud<PointType>::ConstPtr &cloud)
{
  double res = 0.0;
  int n_points = 0;
  int nres;
  std::vector<int> indices (2); // 点云索引
  std::vector<float> sqr_distances (2); // 点云距离
  pcl::search::KdTree<PointType> tree;
  tree.setInputCloud (cloud);

  for (std::size_t i = 0; i < cloud->size (); ++i)
  {
    if (! std::isfinite ((*cloud)[i].x))
    {
      continue;
    }
    //计算第二个邻点,因为第一个邻居就是点本身。
    nres = tree.nearestKSearch (i, 2, indices, sqr_distances);
    if (nres == 2)
    {
      res += sqrt (sqr_distances[1]);
      ++n_points;
    }
  }
    if (n_points != 0)
  {
    res /= n_points;
  }
  return res;
}

// 主程序
int
main (int argc, char *argv[])
{
  parseCommandLine (argc, argv);

  pcl::PointCloud<PointType>::Ptr model (new pcl::PointCloud<PointType> ());
  pcl::PointCloud<PointType>::Ptr model_keypoints (new pcl::PointCloud<PointType> ());
  pcl::PointCloud<PointType>::Ptr scene (new pcl::PointCloud<PointType> ());
  pcl::PointCloud<PointType>::Ptr scene_keypoints (new pcl::PointCloud<PointType> ());
  pcl::PointCloud<NormalType>::Ptr model_normals (new pcl::PointCloud<NormalType> ());
  pcl::PointCloud<NormalType>::Ptr scene_normals (new pcl::PointCloud<NormalType> ());
  pcl::PointCloud<DescriptorType>::Ptr model_descriptors (new pcl::PointCloud<DescriptorType> ());
  pcl::PointCloud<DescriptorType>::Ptr scene_descriptors (new pcl::PointCloud<DescriptorType> ());

  //
  //  加载模型点云
  //
  if (pcl::io::loadPCDFile (model_filename_, *model) < 0)
  {
    std::cout << "Error loading model cloud." << std::endl;
    showHelp (argv[0]);
    return (-1);
  }
  // 加载场景点云
    if (pcl::io::loadPCDFile (scene_filename_, *scene) < 0)
   {
    std::cout << "Error loading scene cloud." << std::endl;
    showHelp (argv[0]);
    return (-1);
  }

  //
  //  建立分辨率不变性。只有在命令行中启用了分辨率不变性标志时,程序才会调整将在接下来的部分中使用半径,方法是将它们乘以估计的模型云分辨率。
  //
  if (use_cloud_resolution_)
  {
    float resolution = static_cast<float> (computeCloudResolution (model));
    if (resolution != 0.0f)
    {
      model_ss_   *= resolution; // 相关参数都乘以分辨率
      scene_ss_   *= resolution;
      rf_rad_     *= resolution;
      descr_rad_  *= resolution;
      cg_size_    *= resolution;
    }

    std::cout << "Model resolution:       " << resolution << std::endl;
    std::cout << "Model sampling size:    " << model_ss_ << std::endl;
    std::cout << "Scene sampling size:    " << scene_ss_ << std::endl;
    std::cout << "LRF support radius:     " << rf_rad_ << std::endl;
    std::cout << "SHOT descriptor radius: " << descr_rad_ << std::endl;
    std::cout << "Clustering bin size:    " << cg_size_ << std::endl << std::endl;
  }

  //
  // 估计点云法向
  //
  pcl::NormalEstimationOMP<PointType, NormalType> norm_est;
  norm_est.setKSearch (10);
  norm_est.setInputCloud (model);
  norm_est.compute (*model_normals);

  norm_est.setInputCloud (scene);
  norm_est.compute (*scene_normals);

  //
  //  下采样点云提取关键点,以找到少量的关键点,这些关键点将被关联到一个3D描述符,以便执行关键点匹配和确定点对点对应。uniformsample使用的半径可以是命令行开关设置的半径,也可以是默认值。
  //

  pcl::UniformSampling<PointType> uniform_sampling; //均匀采样
  uniform_sampling.setInputCloud (model);
  uniform_sampling.setRadiusSearch (model_ss_); // 设置下采样半径
  uniform_sampling.filter (*model_keypoints);  
  std::cout << "Model total points: " << model->size () << "; Selected Keypoints: " << model_keypoints->size () << std::endl;

  uniform_sampling.setInputCloud (scene);
  uniform_sampling.setRadiusSearch (scene_ss_);
  uniform_sampling.filter (*scene_keypoints);
  std::cout << "Scene total points: " << scene->size () << "; Selected Keypoints: " << scene_keypoints->size () << std::endl;


  //
  //  计算关键点组成的SHOT描述符
  //
  pcl::SHOTEstimationOMP<PointType, NormalType, DescriptorType> descr_est;
  descr_est.setRadiusSearch (descr_rad_);

  descr_est.setInputCloud (model_keypoints);
  descr_est.setInputNormals (model_normals);
  descr_est.setSearchSurface (model);
  descr_est.compute (*model_descriptors);

  descr_est.setInputCloud (scene_keypoints);
  descr_est.setInputNormals (scene_normals);
  descr_est.setSearchSurface (scene);
  descr_est.compute (*scene_descriptors);

  //
  // 用KdTree找到模型-场景对应.
  // 将3D描述符与每个模型和场景关键点联系起来。使用SHOTEstimationOMP计算SHOT描述符。
  // 现在我们需要确定模型描述符和场景描述符之间的点对点对应关系。
  // 为此,程序使用KdTreeFLANN,该KdTreeFLANN的输入云被设置为包含模型描述符的云。对于每个描述符关联到一个场景关键点,它有效地找到最相似的模型描述符基于欧几里得距离,只有两个描述符足够相似,即他们的平方距离小于一个阈值,这里设置为0.25。
  //
 pcl::CorrespondencesPtr model_scene_corrs (new pcl::Correspondences ());

  pcl::KdTreeFLANN<DescriptorType> match_search;
  match_search.setInputCloud (model_descriptors);

  // 对于每个场景关键点描述符,在模型关键点描述符云中找到最近的邻居,并将其添加到对应向量中。
  for (std::size_t i = 0; i < scene_descriptors->size (); ++i)
  {
    std::vector<int> neigh_indices (1);
    std::vector<float> neigh_sqr_dists (1);
    if (!std::isfinite (scene_descriptors->at (i).descriptor[0])) //skipping NaNs
    {
      continue;
    }
    int found_neighs = match_search.nearestKSearch (scene_descriptors->at (i), 1, neigh_indices, neigh_sqr_dists);
    // 只有当平方描述符距离小于0.25时才添加match (SHOT描述符距离设计为0到1之间)
    if(found_neighs == 1 && neigh_sqr_dists[0] < 0.25f) 
    {
      pcl::Correspondence corr (neigh_indices[0], static_cast<int> (i), neigh_sqr_dists[0]);
      model_scene_corrs->push_back (corr);
    }
  }
  // 输出找到多少个匹配对儿
  std::cout << "Correspondences found: " << model_scene_corrs->size () << std::endl;

  //
  //  实际的聚类
  // 对之前发现的对应关系的关键点们进行实际的聚类。默认的算法是Hough3DGrouping,它是基于Hough投票过程的。
  // 请注意,这个算法需要为关键点所在云关联一个本地参考框架(LRF),这些关键点被作为参数传递!我们在调用聚类算法之前使用boardlocalreferenceframeestimate估计器显式地计算LRFs集。
  //
  std::vector<Eigen::Matrix4f, Eigen::aligned_allocator<Eigen::Matrix4f> > rototranslations;
  std::vector<pcl::Correspondences> clustered_corrs;

  //  使用Hough3D
  if (use_hough_)
  {
    //
    //  仅计算Hough的(关键点)参考帧
    //
    pcl::PointCloud<RFType>::Ptr model_rf (new pcl::PointCloud<RFType> ());
    pcl::PointCloud<RFType>::Ptr scene_rf (new pcl::PointCloud<RFType> ());

    pcl::BOARDLocalReferenceFrameEstimation<PointType, NormalType, RFType> rf_est;
    rf_est.setFindHoles (true);
    rf_est.setRadiusSearch (rf_rad_);

    rf_est.setInputCloud (model_keypoints);
    rf_est.setInputNormals (model_normals);
    rf_est.setSearchSurface (model);
    rf_est.compute (*model_rf);

    rf_est.setInputCloud (scene_keypoints);
    rf_est.setInputNormals (scene_normals);
    rf_est.setSearchSurface (scene);
    rf_est.compute (*scene_rf);

    //  聚类
    pcl::Hough3DGrouping<PointType, PointType, RFType, RFType> clusterer;
    clusterer.setHoughBinSize (cg_size_);
    clusterer.setHoughThreshold (cg_thresh_);
    clusterer.setUseInterpolation (true);
    clusterer.setUseDistanceWeight (false);

    clusterer.setInputCloud (model_keypoints);
    clusterer.setInputRf (model_rf);
    clusterer.setSceneCloud (scene_keypoints);
    clusterer.setSceneRf (scene_rf);
    clusterer.setModelSceneCorrespondences (model_scene_corrs);

    //clusterer.cluster (clustered_corrs);
    clusterer.recognize (rototranslations, clustered_corrs);
  }
  else // 除了使用Hough3DGrouping
  //通过之前描述的适当的命令行开关,也可以选择使用GeometricConsistencyGrouping算法。在这种情况下,不需要LRF计算,所以只是创建算法类的实例,传递正确的参数并调用识别方法。
  {
    pcl::GeometricConsistencyGrouping<PointType, PointType> gc_clusterer;
    gc_clusterer.setGCSize (cg_size_);
    gc_clusterer.setGCThreshold (cg_thresh_);

    gc_clusterer.setInputCloud (model_keypoints);
    gc_clusterer.setSceneCloud (scene_keypoints);
    gc_clusterer.setModelSceneCorrespondences (model_scene_corrs);

    //gc_clusterer.cluster (clustered_corrs);
    gc_clusterer.recognize (rototranslations, clustered_corrs);
  }

  //
  // 输出结果
  //
  std::cout << "Model instances found: " << rototranslations.size () << std::endl;
  for (std::size_t i = 0; i < rototranslations.size (); ++i)
  {
    std::cout << "\n    Instance " << i + 1 << ":" << std::endl;
    std::cout << "        Correspondences belonging to this instance: " << clustered_corrs[i].size () << std::endl;

    // 打印出识别到的物体旋转和平移矩阵
    Eigen::Matrix3f rotation = rototranslations[i].block<3,3>(0, 0);
    Eigen::Vector3f translation = rototranslations[i].block<3,1>(0, 3);

    printf ("\n");
    printf ("            | %6.3f %6.3f %6.3f | \n", rotation (0,0), rotation (0,1), rotation (0,2));
    printf ("        R = | %6.3f %6.3f %6.3f | \n", rotation (1,0), rotation (1,1), rotation (1,2));
    printf ("            | %6.3f %6.3f %6.3f | \n", rotation (2,0), rotation (2,1), rotation (2,2));
    printf ("\n");
    printf ("        t = < %0.3f, %0.3f, %0.3f >\n", translation (0), translation (1), translation (2));
  }

  //
  //  可视化结果
  //  最后程序在PCLVisualizer窗口中显示场景云,其中有一个红色的覆盖层和有一个模型的实例。如果命令行-k和-c已经被使用,程序还会显示模型云的独立呈现。如果启用了关键点可视化,关键点将显示为蓝点,如果启用了对应线可视化,则它们将显示为绿线,表示聚类过程中对应点的关系。
  //
  pcl::visualization::PCLVisualizer viewer ("Correspondence Grouping");
  viewer.addPointCloud (scene, "scene_cloud");

  pcl::PointCloud<PointType>::Ptr off_scene_model (new pcl::PointCloud<PointType> ());
  pcl::PointCloud<PointType>::Ptr off_scene_model_keypoints (new pcl::PointCloud<PointType> ());

  if (show_correspondences_ || show_keypoints_)
  {
    //  We are translating the model so that it doesn't end in the middle of the scene representation
    pcl::transformPointCloud (*model, *off_scene_model, Eigen::Vector3f (-1,0,0), Eigen::Quaternionf (1, 0, 0, 0));
    pcl::transformPointCloud (*model_keypoints, *off_scene_model_keypoints, Eigen::Vector3f (-1,0,0), Eigen::Quaternionf (1, 0, 0, 0));

    pcl::visualization::PointCloudColorHandlerCustom<PointType> off_scene_model_color_handler (off_scene_model, 255, 255, 128);
    viewer.addPointCloud (off_scene_model, off_scene_model_color_handler, "off_scene_model");
  }

  if (show_keypoints_)
  {
    pcl::visualization::PointCloudColorHandlerCustom<PointType> scene_keypoints_color_handler (scene_keypoints, 0, 0, 255);
    viewer.addPointCloud (scene_keypoints, scene_keypoints_color_handler, "scene_keypoints");
    viewer.setPointCloudRenderingProperties (pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 5, "scene_keypoints");

    pcl::visualization::PointCloudColorHandlerCustom<PointType> off_scene_model_keypoints_color_handler (off_scene_model_keypoints, 0, 0, 255);
    viewer.addPointCloud (off_scene_model_keypoints, off_scene_model_keypoints_color_handler, "off_scene_model_keypoints");
    viewer.setPointCloudRenderingProperties (pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 5, "off_scene_model_keypoints");
  }

  for (std::size_t i = 0; i < rototranslations.size (); ++i)
  {
    pcl::PointCloud<PointType>::Ptr rotated_model (new pcl::PointCloud<PointType> ());
    pcl::transformPointCloud (*model, *rotated_model, rototranslations[i]);

    std::stringstream ss_cloud;
    ss_cloud << "instance" << i;

    pcl::visualization::PointCloudColorHandlerCustom<PointType> rotated_model_color_handler (rotated_model, 255, 0, 0);
    viewer.addPointCloud (rotated_model, rotated_model_color_handler, ss_cloud.str ());

    if (show_correspondences_)
    {
      for (std::size_t j = 0; j < clustered_corrs[i].size (); ++j)
      {
        std::stringstream ss_line;
        ss_line << "correspondence_line" << i << "_" << j;
        PointType& model_point = off_scene_model_keypoints->at (clustered_corrs[i][j].index_query);
        PointType& scene_point = scene_keypoints->at (clustered_corrs[i][j].index_match);

        //  为在模型和场景之间发现的每一对对应点画一条线
        viewer.addLine<PointType, PointType> (model_point, scene_point, 0, 255, 0, ss_line.str ());
      }
    }
  }

  while (!viewer.wasStopped ())
  {
    viewer.spinOnce ();
  }

  return (0);
}

注意:在调用聚类算法之前,不需要显式计算LRFs。如果取到聚类算法的云没有关联一组lrf, Hough3DGrouping在进行聚类之前会自动计算出它们。特别是,在不设置LRF的情况下调用recognition(或cluster)方法时会发生这种情况:在这种情况下,需要将LRF的半径指定为集群算法的附加参数(使用setLocalRfSearchRadius方法)。

??程序运行结果:??

在这里插入图片描述
??使用 -k 的效果,蓝色点为计算出的关键点:

在这里插入图片描述
??使用-c的结果,绿色的线是聚类后对应关键点的连线。

在这里插入图片描述
??同时使用 -k 和 -c 的结果:

在这里插入图片描述


【博主简介】
??斯坦福的兔子,男,天津大学机械工程工学硕士。毕业至今从事光学三维成像及点云处理相关工作。因工作中使用的三维处理库为公司内部库,不具有普遍适用性,遂自学开源PCL库及其相关数学知识以备使用。谨此将自学过程与君共享。
博主才疏学浅,尚不具有指导能力,如有问题还请各位在评论处留言供大家共同讨论。
若前辈们有工作机会介绍欢迎私信。

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-02-28 15:50:37  更:2022-02-28 15:52:02 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 16:51:22-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码