数据结构—单链表

1、链表的概念及结构

1.1链表的概念

链表是一种物理存储结构上非连续、非顺序的存储结构,但在逻辑上确是连续、顺序的,而数据元素的逻辑顺序是通过链表中的指针链接次序实现的。

1.2链表的结构

如下图:

逻辑上的链表,pList是指向头个元素的指针 

物理上的链表

有人可能会有疑问,不是说链表只是在逻辑结构上是连续的,在物理存储结构上是不连续的,那为什么上图中一个个结点明明是挨在一起的,那么它在物理存储结构上肯定是连续的呀,其实不然,上图是为了方便大家理解,才用线条连接了结点,实际上在内存中,每个结点可能会隔得很远,仔细观察每个结点上面的红色文字,那就是这个结点的地址,而蓝色文字是下一个结点的地址,很明显能看到这两个结点并不是相邻的,因此也验证了顺序表在逻辑结构上确实是连续的,但在物理存储结构上确实是不连续的。

2、链表的实现

2.1 定义结点

介绍一下单链表的英文名——single linked list,我们简写成SL(区别于顺序表的SeqList或者SQL)。

注意:next指针的类型是SListNode*,千万不要写成SLTDataType*,因为next指针是结构体指针,是用来指向下一个结点的

typedef int SLTDataType;
typedef struct SListNode
{SLTDataType data;struct SListNode* next;
}SLTNode;

2.2链表的功能

链表要实现那些功能呢?其实这些功能我们都很熟悉,数据结构无非是对数据进行管理,要实现数据的增删查改,因此链表的基本功能也都是围绕着数据的增删查改展开。

注意:链表是不需要初始化的

//打印单链表
void SLTPrint(SLTNode* phead);
//创建一个结点
SLTNode* BuyLTNode(SLTDataType x);
//销毁单链表
void SLTDestroy(SLTNode** pphead);
//头插
void SLPushFront(SLTNode** pphead, SLTDataType x);
//尾插
void SLPushBack(SLTNode** pphead, SLTDataType x);
//头删
void SLPopFront(SLTNode** pphead);
//尾删
void SLPopBack(SLTNode** pphead);
// 单链表查找
SLTNode* STFind(SLTNode* phead, SLTDataType x);// 在pos之前插入
void SLInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
//在pos之后插入
void SLInsertAfter(SLTNode* pos, SLTDataType x);// 删除pos位置的值
void SLErase(SLTNode** pphead, SLTNode* pos);// 删除pos位置后面的值
void SLEraseAfter(SLTNode* pos);// 单链表结点修改
void SLTModify(SLTNode* phead, SLTNode* pos, SLTDataType x);

2.3链表功能实现

2.3.1打印单链表

注意:链表和顺序不同的是,顺序表传过来的指针是肯定不会为空的,而链表传过来的指针是可能为空的,比如说当链表中没有元素时,头指针所指向的就是NULL,如果在第一行写上断言就会有问题。

当cur指向空的时候就可以停止打印了。

void SLTPrint(SLTNode* phead)
{SLTNode* cur=phead;while(cur!=NULL){printf("%d->",cur->data);cur=cur->next;}printf("NULL\n");}

2.3.2 创建一个新结点

后面我们要在单链表中进行头插和尾插,此时插入的不再是像顺序表一样简单的SLDataType数据了,而是一个结点,这个结点是包括SLTDataType数据以及SLTDataType*的指针,因此,为了方便和减少代码的重复度,我们另写一个函数用来专门创建新结点

SLTNode* BuyLTNode(SLTDataType x)
{SLTNode * newnode=(SLTNode*)malloc(sizeof(SLTNode));if(newnode==NULL){perror("malloc fail");return NULL;}newnode->data=x;newnode->next=NULL;//初始化return newnode;
}

2.3.3 单链表尾插

注意:在创建结点时,已经让 结点.next=NULL,所以不需要在插入完结点后,再让新结点的next指针为NULL。

有人可能会有疑问,为什么之前打印链表的时候不用断言指针,而在尾插时就要断言指针,以及为什么函数的形参是二级指针,而不使用一级指针。

因为,尾插分为两种情况(下面有写),当链表为空时,头指针phead指向NULL,尾插相当于头插,此时要改变phead的指向,让phead指向这个新结点,此时就需要二级指针来改变一级指针的值(如果我们用一级指针做形参,形参的改变不会影响实参,那么一级指针phead就不会被改变)。

至于这个什么时候要断言指针,什么时候不用断言指针:一级指针也就是phead,当链表为空的时候,phead就是为NULL,而二级指针永远指向phead,phead的地址是永远存在的,那么pphead就一定不可能为空,所以需要断言pphead。

//尾插
//第一个尾插需要二级指针,接下来就不用二级指针
//第一次是改变结构的指针plist
//第二次是改变结构体尾结点
void SLPushBack(SLTNode** pphead, SLTDataType x)
{SLTNode *newnode= BuyLTNode(x);//是否为空链表if(*pphead==NULL)*pphead=newnode;//改变结构的指针plistelse{SLTNode *tail=*pphead;while(tail->next!=NULL)//找到链表最后一位,当tail->为空时,就把新开辟的链表节点接上去tail=tail->next;tail->next=newnode;//改变结构体尾结点//就是把newnode存放的新结点地址给tail->next,然后在存放在之前最后一个结点的struct SListNode* next;中,这样next指向的地址不是NULL,而是新加的结点的位置//不能用tail=newnode,因为tail和newnode是局部变量,当这函数结束后就释放了}
}

2.2.4 单链表尾删

要想删除链表中的元素,就必须保证原链表就有元素,因此要断言assert(*pphead)

尾删需要分情况去讨论

//尾删
void SLPopBack(SLTNode** pphead)
{//当没有结点(空链表)//暴力检查assert(*pphead);//温柔检查
//    if(*pphead==NULL)
//        return;//当只有一个结点时if((*pphead)->next==NULL){free(*pphead);*pphead=NULL;}else{SLTNode *prev = NULL;//用来指向倒数第二个结点SLTNode *tail = *pphead;//用来释放最后一个指针空间//找尾while (tail->next) {prev = tail;tail = tail->next;}free(tail);//把最后一个指针空间释放掉prev->next = NULL;//把最后一个的前一个结点指针设为空//如果直接tail=NULL的话,倒数第二个结点就指向一个NULL,就成为了一个野指针//while(tail->next->next)//找到倒数第二个//    tail=tail->next;//free(tail->next);//把倒数第二个结点指向的结构体释放掉//tail->next=NULL;//把倒数第二个结点置为空}
}

2.2.5  单链表头删

头删没什么好说的,记住要断言*pphead,保证链表内容不为空。

//头删
void SLPopFront(SLTNode** pphead)
{//空链表assert(*pphead);//    //一个结点
//    if((*pphead)->next==NULL)
//    {
//        free(*pphead);
//        *pphead=NULL;
//    }
//    //多个结点
//    else
//    {
//        SLTNode *del=*pphead;
//        //*pphead=del->nest;
//        *pphead=(*pphead)->next;
//        free(del);
//    }SLTNode* del = *pphead;*pphead = (*pphead)->next;free(del);}

 2.2.6 单链表头插

现在是不是觉得头插很简单了啊!

//头插
void SLPushFront(SLTNode** pphead, SLTDataType x)
{SLTNode *newnode= BuyLTNode(x);newnode->next= *pphead;*pphead=newnode;//都需要二级指针
}

2.2.7 查找某个结点

这个函数返回值不再是void,而是SLTNode*,把找到的结点的地址返回去,这个函数一般会跟结点修改之类的函数一起使用。

SLTNode* STFind(SLTNode* phead, SLTDataType x)
{SLTNode *cur=phead;while (cur){if(cur->data==x){return cur;}cur=cur->next;}return NULL;
}

2.2.8 删除某个节点

// 删除pos位置的值
void SLErase(SLTNode** pphead, SLTNode* pos)
{assert(pphead);assert(pos);//如果只有一个结点if(pos==*pphead)SLPopFront(pphead);else{SLTNode *p1=*pphead;while(p1->next!=pos)p1=p1->next;p1->next=pos->next;free(pos);}
}

注意:

  1. pos也要断言,pos可不能为空呀!
  2. cur->next也要断言,因为当cur->next为NULL时,说明整个链表的结点都排查完了,最后还是没有找到地址为pos的结点,证明pos传值有误。

 2.2.9单链表结点修改

// 单链表结点修改
void SLTModify(SLTNode* phead, SLTNode* pos, SLTDataType x)
{SLTNode* cur=phead;while(cur!=pos){cur=cur->next;assert(cur);}pos->data=x;
}

2.2.10 单链表在pos位前插入结点

// 在pos之前插入
void SLInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{assert(pphead);assert(pos);//如果只有一个结点if(*pphead==pos)SLPushFront(pphead,x);else{SLTNode *p1=*pphead;while(p1->next!=pos){p1=p1->next;}SLTNode *newnode= BuyLTNode(x);p1->next=newnode;newnode->next=pos;}
}

2.2.11单链表在pos位后插入结点

//在pos之后插入
void SLInsertAfter(SLTNode* pos, SLTDataType x)
{assert(pos);SLTNode *newnode= BuyLTNode(x);newnode->next=pos->next;pos->next=newnode;
}

2.2.12销毁链表

销毁链表这一块,咱可不敢直接free(phead),因为链表在物理结构上是不连续存储的,销毁链表必须要一个结点一个结点去销毁!!!!最后不要忘记把phead置为NULL。

//单链表销毁
void SListDesTroy(SLTNode** pphead)
{assert(pphead && *pphead );SLTNode *pcur=*pphead;while(pcur){SLTNode *next=pcur->next;free(pcur);pcur=next;}*pphead=NULL;}

3、总代码

3.1 SList.h

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>typedef int SLTDataType;
typedef struct SListNode
{SLTDataType data;struct SListNode* next;
}SLTNode;void SLTPrint(SLTNode* phead);
//头插
void SLPushFront(SLTNode** pphead, SLTDataType x);
//尾插
void SLPushBack(SLTNode** pphead, SLTDataType x);
//头删
void SLPopFront(SLTNode** pphead);
//尾删
void SLPopBack(SLTNode** pphead);
// 单链表查找
SLTNode* STFind(SLTNode* phead, SLTDataType x);// 在pos之前插入
void SLInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
//在pos之后插入
void SLInsertAfter(SLTNode* pos, SLTDataType x);// 删除pos位置的值
void SLErase(SLTNode** pphead, SLTNode* pos);// 删除pos位置后面的值
void SLEraseAfter(SLTNode* pos);//单链表销毁
void SListDesTroy(SLTNode** pphead);// 单链表结点修改
void SLTModify(SLTNode* phead, SLTNode* pos, SLTDataType x);

3.2 SList.c

#include "SList.h"void SLTPrint(SLTNode* phead)
{SLTNode* cur=phead;while(cur!=NULL){printf("%d->",cur->data);cur=cur->next;}printf("NULL\n");}//创建一个新的动态内存
SLTNode* BuyLTNode(SLTDataType x)
{SLTNode * newnode=(SLTNode*)malloc(sizeof(SLTNode));if(newnode==NULL){perror("malloc fail");return NULL;}newnode->data=x;newnode->next=NULL;//初始化return newnode;
}//头插
void SLPushFront(SLTNode** pphead, SLTDataType x)
{SLTNode *newnode= BuyLTNode(x);newnode->next= *pphead;*pphead=newnode;//都需要二级指针
}//尾插
//第一个尾插需要二级指针,接下来就不用二级指针
//第一次是改变结构的指针plist
//第二次是改变结构体尾结点
void SLPushBack(SLTNode** pphead, SLTDataType x)
{SLTNode *newnode= BuyLTNode(x);//是否为空链表if(*pphead==NULL)*pphead=newnode;//改变结构的指针plistelse{SLTNode *tail=*pphead;while(tail->next!=NULL)//找到链表最后一位,当tail->为空时,就把新开辟的链表节点接上去tail=tail->next;tail->next=newnode;//改变结构体尾结点//就是把newnode存放的新结点地址给tail->next,然后在存放在之前最后一个结点的struct SListNode* next;中,这样next指向的地址不是NULL,而是新加的结点的位置//不能用tail=newnode,因为tail和newnode是局部变量,当这函数结束后就释放了}
}//头删
void SLPopFront(SLTNode** pphead)
{//空链表assert(*pphead);//    //一个结点
//    if((*pphead)->next==NULL)
//    {
//        free(*pphead);
//        *pphead=NULL;
//    }
//    //多个结点
//    else
//    {
//        SLTNode *del=*pphead;
//        //*pphead=del->nest;
//        *pphead=(*pphead)->next;
//        free(del);
//    }SLTNode* del = *pphead;*pphead = (*pphead)->next;free(del);}//尾删
void SLPopBack(SLTNode** pphead)
{//当没有结点(空链表)//暴力检查assert(*pphead);//温柔检查
//    if(*pphead==NULL)
//        return;//当只有一个结点时if((*pphead)->next==NULL){free(*pphead);*pphead=NULL;}else{SLTNode *prev = NULL;//用来指向倒数第二个结点SLTNode *tail = *pphead;//用来释放最后一个指针空间//找尾while (tail->next) {prev = tail;tail = tail->next;}free(tail);//把最后一个指针空间释放掉prev->next = NULL;//把最后一个的前一个结点指针设为空//如果直接tail=NULL的话,倒数第二个结点就指向一个NULL,就成为了一个野指针//while(tail->next->next)//找到倒数第二个//    tail=tail->next;//free(tail->next);//把倒数第二个结点指向的结构体释放掉//tail->next=NULL;//把倒数第二个结点置为空}
}// 单链表查找
SLTNode* STFind(SLTNode* phead, SLTDataType x)
{SLTNode *cur=phead;while (cur){if(cur->data==x){return cur;}cur=cur->next;}return NULL;
}// 在pos之前插入
void SLInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{assert(pphead);assert(pos);//如果只有一个结点if(*pphead==pos)SLPushFront(pphead,x);else{SLTNode *p1=*pphead;while(p1->next!=pos){p1=p1->next;}SLTNode *newnode= BuyLTNode(x);p1->next=newnode;newnode->next=pos;}
}//在pos之后插入
void SLInsertAfter(SLTNode* pos, SLTDataType x)
{assert(pos);SLTNode *newnode= BuyLTNode(x);newnode->next=pos->next;pos->next=newnode;
}// 删除pos位置的值
void SLErase(SLTNode** pphead, SLTNode* pos)
{assert(pphead);assert(pos);//如果只有一个结点if(pos==*pphead)SLPopFront(pphead);else{SLTNode *p1=*pphead;while(p1->next!=pos)p1=p1->next;p1->next=pos->next;free(pos);}
}// 删除pos位置后面的值
void SLEraseAfter(SLTNode* pos)
{assert(pos);assert(pos->next);SLTNode *newnode=pos->next;pos->next=newnode->next;free(newnode);
}//单链表销毁
void SListDesTroy(SLTNode** pphead)
{assert(pphead && *pphead );SLTNode *pcur=*pphead;while(pcur){SLTNode *next=pcur->next;free(pcur);pcur=next;}*pphead=NULL;}
// 单链表结点修改
void SLTModify(SLTNode* phead, SLTNode* pos, SLTDataType x)
{SLTNode* cur=phead;while(cur!=pos){cur=cur->next;assert(cur);}pos->data=x;
}

3.3 text.c

测试函数可以根据大家的想法进行测试,下面是我的测试函数以及输出情况

#include"SList.h"void TestSList1()
{SLTNode* plist = NULL;SLPushFront(&plist, 1);SLPushFront(&plist, 2);SLPushFront(&plist, 3);SLPushFront(&plist, 4);SLTPrint(plist);
}void TestSList2()
{SLTNode *plist=NULL;SLPushFront(&plist, 1);SLPushFront(&plist, 2);SLPushFront(&plist, 3);SLPushFront(&plist, 4);SLPushBack(&plist, 5);SLPushBack(&plist, 6);SLPushBack(&plist, 7);SLPushBack(&plist, 8);SLTPrint(plist);SLTNode *pos= STFind(plist,3);if(pos){SLInsert(&plist,pos,30);}SLTPrint(plist);pos= STFind(plist,6);if(pos){SLInsertAfter(plist,88);}SLTPrint(plist);pos= STFind(plist,7);if(pos){SLErase(&plist,pos);}SLTPrint(plist);pos= STFind(plist,1);if(pos){SLEraseAfter(pos);}SLTPrint(plist);SListDesTroy(&plist);
}void Swapi(int* p1, int* p2)
{int tmp = *p1;*p1 = *p2;*p2 = tmp;
}void Swappi(int** pp1, int** pp2)
{int* tmp = *pp1;*pp1 = *pp2;*pp2 = tmp;
}int main()
{
//    TestSList1();TestSList2();
//    int a = 0, b = 1;
//    Swapi(&a, &b);
//
//    int* px = &a, * py = &b;
//    Swappi(&px, &py);return 0;
}

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

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

相关文章

中断的设备树修改及上机实验(按键驱动)流程

写在前面的话&#xff1a;对于 GPIO 按键&#xff0c;我们并不需要去写驱动程序&#xff0c;使用内核自带的驱动程序 drivers/input/keyboard/gpio_keys.c 就可以&#xff0c;然后你需要做的只是修改设备树指定引脚及键值。 根据驱动文件中的platform_driver中的.of_match_tabl…

gemini国内能用吗

gemini国内能用吗 虽然 Gemini 的具体功能和性能还未完全公开&#xff0c;但基于 Google 在 AI 领域的强大背景和技术实力&#xff0c;已经火出圈了&#xff0c;很多小伙伴已经迫不及待想了解一下它有什么优势以及如何快速使用上 首先我们来讲一下gemini的优势 多模态能力&a…

Springboot配置文件(application.yml)的加载顺序

spring boot 启动会扫描一下位置的application.properties或者application.yml文件作为Spring boot的默认配置文件 file…/config/ file…/ classpath:/config classpath:/ 以上是按照优先级从高到低的顺序&#xff0c;所有位置的文件都会被加载&#xff0c;高优先级配置内容会…

C++-命名空间

C 命名空间是一种用于组织代码的机制&#xff0c;可以帮助避免命名冲突&#xff0c;提高代码的可读性和可维护性。命名空间将代码分组到逻辑单元中&#xff0c;允许在不同的代码单元中使用相同的名称而不会产生冲突。 命名空间通过将代码放置在一个命名空间内部来实现。在 C 中…

算法-反转单向链表

需求 思路 链表必有节点&#xff0c;节点两要素&#xff1a;当前元素值&#xff0c;下一个节点地址 import java.util.Scanner;// 定义一个单向链表 public class MyLinkedList<E> {int size 0;// 顶一个私有的内部类&#xff0c;表示链表的节点public class Node {E da…

JavaSE 有这一篇就够(呕心狂敲41k字,只为博君一点赞!)

目录 一. 基础语法 1. 数据类型 2. 基本数据类型转换 3. 运算符 3. 循环语句 5. 定义方法 6. 数组 二. 面向对象 1. 类和对象 2. 构造方法 3. 方法的重载 4. this关键字 5. static关键字 6. 代码块 7. 访问权限修饰符 8. 面向对象的三大特征 封装 继承…

你会写SAP技术规格说明书(Specification)吗

有些小伙伴可能还在发愁技术规则说明书应该写什么&#xff0c;做了张思维导图&#xff0c;包含了所有RICEFW。 R - Report - 报表 I - Interface - 接口 C - Conversion - 数据转换 E - Enhancement - 增强 F - Form - 表单 W - Workflow - 工作流

【机器学习】数据变换---小波变换特征提取及应用案列介绍

引言 在机器学习领域&#xff0c;数据变换是一种常见且重要的预处理步骤。通过对原始数据进行变换&#xff0c;我们可以提取出更有意义的特征&#xff0c;提高模型的性能。在众多数据变换方法中&#xff0c;小波变换是一种非常有效的方法&#xff0c;尤其适用于处理非平稳信号和…

OpenHarmony网络协议通信c-ares [交叉编译]异步解析器库

简介 c-ares是异步解析器库&#xff0c;适用于需要无阻塞地执行 DNS 查询或需要并行执行多个 DNS 查询的应用程序。 下载安装 直接在OpenHarmony-SIG仓中搜索c-ares并下载。 使用说明 以OpenHarmony 3.1 Beta的rk3568版本为例 将下载的c-ares库代码存在以下路径&#xff1a;…

AI大模型日报#0416:李飞飞《2024年人工智能指数报告》、Sora加入Adobe、李彦宏聊百度大模型之路

​导读&#xff1a; 欢迎阅读《AI大模型日报》&#xff0c;内容基于Python爬虫和LLM自动生成。目前采用“文心一言”生成了每条资讯的摘要。标题: 刚刚&#xff0c;李飞飞团队发布《2024年人工智能指数报告》&#xff1a;10大趋势&#xff0c;揭示AI大模型的“喜”与“忧” 摘…

代码随想录打卡—day27—【回溯】— 回溯基础练习 4.15+4.16

1 39. 组合总和 39. 组合总和 我的AC代码&#xff1a; class Solution { public:vector<vector<int>> ans;vector<int> path;void dfs(int sum,vector<int>& candidates,int target,int start_idx){if(sum > target)return;if(sum target){a…

将 Notepad++ 添加到右键菜单

目录 方式一&#xff1a;添加注册表&#xff08;手动&#xff09; 方式二&#xff1a;添加注册表&#xff08;一键添加&#xff09; 有时安装了notepad后&#xff0c;在txt文件上右键&#xff0c;在弹出的菜单栏中没有【通过 Notepad 打开】&#xff0c;如下&#xff1a; 这…

【面经】2024春招-云计算后台研发工程师1(3个问题,移动TW等)

【面经】2024春招-云计算后台研发工程师1&#xff08;3个问题&#xff0c;移动&TW等&#xff09; 文章目录 岗位与面经基础1&#xff1a;数据库 & 网络&#xff08;3个问题&#xff09;基础2&#xff1a;系统 & 语法模板3&#xff1a;算法 & 项目&#xff08;移…

[StartingPoint][Tier2]Base

Task 1 Which two TCP ports are open on the remote host? (远程服务器开放了哪两个TCP端口?) $ nmap -sC -sV 10.129.234.232 22,80 Task 2 What is the relative path on the webserver for the login page? (相关的登录页面路径是什么?) /login/login.php Task 3 …

欢乐钓鱼大师加速、暴击内置脚本,直接安装

无需手机root,安装软件即可使用&#xff0c;仅限安卓。 网盘自动获取 链接&#xff1a;https://pan.baidu.com/s/1lpzKPim76qettahxvxtjaQ?pwd0b8x 提取码&#xff1a;0b8x

指数平滑算法介绍及代码实现

一、一阶指数平滑 一阶指数平滑&#xff0c;也称为一次指数平滑或简单指数平滑&#xff08;Simple Exponential Smoothing, SES&#xff09;&#xff0c;是时间序列预测中的一种方法。这种方法适用于没有明显趋势和季节性成分的时间序列数据。一阶指数平滑的基本思想是最近的观…

UUPSUpgradeable部署合约和升级合约

文章目录 写一个合约1. 使用代理部署 并添加拥有者2. 没有name number为103. 使用代理升级部署 填写上面代理的合约地址4. 合约地址没有变&#xff0c;但是添加了name&#xff0c;并且保存了number的属性值 写一个合约 // SPDX-License-Identifier: MIT // Compatible with Op…

《Chain-of-Thought Prompting Elicits Reasoning in Large Language Models》【大模型思维链】

目录 前言一、思维链介绍1-1、指令1-2、逻辑依据1-3、示例 二、Cot一般分类2-1、Zero-Shot-CoT2-2、Few-Shot-CoT 三、Cot的好处&缺陷&适用3-1、Cot的好处3-2、Cot的缺陷3-3、Cot的适用 四、变体4-1、自我验证&#xff08;self-consistency checking&#xff09; 总结 …

freemarker实现代码生成器

这个主要是黑马的ihrm课程中讲的代码生成器的部分内容。 文章目录 应用场景第一个FreeMarker程序&#xff08;数据模板 文件输出&#xff09; 概述数据模型模板的常用标签模板的常用指令 元数据数据库元数据参数元数据结果集元数据 代码生成器思路分析搭建环境导入坐标配置实体…

CloudCompare——体元法计算树冠体积

目录 1.概述2.软件实现3.完整操作4.相关代码 本文由CSDN点云侠原创&#xff0c;CloudCompare——体元法计算树冠体积&#xff0c;爬虫自重。如果你不是在点云侠的博客中看到该文章&#xff0c;那么此处便是不要脸的爬虫与GPT生成的文章。 1.概述 体元法将树冠所在的空间范围划…