拼图算法,将零碎小图,整理到一张大图之上,自动合并。二叉树实现

需求描述

在开发需求中,有种场景:工程中图片太多,很多png图片,导致程序包很大。尤其是出现在移动平台iOS,Android和游戏平台。这是因为png的头占用资源比较大,如果将所有png图片拼接到一起,就省了png的头部信息;那么程序包应该会少不小!

想法很好!如果程序中有四五百个png图片,手动合成到大图上恐怕不是小工作量。何况每天的资源图片都有可能更新,更新一个就要重新拼图。这个工作量肯定不是人能搞定的了。可不可让计算机帮助我们来拼图。拼图完成后,计算机告诉我们每个图片的坐标,程序运行时候我们根据坐标去拿图片就行了。

文章最后可下载demo

想想,手动拼图的第一步。

首先设定一个很大的空白图片,假如是(1000,1000)这样就够装很多图片。考虑到最大限度利用该空白图片。事先将所有图片的大小排序。最大在最前面。

将最大的图片放到空白图片左上角,如下图所示:那么可以将剩余的区域分为两部分:left和right部分,用于放后继图片。

设计数据模型——二叉树

从刚刚的图片中,很容易想到二叉树的数据结构,如下图所示

看懂这个图片,主要通过三个● 和三块区域

采用二叉树数据结构

按照上图的思路,采用二叉树数据结构。怎么将图片一个个插入到二叉树中呢?有一下几种方法:

● 前序遍历

● 后序遍历

● 中序遍历

● 层序遍历

实现初始化一个数组,存储已经排序好了的图片资源

    NSMutableArray* tmp = [NSMutableArray array];
    [tmp addObject:[[picNode alloc]initWithSize:CGSizeMake(120, 100) name:@"1.png"]];
    [tmp addObject:[[picNode alloc]initWithSize:CGSizeMake(120, 100) name:@"2.png"]];
    [tmp addObject:[[picNode alloc]initWithSize:CGSizeMake(100, 100) name:@"3.png"]];
    [tmp addObject:[[picNode alloc]initWithSize:CGSizeMake(80, 21) name:@"4.png"]];
    [tmp addObject:[[picNode alloc]initWithSize:CGSizeMake(70, 40) name:@"5.png"]];
    [tmp addObject:[[picNode alloc]initWithSize:CGSizeMake(65, 30) name:@"6.png"]];
    [tmp addObject:[[picNode alloc]initWithSize:CGSizeMake(50, 21) name:@"7.png"]];
    [tmp addObject:[[picNode alloc]initWithSize:CGSizeMake(50, 21) name:@"8.png"]];
    [tmp addObject:[[picNode alloc]initWithSize:CGSizeMake(50, 21) name:@"9.png"]];
    [tmp addObject:[[picNode alloc]initWithSize:CGSizeMake(50, 21) name:@"10.png"]];
    [tmp addObject:[[picNode alloc]initWithSize:CGSizeMake(40, 40) name:@"11.png"]];
    [tmp addObject:[[picNode alloc]initWithSize:CGSizeMake(35, 30) name:@"12.png"]];

看一下效果GIF图片,然后再说一下insertPicture的代码。

看看insertPicture的逻辑:

-(bool) insertPicture:(BTreeNode*)node{

    //如果图片大小,可以放下这个rect。那么就设置一下这个rect的大小为图片大小,剩余面积分为left和right
    if (!node.isFull
        && node.virtualPic.size.width >= _currentPic.size.width
        && node.virtualPic.size.height >= _currentPic.size.height) {
        node.isFull = TRUE;
        /*
         拆分剩余rect为left和right两个分支。
         ●---------●-----------------
         | picture |   right        |
         |         |                |
         ●---------------------------
         |                          |
         |        left              |
         |                          |
         |                          |
         |                          |
         ----------------------------
         例如.picture已经占据了【左上角】区域。剩下的区域分为left和right。点(●)的地方就是CGPoint了。
         ● 优化点:如果picture是矩形,那么在生成left和right的时候。可以有两个选择:向下延伸,向右延伸。
         通常是向值小的一方延伸,这样保证值大的一方可以放进去更多的图片。
         ● 上面的例子是向右延伸。
         */
        node.left = [[BTreeNode alloc]init];
        node.left.point = CGPointMake(node.point.x, node.point.y + _currentPic.size.height);
        node.left.virtualPic = [[picNode alloc]init];

        node.right = [[BTreeNode alloc]init];
        node.right.point = CGPointMake(node.point.x+ _currentPic.size.width, node.point.y);
        node.right.virtualPic = [[picNode alloc]init];

        //优化一下
        if (_currentPic.size.width >= _currentPic.size.height)
        {
            //left的宽度是parent的宽度。高度是parent高度 - 图片的高度
            node.left.virtualPic.size = CGSizeMake(node.virtualPic.size.width, node.virtualPic.size.height - _currentPic.size.height);
            //right的宽度是,parent的宽度-图片的宽度。高度是图片的高度。
            node.right.virtualPic.size = CGSizeMake(node.virtualPic.size.width- _currentPic.size.width, _currentPic.size.height);
        }else{
            //left的宽度是图片的宽度。高度是parent高度 - 图片的高度(不变)
            node.left.virtualPic.size = CGSizeMake(_currentPic.size.width, node.virtualPic.size.height - _currentPic.size.height);
            //right的宽度是,parent的宽度-图片的宽度。高度parent的高度。
            node.right.virtualPic.size = CGSizeMake(node.virtualPic.size.width- _currentPic.size.width, node.virtualPic.size.height);
        }

        //将虚拟的pic复制
        node.virtualPic = [_currentPic copy];
    return YES;
    }
    return NO;
}

总结:

后序遍历的图片利用率最高,这样的结果也符合人类的思维,假如我们手动排班。那么应该也是最大在上角,此大的往后排。后侧排满后再从左侧开始,即蛇型曲线。

demo下载地方点击打开链接

拼图算法,将零碎小图,整理到一张大图之上,自动合并。二叉树实现

时间: 2024-11-08 11:00:20

拼图算法,将零碎小图,整理到一张大图之上,自动合并。二叉树实现的相关文章

《算法心得》一点整理

最近在图书馆看到本神书<算法心得:高效算法的奥秘>,主要讲解计算机算法的,强调编译器优化和计算机体系结构设计的.虽然看的不大懂,但还是给自己增长了见识和知识.少许整理些自己感兴趣的算法,以备后续温故知新. 1. 操作最右边的位元 a. 将字组中值为1且最靠右的位元置0,如果不存在值为1的位元,则全部结果为0(例如 0101 1110 => 0101 1100): x & (x-1) 这个操作可以判断无符号证书是不是2的幂或者0. b. 将字组中值为0且最靠右的位元置1,如果不存在

DBScan聚类算法原理与实现整理

百度百科中的描述 算法描述: (1)检测数据库中尚未检查过的对象p,如果p为被处理(归为某个簇或者标记为噪声),则检查其邻域,若包含的对象数不小于minPts,建立新簇C,将其中的所有点加入候选集N: (2)对候选集N 中所有尚未被处理的对象q,检查其邻域,若至少包含minPts个对象,则将这些对象加入N:如果q 未归入任何一个簇,则将q 加入C: (3)重复步骤2),继续检查N 中未处理的对象,当前候选集N为空: (4)重复步骤1)~3),直到所有对象都归入了某个簇或标记为噪声. 伪代码: 输

客户端地图拼图算法解析

概述:主要是阐述如何将瓦片地图图片拼接成完整地图的一些概念以及相关算法. 基本概念: 地图瓦片地址:http://mt2.google.cn/vt/[email protected]&hl=zh-CN&gl=cn&x=420&y=193&z=9&s=Galil 现在就是要将一张张这类的地图瓦片,在客户端拼接成一幅完整的地图. 瓦片大小为:256x256. url中关键参数解析: 参数 描述 mt2.google.cn Google瓦片服务服务器,可以尝试mt

less实现小图自动合并大图

1. 执行安装命令: cnpm install grunt-spritesmith --save-dev      //在package.json里自动生成"grunt-spritesmith": "^4.6.0" 2. 查找配置文件:通过npm官网,找到geetting started配置文件,将代码sprite那段拷入grutefile.js文件 sprite:{ all: { src: 'path/to/your/sprites/*.png',   //要合并

Javascript实现拼图算法

拼图,就是将1-8这几个数字,通过移动后,按照顺序排列,比如下图, 排列完成后成为 实现的算法如下: 可以把空格认为是0,每一次移动就是数字0和周围的数字做一次交换. 1.比如对状态A,数字0在4个方向上尝试(有的位置不能再移动,忽略该状态)后,得到4个不同的状态A1,A2,A3,A4.那么可以有一棵树以A为根,A1,A2,A3,A4都为叶子节点.检测这4个节点是否已经满足结果,如果是,则已经找到解了.然后顺着这个叶子结点,一路向上逆序到它的父节点,所经过的所有叶子节点,即为每一步的状态.如果否

DFA算法过滤敏感词整理

这里有部分是从网上找的,但看起来太乱了,分的太散了.研究了几天,整理出来,有问题的话还请大虾们提出来.... package org.rui.util; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.util.HashMap; i

A*算法之烂片整理

把A*算法的几个烂片整理一下. 缘起于 是Google对开源中国有意见,还是我们"非著名" http://www.oschina.net/question/660460_238919 其实事情的经过是这样地??????英文的原篇地址在这里哦. http://my.oschina.net/u/660460/blog/425664 垃圾翻译再译 再译<A *路径搜索入门>之一 http://my.oschina.net/u/660460/blog/425924 再译<A

【算法】组合数学的整理

组合数学基础知识,整理所学          一.   组合数递推公式: ??_??^??=??_(???1)^(???1)+??_(???1)^?? C(n,m) = C(n-1,m)+C(n-1,m-1);         二.   鸽笼原理 描述: 如果n个物体被放进m个盒子,那么至少有一个盒子有???/???个物体. -->意思为向上取整,用floor函数可以实现               经典案例:                           对数列a1,a2,a3,--an

数据结构与算法笔试面试题整理

给出的一些常见的数据结构与算法的笔试面试题,特整理如下,后期遇到新的再更新. 笔试面试题 常见时空复杂度有 常数级复杂度:O(1) 对数级复杂度:O(logN) 线性级复杂度:O(N) 线性对数级复杂度:O(NlogN) 平方级复杂度:O(N2) 冒泡排序算法(重点) (1)算法流程 a.比较两个相邻的元素,如果第一个比第二个大,则交换两个元素的位置: b.对每一对相邻的元素做同样的工作,从开始的第一对一致到结尾的最后一对,经过这一步,最后的元素将是最大值: c.针对所有的元素重复以上步骤,除了