zcy算法入门笔记002-位图

位图是一种数据结构,用于表示和处理图像或图形的像素信息,常用于图像处理、压缩、编辑软件、扫描和打印以及GUI设计。文章介绍了位运算,如左移、右移、无符号右移,并展示了如何使用位图实现集合功能,包括添加、删除和检查元素,以及如何使用位运算进行加、减、乘、除操作。此外,还讨论了在处理系统最小值时的特殊情况和解决方案。
摘要由CSDN通过智能技术生成

引入:位图是什么?

位图(Bitmap)是一种数据结构,用于表示和处理二进制图像或图形。它的作用是在有限的空间中表示和存储图像或图形的像素信息。每个像素都用一个位或几个位来表示其颜色或其他属性。

应用场景:

图像处理:位图广泛用于图像处理和计算机图形学领域。它可以表示真彩色图像、灰度图像或黑白图像,以及透明度、图层和特定效果等。

图像压缩:位图可以通过压缩算法来减小文件大小。一些常见的位图压缩算法包括JPEG和PNG。压缩后的位图在网络传输和存储方面更具效率。

图像编辑和绘图软件:许多图像编辑和绘图软件使用位图作为内部数据结构。用户可以对位图进行编辑、绘制、调整和处理,以创建或修改图像。

扫描仪和打印机:扫描仪用位图来捕捉图像或文档,并将其转换为数字形式。打印机则使用位图来生成图像或文档的输出。

图形界面(GUI):在计算机图形用户界面中,位图用于绘制图标、按钮、窗口等界面元素。位图还用于绘制和渲染图形效果,如过渡、阴影和纹理等。

游戏开发:位图在游戏开发中扮演重要角色。它可以表示游戏中的角色、场景、纹理、动画等图像元素。

需要注意的是,位图相对于矢量图形(Vector Graphics)具有一些限制,例如在放大时可能失去清晰度,文件大小可能较大等。因此,在选择使用位图还是矢量图形时,需要根据具体的应用场景和需求进行权衡。

一、前置知识点:左移、右移、无符号右移

  1. <<:左移 左边最高位丢弃,右边补0(高位向左移动,低位补零)

  • 若左移时舍弃的高位不包含1,则每左移一位,相当于该数乘以2。

  1. >>:右移 最高位是0,左边补0;最高为是1,左边补1 ,即高位补符号位

  • 为非负数时,>> 1和/ 2的结果是一样的

  • 为负数且还是偶数时,>> 1和/ 2的结果是一样的

  • 为负数且还是奇数时,>> 1和/ 2的结果是不一样的

  1. >>>:无符号右移 无论最高位是0还是1,左边补0

二、位图的功能和实现

引入:假设有一个集合可以帮我们收集数字,既可以add,也可以去contains。但是存在空间问题:每个整型数字(起码)占4Byte(字节)。若集合中此时只有一个整数,而一个整数有32位(4Byte*8bit),足以表示0~31之间的数字在集合中有没有。

初始是:0000...00000000 --> 一共32位

把5位置标为1即可以表示集合中有5:0000...00100000

结论:只需要4个字节即32个bit即可表示0~31之间数字是否出现过。

推广:则int[32]-> 可以表示32*32=1024个数。

功能:位图可以做出一个集合,在数字范围(最大值)确定情况下,可使用位图收集数字并判断某个数存不存在。优点就在于它可以极大压缩空间。

实现:

public static class BitMap {

        private long[] bits;    //一个long类型可表示64个数

        public BitMap(int max) {//在数字范围确定情况下传入最大值
            //(max + 64) >> 6  -> (max + 64) / 64   即一共需要几位存数字
            bits = new long[(max + 64) >> 6];
        }

        public void add(int num) {
            /*
                1.首先num/64可以定位到是哪个整数   num >> 6  ->  num/64   -->找到第几个整数
                2.(num & 63) -> (num % 64)  63即000..111111,&操作把前面全变成0了,剩下的等同于num%64 -->找到第几位用于描述num
                3.why? 因为+ - * / %比位运算>> << & | ^慢多了
                4. 1L<<(num & 63) -> 把1移到描述num的位置通过或运算标到位图里
                例如170:  170/64=2,170%64=42,  1L<<42然后和000...0000进行或运算来完成num的add操作
            */
            bits[num >> 6] |= (1L << (num & 63));
        }

        public void delete(int num) {
            /*
                例如删170,先170/64=2再170%64=42,然后到bit[2]中描述num的位置改成0;
                不管描述num的位置是0还是1,和0进行&操作都会变成0->等同于删掉
            */
            bits[num >> 6] &= ~(1L << (num & 63));
        }

        public boolean contains(int num) {
            //描述num的位置和1进行&操作-> 若是1: 1&1->1 !=0 返回true即存在
            return (bits[num >> 6] & (1L << (num & 63))) != 0;
        }

    }

三、使用位运算实现加法

两数相加思路:

int a = 46; //0101110

int b = 20; //0010100

1.异或运算也叫无进位相加

0101110 ^ 0010100 -> 0111010

2.得到a、b的进位信息:先&再左移1位

0101110 & 0010100 -> 0000100 -> 0001000

3. a+b=a^b+进位信息

0111010 + 0001000 = 1000010 -> 66

代码

public static int add(int a, int b) {
        int sum = a;
        while (b != 0) {
            sum = a ^ b;    //无进位相加的信息
            b = (a & b) << 1;   //然后计算进位信息 -> b->b'(进位信息)
            a = sum;    //a -> a'  无进位相加信息
        }
        return sum;
    }

四、使用位运算实现减法

a-b即a+b的相反数

代码

public static int negNum(int n) {
        return add(~n, 1);
}

public static int minus(int a, int b) {
    return add(a, negNum(b));
}

五、使用位运算实现乘法

a=0110

b=0111

则a*b=ans=0110+01100+011000

代码

public static int multi(int a, int b) { //支持正负
        int res = 0;
        while (b != 0) {
            if ((b & 1) != 0) { //等于0则不加此数
                res = add(res, a);  //结果接收a
            }
            a <<= 1;    //左移右边补0
            b >>>= 1;   //无符号右移左边补0
        }
        return res;
    }

六、使用位运算实现除法

小例:正数除以正数

a:01101100 b:00001100

2.小例:a/b=c 若按乘法思路: a=b*c

b=01110 c=00110

a=b*2¹+b*2² (c在第1、2位上是1) 反过来求c:

3.小例: a=01101100 b=00000011 求a/b

得到最终结果:100100(5位、2位有1)

4.小例:

减掉之后周而复始

代码

public static boolean isNeg(int n) {
        return n < 0;
    }

    public static int div(int a, int b) {   //会向下取整
        //在正式逻辑开始之前一定都转成正数
        int x = isNeg(a) ? negNum(a) : a;   //得到正数绝对值形式
        int y = isNeg(b) ? negNum(b) : b;
        int res = 0;
        // x/y
        for (int i = 30; i >= 0; i = minus(i, 1)) {
            //代码上是x去右移匹配y->因为y左移到头可能改变符号位
            if ((x >> i) >= y) {    //30->0位
                res |= (1 << i);    //移动到某个位置可以减掉时把结果设置上
                x = minus(x, y << i);   //此时y左移被x减掉
            }
        }
        //如果a、b符号不一样,取个负号返回
        return isNeg(a) ^ isNeg(b) ? negNum(res) : res;
    }

怎么解决系统最小值转绝对值

例子1:假设-10(最小)~9(最大)

问题:当使用-10/2无法转成10/2,因为系统最大没有10。

第一步: -10+1=-9

第二步:求-9/2=-4

第三步:再乘回去看差多少--> -4*2=-8 -->发现和-10相差-2

第四步:-2/2=-1

第五步:最后让-4加上-1等于-5

例子2:-15~14 问题-15/3=?

第一步:-15+1=-14

第二步:-14/3=-4

第三步:-4*3=-12 --> -15-(-12)=-3

第四步:-3/3=-1

第五步:-1+(-4)=-5

成功绕过-15无法转成绝对值的问题!

代码

public static int divide(int a, int b) {
    //在所有整数中只有系统最小值是没法转成绝对值的->怎么解决系统最小值转绝对值?
    if (a == Integer.MIN_VALUE && b == Integer.MIN_VALUE) {
        return 1;   //1.ab都是系统最小,除完返回1
    } else if (b == Integer.MIN_VALUE) {
        return 0;   //2.a不是最小,b系统最小,除完返回0  因为b的绝对值最大:a/b返回0
    } else if (a == Integer.MIN_VALUE) {    //3. a系统最小,b不是最小->再谈论
        if (b == negNum(1)) {
            //如果是系统最小除以-1则返回系统最大(力扣的约定)
            return Integer.MAX_VALUE;
        } else {
            //  a/b
            //  (a + 1) / b = c
            //  a - (b * c) = d
            //  d / b =e
            //  c + e
            int c = div(add(a, 1), b);
            return add(c, div(minus(a, multi(c, b)), b));
        }
    } else {    //4.  a、b都不是系统最小-->div方法直接用
        return div(a, b);
    }
}

七、完整代码

位图:

public class Code02_BitMap2 {

    // 这个类的实现是正确的
    public static class BitMap {

        private long[] bits;

        public BitMap(int max) {
            bits = new long[(max + 64) >> 6];
        }

        public void add(int num) {
            bits[num >> 6] |= (1L << (num & 63));
        }

        public void delete(int num) {
            bits[num >> 6] &= ~(1L << (num & 63));
        }

        public boolean contains(int num) {
            return (bits[num >> 6] & (1L << (num & 63))) != 0;
        }

    }

    public static void main(String[] args) {
        System.out.println("测试开始!");
        int max = 10000;
        BitMap bitMap = new BitMap(max);
        HashSet<Integer> set = new HashSet<>();
        int testTime = 10000000;
        for (int i = 0; i < testTime; i++) {
            int num = (int) (Math.random() * (max + 1));
            double decide = Math.random();
            if (decide < 0.333) {
                bitMap.add(num);
                set.add(num);
            } else if (decide < 0.666) {
                bitMap.delete(num);
                set.remove(num);
            } else {
                if (bitMap.contains(num) != set.contains(num)) {
                    System.out.println("Oops!");
                    break;
                }
            }
        }
        for (int num = 0; num <= max; num++) {
            if (bitMap.contains(num) != set.contains(num)) {
                System.out.println("Oops!");
            }
        }
        System.out.println("测试结束!");
    }

}

加减乘除

// 测试链接:https://leetcode.com/problems/divide-two-integers
public class Code03_BitAddMinusMultiDiv {

    public static int add(int a, int b) {
        int sum = a;
        while (b != 0) {
            sum = a ^ b;
            b = (a & b) << 1;
            a = sum;
        }
        return sum;
    }

    public static int negNum(int n) {
        return add(~n, 1);
    }

    public static int minus(int a, int b) {
        return add(a, negNum(b));
    }

    public static int multi(int a, int b) {
        int res = 0;
        while (b != 0) {
            if ((b & 1) != 0) {
                res = add(res, a);
            }
            a <<= 1;
            b >>>= 1;
        }
        return res;
    }

    public static boolean isNeg(int n) {
        return n < 0;
    }

    public static int div(int a, int b) {
        int x = isNeg(a) ? negNum(a) : a;
        int y = isNeg(b) ? negNum(b) : b;
        int res = 0;
        for (int i = 30; i >= 0; i = minus(i, 1)) {
            if ((x >> i) >= y) {
                res |= (1 << i);
                x = minus(x, y << i);
            }
        }
        return isNeg(a) ^ isNeg(b) ? negNum(res) : res;
    }

    public static int divide(int a, int b) {
        if (a == Integer.MIN_VALUE && b == Integer.MIN_VALUE) {
            return 1;
        } else if (b == Integer.MIN_VALUE) {
            return 0;
        } else if (a == Integer.MIN_VALUE) {
            if (b == negNum(1)) {
                return Integer.MAX_VALUE;
            } else {
                int c = div(add(a, 1), b);
                return add(c, div(minus(a, multi(c, b)), b));
            }
        } else {
            return div(a, b);
        }
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值