C语言实现四进制(四叉树)、十进制的Morton码

时间:2024-10-09 07:44:47
一、前言:
由《地理信息信息系统与原理》《地理信息系统基础》介绍,感觉有点意思,故想用C语言实现一下,顺便梳理相关内容。
前提知识:

叶节点:即没有子节点的节点。

线性四叉数只存储最后叶节点信息,包括叶节点的位置、深度和网格值。

地址码(Morton码):线性四叉树叶节点遵照一定的规则,隐含了叶节点位置信息的编号。

拓展:

3维空间xyz 的 32 位的莫顿码 编码方式zyxzyx, 每个维度用10bit 表示(0~1023)。

树的结点:包含一个数据元素及若干指向子树的分支;

孩子结点:结点的子树的根称为该结点的孩子;

双亲结点:B 结点是A 结点的孩子,则A结点是B 结点的双亲;

兄弟结点:同一双亲的孩子结点; 堂兄结点:同一层上结点;

祖先结点: 从根到该结点的所经分支上的所有结点子孙结点:以某结点为根的子树中任一结点都称为该结点的子孙

结点层:根结点的层定义为1;根的孩子为第二层结点,依此类推;

树的深度:树中最大的结点层

结点的度:结点子树的个数

树的度: 树中最大的结点度。

叶子节点:也叫终端结点,是度为 0 的结点;

分枝结点:度不为0的结点;

有序树:子树有序的树,如:家族树;

无序树:不考虑子树的顺序;

二、基本原理:
两种方法:

基于四进制Morton码的生成和四叉数的建立过程有两种不同方案

1.自上而下分裂的方式在建立四叉数的过程中逐步产生Morton码;

2.先计算每个格网的Morton码,然后按一定扫描方式自下而上合并建立四叉数。

自下而上与自上而下生成的四叉数一致,但前者的效率更高

对于第一种方法的Morton码:

第一步:分割子象限,标号0,1,2,3分别表示左上,右上,左下,右下四个子象限。

第二步:进行检测,如有需要继续细分。

分割过程中,标号的位数不断增加,这种标号即Morton码。

且Morton码的每一位数字都是不大于3的四进制数,每经过一次分割,增加一位数字,分割次数越多,所得子区域越小,相应的Morton码值越大。

最后结点的Morton码值是所有各位上相应象限值相加,MQ=q1q2q3..qn=q1*10^(k-1)+q2*10^(k-2)...+qk

对于第二种方法的Morton码:

1.将十进制的行列号转化为二进制数表示

2.依据MQ=2*Ib(二进制行号)+Jb(二进制列号)

三、代码实现:
小试牛刀:

理解一下四进制的Morton码:

  1. #include<>
  2. #include<>
  3. //计算MQ值
  4. int calculateMq(int Ib,int Jb)
  5. {
  6. return (2 * Ib + Jb);
  7. }
  8. //计算十进制数
  9. int tenTransToTwo(int II)
  10. {
  11. int sum = 0;//用于存放转化后的二进制
  12. int n = 0;//用于转化输出形式的指数
  13. while (II > 1)//判断除二取余是否完成
  14. {//逆序存储
  15. sum += ((II % 2)*pow(10,n));
  16. II = (II- II % 2)/2;
  17. n++;
  18. }
  19. sum += (II * pow(10, n));
  20. return sum;
  21. }
  22. int main()
  23. {
  24. //提示用户输入
  25. printf("请输入十进制的II,JJ值\n");
  26. //初始化与读取十进制数
  27. int II=0;
  28. int JJ=0;
  29. scanf_s("%d", &II);
  30. scanf_s("%d", &JJ);
  31. //实现行和列的二进制转换
  32. printf("%d\n", tenTransToTwo(II));
  33. printf("%d\n", tenTransToTwo(JJ));
  34. //输出四进制的Morton值(MQ值)
  35. printf("%d\n", calculateMq(tenTransToTwo(II), tenTransToTwo(JJ)));
  36. return 0;
  37. }
输出结果:

四进制的缺点:

1.码内外内存开销大,多数语言不支持四进制变量。

2.运算效率不高

因此Mark等人(1989)建议采用十进制的Morton码作为线性四叉数的地址码

再次体验:

十进制的Morton码:

法一:

基于数学公式的计算法

  1. #include<>
  2. #include<>
  3. //计算MQ值
  4. int calculateMq(int Ib, int Jb)
  5. {
  6. return (2 * Ib + Jb);
  7. }
  8. //计算十进制数
  9. int tenTransToTwo(int II)
  10. {
  11. int sum = 0;//用于存放转化后的二进制
  12. int n = 0;//用于转化输出形式的指数
  13. while (II > 1)//判断除二取余是否完成
  14. {//逆序存储
  15. sum += ((II % 2)*pow(10, n));
  16. II = (II - II % 2) / 2;
  17. n++;
  18. }
  19. sum += (II * pow(10, n));
  20. return sum;
  21. }
  22. //计算二进制数
  23. int twoTransToFour(int transtedTwo)
  24. {
  25. int sum = 0;//用于存储四进制数
  26. int n = 0;//用于转化输出形式的指数
  27. int digit = 0;//用于暂存每一位数;
  28. //遵循逆序输出的原则,正序存入
  29. while(transtedTwo>0)
  30. {
  31. digit = transtedTwo % 2;
  32. transtedTwo /= 2;
  33. sum += (digit*pow(4, n));
  34. n++;
  35. }
  36. //返回数组即返回四进制数
  37. return sum;
  38. }
  39. int main()
  40. {
  41. //提示用户输入
  42. printf("请输入十进制的II,JJ值\n");
  43. //初始化与读取十进制数
  44. int II = 0;
  45. int JJ = 0;
  46. scanf_s("%d", &II);
  47. scanf_s("%d", &JJ);
  48. 实现行和列的二进制转换
  49. //printf("Ib = %d\n", tenTransToTwo(II));
  50. //printf("Jb = %d\n", tenTransToTwo(JJ));
  51. 输出四进制的Morton值(MQ值)
  52. //printf("目标处的Morton码为%d\n", calculateMq(tenTransToTwo(II), tenTransToTwo(JJ)));
  53. //实现行和列的四进制转换
  54. printf("Ib = %d\n", twoTransToFour(II));
  55. printf("Jb = %d\n", twoTransToFour(JJ));
  56. //输出十进制的Morton值(MQ值)
  57. printf("目标处的Morton码为%d\n", calculateMq(twoTransToFour(II), twoTransToFour(JJ)));
  58. return 0;
  59. }
法二:

基于按位操作的运算法,为了简化对按位操作符的使用作者打算使用数组模拟。

  1. #include<>
  2. #include<>
  3. //计算十进制数
  4. int tenTransToTwo(int II)
  5. {
  6. int sum = 0;//用于存放转化后的二进制
  7. int n = 0;//用于转化输出形式的指数
  8. while (II > 1)//判断除二取余是否完成
  9. {//逆序存储
  10. sum += ((II % 2)*pow(10,n));
  11. II = (II- II % 2)/2;
  12. n++;
  13. }
  14. sum += (II * pow(10, n));
  15. return sum;
  16. }
  17. //计算二进制数
  18. int MyBitOperator(int transtedTwo1 , int transtedTwo2)
  19. {
  20. int sum = 0;//用于存储四进制数
  21. int i=0;//移动数组下标
  22. //遵循逆序输出的原则,正序存入
  23. int arr[8] = { 0 };//用于存放II和JJ的位,偶数(和0)为II位,奇数为JJ位
  24. while (transtedTwo1 > 0)
  25. {
  26. arr[0+i] = transtedTwo1% 2;//0,2,4,6
  27. transtedTwo1 /= 10;
  28. i += 2;
  29. }
  30. i = 0;
  31. while (transtedTwo2 > 0)
  32. {
  33. arr[1+i] = transtedTwo2 % 2;//1,3,5,7
  34. transtedTwo2 /= 10;
  35. i += 2;
  36. }
  37. //计算Md
  38. int n = 0;//用于转化输出形式的指数
  39. for (int i = (sizeof(arr) / sizeof(arr[0]))-3; i >=0; i--)//前面数组冗余
  40. {
  41. if (arr[i] > 0) {
  42. sum += (arr[i] * pow(2, n));
  43. printf("%d\n", sum);
  44. }
  45. n++;
  46. }
  47. //返回数组即返回四进制数
  48. return sum;
  49. }
  50. int main() {
  51. //提示用户输入
  52. printf("请输入十进制的II,JJ值\n");
  53. //初始化与读取十进制数
  54. int II = 0;
  55. int JJ = 0;
  56. scanf_s("%d", &II);
  57. scanf_s("%d", &JJ);
  58. printf("MD值为%d", MyBitOperator(tenTransToTwo(II), tenTransToTwo(JJ)));
  59. return 0;
  60. }

输出结果:

法一:

法二:

总结:整体来说体型简单,主要关于位与进制的运算,需要细心,作者因此调试许久!!

觉得不错可以点赞收藏关注!我们一同进步!

菜鸡一枚,欢迎大家批评指正!

点点赞哦