【C语言】huffman编码实现数据压缩

目录

  • 原理
  • 类型定义
  • 完整代码
  • 实验
    • 无重复数据的压缩情况
    • 有重复数据的压缩情况
    • 数据中只有一种字符的情况

原理

huffman统计数据中字符的出现次数,根据每个字符的出现次数来编码,出现次数越多的数据使用越短的编码长度,从而实现数据压缩的目的。

类型定义

定义Huffman树节点类型和Huffman结构体,由于一个字节最多可以表示256种数据,index_table和count_table长度最大设置为256即可满足所有数据的压缩。

typedef struct _huff_tree{uint8_t data;uint8_t pos;// 位置,左为1,右为0uint32_t count;struct _huff_tree *parant;struct _huff_tree *left;struct _huff_tree *right;
}huff_tree;typedef struct{huff_tree *tree;uint32_t index_table_index;huff_tree *index_table[256];uint32_t count_table[256];uint8_t *out;uint32_t out_len;const uint8_t *in;uint32_t in_len;uint32_t in_bit_count;uint32_t arr_bit_index;/* 以下成员调试时使用 */uint32_t tree_point_num;// 使用的树节点个数
}huffman_def;

完整代码

头文件定义如下:

#ifndef huffman_h__
#define huffman_h__#include "stdint.h"// huffman编码的实现
// out使用之后需要free内存
int hm_encode(const uint8_t *in,const int in_len,uint8_t **out,int *out_len);// huffman解码
// out使用之后需要free内存
int hm_decode(const uint8_t* in, const int in_len, uint8_t** out, int* out_len);#endif

源文件定义如下:


#include "stdlib.h"
#include "stdio.h"
#include "string.h"
#include "huffman_.h"
#include <stdlib.h>
// huffman编码的实现#define DBG_WARN printf
#define DBG_LOG printftypedef struct _huff_tree{uint8_t data;uint8_t pos;// 位置,左为1,右为0uint32_t count;struct _huff_tree *parant;struct _huff_tree *left;struct _huff_tree *right;
}huff_tree;typedef struct{huff_tree *tree;uint32_t index_table_index;huff_tree *index_table[256];uint32_t count_table[256];uint8_t *out;uint32_t out_len;const uint8_t *in;uint32_t in_len;uint32_t in_bit_count;uint32_t arr_bit_index;/* 以下成员调试时使用 */uint32_t tree_point_num;// 使用的树节点个数
}huffman_def;static int hm_calc_value_of_tree(huff_tree *t);
static int hm_calc_deep_of_child(huff_tree* t);// 生成一个树节点
static huff_tree *hm_creat_tree_point(huffman_def *h)
{h->tree_point_num++;return calloc(1,sizeof(huff_tree));
}// 删除一个树节点
static void hm_del_tree_point(huffman_def *h,huff_tree *t)
{if(h->tree_point_num>0){h->tree_point_num--;free(t);}
}// 按出现频次排序
static void hm_sort_index_table(huff_tree **table,int num)
{for(int i=0;i<num;i++){huff_tree *item=table[i];for (int j=i;j<num;j++){if(hm_calc_value_of_tree(table[j])>hm_calc_value_of_tree(item)){table[i]=table[j];table[j]=item;item=table[i];}}}
}// 打印index_table
static void hm_index_table_print(huffman_def *h){DBG_LOG("-----index_table-----\n");for(int i=0;i<h->index_table_index;i++){DBG_LOG("index:%d,data:%02x,count:%d\n",i,h->index_table[i]->data,h->index_table[i]->count);}
}// 打印数据的编码
static void hm_data_code_print(huffman_def *h){huff_tree *t;DBG_LOG("------data code------\n");for(int i=0;i<h->index_table_index;i++){t=h->index_table[i];DBG_LOG("%c:",t->data);while(t->parant){DBG_LOG("%d",t->pos);t=t->parant;}DBG_LOG("\n");}
}static void hm_calc_count(huffman_def *h,const uint8_t *d,const int d_len)
{int num = d_len;int index;memset(h->count_table,0,256);// DBG_LOG("calc count_table\n");for(int i=0;i<num;i++){h->count_table[d[i]]++;}// DBG_LOG("calc index_table\n");for(int i=0;i<256;i++){if(h->count_table[i]>0){index=h->index_table_index;h->index_table[index]=hm_creat_tree_point(h);h->index_table[index]->count=h->count_table[i];h->index_table[index]->data=i;h->index_table_index++;}}// DBG_LOG("sort index_table\n");hm_sort_index_table(h->index_table,h->index_table_index);// hm_index_table_print(h);
}// 计算编码后的长度
// 需要先计算index_table和生成huffman树
static int hm_calc_encode_len(huffman_def* h)
{// index_table_len(1byte)+index_data(index_table_len bytes)int sum =1+ h->index_table_index;int bit_count = 0;huff_tree* t;for (int i = 0; i < h->index_table_index; i++) {// 计数占用的字节数t = h->index_table[i];sum += t->count/255+1;// 压缩后占用的bit数bit_count += hm_calc_deep_of_child(t) * t->count;}// 补零数目字节sum += 1;sum += (bit_count + 7) / 8;DBG_LOG("data len for encode:%d\n", sum);return sum;
}// 计算树的值
static int hm_calc_value_of_tree(huff_tree *t)
{int sum=0;if(t->left&&t->right)sum=hm_calc_value_of_tree(t->left)+hm_calc_value_of_tree(t->right);elsesum=t->count;// DBG_LOG("tree sum:%d\n",sum);return sum;
}// 计算子节点的深度
static int hm_calc_deep_of_child(huff_tree* t)
{int deep = 0;while (t->parant) {deep++;t = t->parant;}return deep;
}// 打印huffman树
static void hm_tree_print(huff_tree *t)
{if(t->left&&t->right){DBG_LOG("point:,count:%d\n",hm_calc_value_of_tree(t));hm_tree_print(t->left);hm_tree_print(t->right);}else{DBG_LOG("data:%d,count:%d\n",t->data,t->count);}}// 建立huffman树
static void hm_creat_tree(huffman_def *h)
{int tail=h->index_table_index;huff_tree *sub1,*sub2;huff_tree **table=calloc(tail,sizeof(huff_tree *));for(int i=0;i<tail;i++){table[i]=h->index_table[i];}while(tail>1){huff_tree *temp;sub1=table[tail-1];sub2=table[tail-2];// 大在左,小在右temp=hm_creat_tree_point(h);sub1->parant=temp;sub2->parant=temp;// 左为1,右为0if(hm_calc_value_of_tree(sub1)>hm_calc_value_of_tree(sub2)){temp->left=sub1;sub1->pos=1;temp->right=sub2;sub2->pos=0;}else{temp->left=sub2;sub2->pos=1;temp->right=sub1;sub1->pos=0;}table[tail-2]=temp;tail--;hm_sort_index_table(table,tail);// DBG_LOG("-----table-----\n");// for(int i=0;i<tail;i++){//   DBG_LOG("index:%d,count:%d\n",i,hm_calc_value_of_tree(table[i]));// }}h->tree=table[0];free(table);
}// 删除树
static void hm_del_tree(huffman_def *h,huff_tree *t)
{if(t->left&&t->right){hm_del_tree(h,t->left);hm_del_tree(h,t->right);}hm_del_tree_point(h,t);
}// 数据中添加一个bit
static void hm_add_bit(uint8_t *d,int *d_len,int bit,int *index)
{if(*index<(*d_len )*8){uint8_t c = d[*d_len - 1];c|=bit<<(*index%8);d[*d_len - 1] = c;}else{d[*d_len] = bit;(*d_len)++;}(*index)++;
}// 根据数据添加bit
static int hm_encode_byte(huffman_def *h,uint8_t d)
{huff_tree *t=0;// 这里默认一定能找到对应的值for(int i=0;i<h->index_table_index;i++){t=h->index_table[i];if(t->data==d)break;}if(t->data!=d){DBG_WARN("can not encode.\n");exit(-1);}while(t->parant){hm_add_bit(h->out,&h->out_len,t->pos,&h->arr_bit_index);t=t->parant;}return 0;
}// 生成索引
static int hm_creat_index_table(huffman_def *h,uint8_t *data,int *data_len)
{int temp;int diff;int temp_num;data[*data_len] = h->index_table_index; (*data_len)++;// hm_index_table_print(h);for(int i=0;i<h->index_table_index;i++){data[*data_len] = h->index_table[i]->data; (*data_len)++;temp=h->index_table[i]->count;temp_num = temp / 255 + 1;for (int i = 0; i < temp_num; i++) {if (i < temp_num - 1) {data[*data_len] = 255; (*data_len)++;}else {data[*data_len] = temp % 255; (*data_len)++;}}}// 填充0个数temp=8-(h->arr_bit_index%8);//DBG_LOG("fill with 0 by:%d\n", temp);data[*data_len] = temp; (*data_len)++;return 0;
}// huffman编码
/*
压缩后数据格式
data[0]:索引表长度
data[1 ~ n]:索引表,每个索引由值(1byte)和频次(1byte,小于255)(2byte,大于等于255,频次由两个字节相加)
data[n+1]:数据中填充0个数
data[n+2 ~ m]:压缩后的数据*/
int hm_encode(const uint8_t* in, const int in_len, uint8_t** out, int* out_len)
{int input_len = in_len;int output_len=0;int output_index = 0;huffman_def *h=calloc(1,sizeof(huffman_def));hm_calc_count(h,in, input_len);hm_creat_tree(h);DBG_LOG("huffman tree point num:%d\n",h->tree_point_num);output_len = hm_calc_encode_len(h);(*out) = calloc(output_len + 1, sizeof(uint8_t));hm_creat_index_table(h, *out, &output_index);DBG_LOG("output_len=%d\n", output_index);h->out = &(*out)[output_index];for(int i=0;i<input_len;i++){hm_encode_byte(h,in[i]);}DBG_LOG("bitcount:%d\n", h->arr_bit_index);(*out)[output_index-1] = h->out_len*8- h->arr_bit_index;DBG_LOG("fill with 0 by:%d\n", (*out)[output_index - 1]);(*out_len) = output_len;hm_del_tree(h,h->tree);DBG_LOG("after del tree point num:%d\n",h->tree_point_num);DBG_LOG("lenth_in:%d,length_encode:%d\n",input_len, output_len);free(h);return 0;
}// 读取编码表,返回数据开始的位置
static int hm_unpack_count(huffman_def *h,const uint8_t *d,int d_len)
{int num = d[0]==0?256:d[0];;int index=1;uint8_t temp;for(int i=0;i<num;i++){h->index_table[i]=hm_creat_tree_point(h);h->index_table[i]->data=d[index];index++;do{temp= d[index];index++;h->index_table[i]->count+=temp;}while(temp==0xff);h->index_table_index++;}temp= d[index];index++;h->in_bit_count=(d_len -index)*8-temp;h->in=&d[index];// hm_index_table_print(h);printf("bitcount:%d,\n",h->in_bit_count);return index;
}// 获取指定index的bit值
static inline int hm_get_bit(const uint8_t *d,int index)
{uint8_t t=d[index/8];return t&(1<<(index%8))?1:0;
}// 对比树节点,匹配返回bit数,不匹配返回0
static inline int hm_cmp_bits(huffman_def *h,huff_tree *t)
{int count=0;// DBG_LOG("tree pos:",t->pos);while(t){// DBG_LOG("%d",t->pos);if(hm_get_bit(h->in,h->arr_bit_index+count)!=t->pos){// DBG_LOG(" |failed\n");return 0;}else{count++;t=t->parant;}}h->arr_bit_index+=count;// DBG_LOG(" |ok,\n");return count;
}static uint8_t hm_decode_byte(huffman_def *h)
{huff_tree *t=h->tree;int bit;// DBG_LOG("decode:");while(t->left&&t->right){bit=hm_get_bit(h->in,h->arr_bit_index-1);// DBG_LOG("%d",bit);if(bit==t->left->pos)t=t->left;elset=t->right;h->arr_bit_index--;}// DBG_LOG(" | decode byte:%c\n",t->data);return t->data;
}static int hm_calc_decode_len(huffman_def *h)
{int sum=0;for(int i=0;i<h->index_table_index;i++){sum+=h->index_table[i]->count;}DBG_LOG("data len for decode:%d\n",sum);return sum;
}// huffman解码
/*
*/
int hm_decode(const uint8_t* in, const int in_len, uint8_t** out, int* out_len)
{int decode_len,decode_index;uint8_t *decode_data=0;uint8_t c;huffman_def *h=calloc(1,sizeof(huffman_def));if (h == 0) {return -1;}hm_unpack_count(h,in,in_len);hm_creat_tree(h);// hm_data_code_print(h);// hm_tree_print(h->tree);DBG_LOG("huffman tree point num:%d\n",h->tree_point_num);decode_len=hm_calc_decode_len(h);decode_index=decode_len;decode_data=calloc(decode_len+1,sizeof(uint8_t));h->arr_bit_index=h->in_bit_count;while(decode_index >0){c=hm_decode_byte(h);decode_data[decode_index-1]=c;decode_index--;}hm_del_tree(h,h->tree);DBG_LOG("after del tree point num:%d\n",h->tree_point_num);free(h);(*out) = decode_data;(*out_len) = decode_len;return 0;
}

实验

无重复数据的压缩情况

编写实验代码如下:


int main(int argc, char *argv[])
{// encode_file(argv[1]);//const uint8_t file_data[]="2023 5830628A000005830628A000015830628A000025830628A000035830628A000045830628A000055830628A000065830628A000075830628A000085830628A00009";const uint8_t file_data[]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70};uint8_t *encode_data=0;int encode_size;uint8_t *decode_data=0;int decode_size;hm_encode(file_data,sizeof(file_data),&encode_data,&encode_size);print_data(encode_data,encode_size);hm_decode(encode_data,encode_size,&decode_data,&decode_size);print_data(decode_data,decode_size);free(encode_data);free(decode_data);return 0;
}

实验1

可以看到在输入数据没有重复性的时候压缩之后的数据反而增大了(原始数据长度为70,压缩之后的数据长度为196),扩大了接近3倍。

有重复数据的压缩情况

编写验证代码如下:


int main(int argc, char *argv[])
{// encode_file(argv[1]);const uint8_t file_data[]="2023 5830628A000005830628A000015830628A000025830628A000035830628A000045830628A000055830628A000065830628A000075830628A000085830628A00009";// const uint8_t file_data[]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,//   39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70};uint8_t *encode_data=0;int encode_size;uint8_t *decode_data=0;int decode_size;hm_encode(file_data,sizeof(file_data),&encode_data,&encode_size);print_data(encode_data,encode_size);hm_decode(encode_data,encode_size,&decode_data,&decode_size);// print_data(decode_data,decode_size);printf("%s",(const char *)decode_data);free(encode_data);free(decode_data);return 0;
}

实验2
原始数据存在重复数据的时候,Huffman编码则可以大放异彩(原始数据长度136,压缩之后的数据长度76),数据量减小了接近一半。

数据中只有一种字符的情况

编写如下代码:

int main(int argc, char *argv[])
{// encode_file(argv[1]);// const uint8_t file_data[]="2023 5830628A000005830628A000015830628A000025830628A000035830628A000045830628A000055830628A000065830628A000075830628A000085830628A00009";// const uint8_t file_data[]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,//   39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70};uint8_t file_data[200]={0};uint8_t *encode_data=0;int encode_size;uint8_t *decode_data=0;int decode_size;hm_encode(file_data,sizeof(file_data),&encode_data,&encode_size);print_data(encode_data,encode_size);hm_decode(encode_data,encode_size,&decode_data,&decode_size);print_data(decode_data,decode_size);// printf("%s",(const char *)decode_data);free(encode_data);free(decode_data);return 0;
}

实验3
原始数据有200字节,压缩之后仅4个字节,[0x01,0x00,0xc8,0x00]。

项目Value
0x01压缩数据中出现的字符数
0x00压缩数据中出现的第一个字符
0xc8第一个字符的出现次数
0x00没有编码末尾补0

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

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

相关文章

南京观海微电子---Vitis HLS的工作机制——Vitis HLS教程

1. 前言 Vitis HLS&#xff08;原VivadoHLS&#xff09;是一个高级综合工具。用户可以通过该工具直接将C、 C编写的函数翻译成HDL硬件描述语言&#xff0c;最终再映射成FPGA内部的LUT、DSP资源以及RAM资源等。 用户通过Vitis HLS&#xff0c;使用C/C代码来开发RTL IP核&#x…

VSCode 如何同步显示网页在手机或者平板上

首先要确保 ①电脑上安装了VsCode ②VsCode安装插件LiveServer 安装成功之后 连续按住 Alt L 、Alt O 会跳转到对应的html页面上 http://127.0.0.1:5500/....... 是这个开头的 然后打开网络 如果桌面有网上邻居的可以直接点桌面的网上邻居 进来找到WLAN这个…

RabbitMQ安装及使用笔记

RabbitMQ安装及使用笔记 RabbitMQ是一个开源的消息代理软件&#xff0c;它实现了高级消息队列协议&#xff08;AMQP&#xff09;&#xff0c;用于在分布式系统中进行消息传递。 1.安装 利用docker load命令加载mq镜像 docker load -i mq.tar 基于Docker来安装RabbitMQ&#xff…

数据结构(四)顺序表与链表的深层次讲解

我们在数据结构&#xff08;二&#xff09;&#xff0c;对链表和顺序表已经讲解过了。但很多同学表示有点晦涩难懂那我就出一篇深层次讲解&#xff0c;一步一步来带领大家学习。 我们从头&#xff08;数据结构&#xff09;开始完整的来为大家讲解&#xff0c;大家好好看好好学。…

人工智能在产业中应用--生成智能

二、生成式人工智能 前面介绍了很多人工智能的应用&#xff0c;接下来部分我们会介绍当前正在进行的生成智能。生成智能和以往的人工智能有什么区别&#xff0c;个人觉得主要区别就在于“度”。在表现上就是以前的人工智能更多是利用既有的数据集分布挖掘和解决在这个数据集下…

Python人工智能:气象数据可视化的新工具

Python是功能强大、免费、开源&#xff0c;实现面向对象的编程语言&#xff0c;在数据处理、科学计算、数学建模、数据挖掘和数据可视化方面具备优异的性能&#xff0c;这些优势使得Python在气象、海洋、地理、气候、水文和生态等地学领域的科研和工程项目中得到广泛应用。可以…

单片机原理及应用

单片机时钟电路及时序 时钟电路用于产生AT89S51单片机工作时所必需的时钟脉冲信号(工作频率)&#xff1b;AT89S51单片机的CPU正是在时钟脉冲信号的控制下&#xff0c;严格地按时序执行指令进行工作的。AT89S51单片机的最高时钟频率为33MHz。 时钟电路 AT89S51单片机常用的时…

学习或复习电路的game推荐:nandgame(NAND与非门游戏)、Turing_Complete(图灵完备)、logisim工具

https://www.nandgame.com/ 免费 https://store.steampowered.com/app/1444480/Turing_Complete/ 收费&#xff0c;70元。据说可以导出 Verilog &#xff01; logisim及其衍生版本 都需要安装java环境。 http://www.cburch.com/logisim/ 是原版&#xff0c; 下载页面&#…

Python拆分PDF、Python合并PDF

WPS能拆分合并&#xff0c;但却是要输入编辑密码&#xff0c;我没有。故写了个脚本来做拆分&#xff0c;顺便附上合并的代码。 代码如下&#xff08;extract.py) #!/usr/bin/env python """PDF拆分脚本(需要Python3.10)Usage::$ python extract.py <pdf-fil…

垃圾回收机制--GC 垃圾收集器--JVM调优-面试题

1.触发垃圾回收的条件 新生代 Eden区域满了&#xff0c;触发young gc (ygc)老年代区域满了&#xff0c;触发full gc (fgc)通过ygc后进入老年代的平均大小大于老年代的可用内存,触发full gc(fgc).程序中主动调用的System.gc()强制执行gc,是full gc&#xff0c;但是不必然执行。…

蓝桥杯(3):python搜索DFS

目录 1 DFS简介 1.1 DFS与n重循环 1.2 代码实现 1.3 例题 1.3.1 分糖果 1.3.2 买瓜 2 回溯 2.1 定义 2.2 代码实例 2.1.1 排列数 2.1.2 找子集 2.3 例题 2.3.1 N皇后 2.3.2 小朋友崇拜圈 2.3.3 全球变暖 3 剪枝 3.1 定义 3.2 分类 3.3 例子 3.3.1 数字王国之…

自动化测试:Selenium中的时间等待

在 Selenium 中&#xff0c;时间等待指在测试用例中等待某个操作完成或某个事件发生的时间。Selenium 中提供了多种方式来进行时间等待&#xff0c;包括使用 ExpectedConditions 中的 presence_of_element_located 和 visibility_of_element_located 方法等待元素可见或不可见&…

javaWeb项目-火车票订票信息系统功能介绍

项目关键技术 开发工具&#xff1a;IDEA 、Eclipse 编程语言: Java 数据库: MySQL5.7 框架&#xff1a;ssm、Springboot 前端&#xff1a;Vue、ElementUI 关键技术&#xff1a;springboot、SSM、vue、MYSQL、MAVEN 数据库工具&#xff1a;Navicat、SQLyog 1、Spring Boot框架 …

任务2.1 一元二次方程(顺序结构版)

在这个任务中&#xff0c;我们编写了一个Java程序来解决一元二次方程。程序接受用户输入的系数a、b、c&#xff0c;并计算出方程的根。通过计算判别式delta的值&#xff0c;我们可以确定方程有两个不相等实根、两个相等实根还是没有实数根。这个程序遵循了IPO模式&#xff0c;即…

MCGS学习——MCGS仿真与实体西门子监控

MCGS仿真与西门子监控 前提知识——博图与MCGS联合仿真 适用于什么设备都没有的情况下进行学习 对NetToPLCsim进行初始设置 找到博图的IP地址 勾选允许远程对象的通信访问 勾选系统时钟参数&#xff0c;主要是需要用到1HZ的脉冲&#xff0c;做一个闪烁的灯 编写简单程序&am…

如何在Linux系统使用Docker本地部署Halo网站并实现无公网IP远程访问

最近&#xff0c;我发现了一个超级强大的人工智能学习网站。它以通俗易懂的方式呈现复杂的概念&#xff0c;而且内容风趣幽默。我觉得它对大家可能会有所帮助&#xff0c;所以我在此分享。点击这里跳转到网站。 文章目录 1. Docker部署Halo1.1 检查Docker版本如果未安装Docker可…

2024/3/28 IOday1

编写一条学生链表&#xff0c;写一些能够像链表里边添加数据的函数 实现&#xff1a;将链表中的所有内容保存到文件中去 以及 读取文件中的所有内容&#xff0c;加载到链表里 #include <stdio.h> #include <string.h> #include <stdlib.h> typedef int datat…

SpringBoot学习之ElasticSearch下载安装和启动(Windows版)(三十)

本文先写windows下的下载安装和启动,后续有时间再补充其他环境下(Mac、Linux、Docker)的,这里我们后续对ElasticSearch简称为ES,读者习惯这一称呼就好。 一,ES下载 可以百度【ElasticSearch官网】或者直接点击这里的ES官网下载地址:​​​​​ Download Elasticsearch…

springboot检测脚本

import requests import urllib3 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) session requests.session()# 从文本文件中读取 with open(dic.txt, r) as file:paths file.readlines()# 移除每个末尾的换行符 paths [path.strip() for path in pa…

智慧公厕解决方案打造更加智能的卫生空间

一、智慧公厕方案概述 智慧公厕方案旨在解决现有公厕存在的诸多问题&#xff0c;包括民众用厕困难、环境卫生状况不佳、管理效率低下等方面。针对民众的需求和管理方面的挑战&#xff0c;智慧公厕提供了一套综合解决方案&#xff0c;包括智能导航、环境监测、资源管理等功能&a…