打家劫舍(4)(2023-09-19)

打家劫舍(4)

题目

沿街有一排连续的房屋。每间房屋内都藏有一定的现金。现在有一位小偷计划从这些房屋中窃取现金。

由于相邻的房屋装有相互连通的防盗系统,所以小偷 不会窃取相邻的房屋 。

小偷的 窃取能力 定义为他在窃取过程中能从单间房屋中窃取的 最大金额 。

给你一个整数数组 nums 表示每间房屋存放的现金金额。形式上,从左起第 i 间房屋中放有 nums[i] 美元。

另给你一个整数 k ,表示窃贼将会窃取的 最少 房屋数。小偷总能窃取至少 k 间房屋。

返回小偷的 最小 窃取能力。

题目分析

这里的核心是找到最少能偷的K间房
设x是预设的偷窃能力
f(x)是在x的偷窃能力下,能够偷的房屋
随着x的增大,f(x)总是能保持增加或者相等
我们只需要找到这个搜索能力值,并且满足f(x)正好小于k的情况

为图片添加水印


import numpy from PIL import Image from PIL import ImageDraw from PIL import ImageFont from numpy import fft i1 = Image.open("d:/abc.jpg") weigh = i1.size[0] heigh = i1.size[1] i2 = Image.new('RGB', (weigh, heigh), (255, 255, 255)) font = ImageFont.truetype("arial.ttf", 36) font.size = 36 draw = ImageDraw.Draw(i2) draw.text((0, 0), "Mark By Michaelssss", font=font, fill=(0, 0, 0)) i2 = i2.transpose(Image.ROTATE_180) draw = ImageDraw.Draw(i2) draw.text((0, 0), "Mark By Michaelssss", font=font, fill=(0, 0, 0)) arr1 = numpy.array(i1) arr2 = numpy.array(i2) final = numpy.uint8(fft.ifft2(arr2 * 40 + fft.fft2(arr1))) Image.fromarray(final).show() # Image.fromarray(numpy.uint8(arr2)).show() Image.fromarray(numpy.uint8(fft.fft2(numpy.array(final)) - fft.fft2(arr1))).show()

这样就能添加一个盲水印,感谢开源让我不用学习如何手撕FFT哈哈哈哈哈哈

网络路径是如何被发现的

网络路径是如何被发现的

最近突然好奇到,我们熟悉的IP网络是如何运作的,查了半天资料都是说TCP/IP那几层协议,根本没有说网络是如何运行的。那我们试试头脑风暴,再发明一次网络吧

一个理想的模型

网络是由无数个可以相互连接的点组成

一些限制

每个点是没有先验知识的,也就是说,点与点之间如果不是直接连接在一开始是不知道能否由通路的,但点能知道直接连通的道路

发现通信路径

在没有点与点之间没有先验知识的前提下,我们就只能通过BFS/DFS算法来发现路径
所以当第一次通信的时候,点与点之间的时间最优情况T(n^n) (假设每个点直接连接n个点,在第二跳就能找到通路)

问题

这个情况下,我们会发现性能是和我们现在的网络不同,应该是会很慢的

优化

如果需要提升性能,就需要增加先验知识

增加假设

点的直接通路中由特定的点具有网络通路的解释权,我们称之为高优先级点,高优先级点的位置应该预先内置在访问点处,高优先级点数量m << n

优化结果

性能会提升至T(m^m)

再增加假设

假设我们可以广播,亦即是发出一个信号,如果点能到达,则返回路径表,此时的实际访问次数是T(h) h是网络中的最先达成通路的深度

问题

在未达成通路之前,整个网络相当于开启了m^m^h个半连接,会产生信息风暴

优化

划分网络,减少同时建立的半连接数量

对应到我们真实的IP网络

这个时候是不是就有点像我们的现有的IP网络了

硬件层就是能直接连接的路径

需要中间转发的,就在IP协议层完成

比如ICMP就是我们的广播信号

在一些核心交换机上还存在手工配置的路由表,就是高优先级点的先验信息

网关就是我们的高优先级点

划分就是我们的掩码

当然这都是我瞎几把想的东西,和实际出入应该大了去了,IP网发展了这么多年,基本模型都有可能变了

又是面试总结时间

更新1:按照此思路实现的简单版本

又是面试总结时间

这次面试的是一个初创公司的职位,其中一道题是不可能现场能够给出正确答案的,这里我的思考用时为六个小时。

原命题是:

如何设计一个LRUCache的System。能在接近O(1)时间内完成操作

那么以前两次提到的如何问问题,我们先确定使用场景,这是一个K-V键值系统,其中K-V值很小,但是延时要求高。

我们要求是在普通的服务器环境中构建一个尽可能低延时的LRUCache系统。

第一点是:确定数据结构

因为要以O(1)时间做查询,那么Map结构是跑不掉的,那么我们知道Map从语义上来说不一定是有序的。为了达到有序一定会有个辅助数据结构,这个结构一定是有序的。这是在我脑子里面第一个是小堆。小堆的维持堆结构操作最坏是nlogn,那么在频繁的写时候,并不能满足O(1)的需求,放弃掉;

第二个出现在我脑海中的数据结构是树,同样维持树结构仍不能满足需求;

第三个是我已经离开了面试地点想到的,跳表

这不是一般意义上的跳表,我们按照时间先后使得跳表的查询能够想hash操作一样是计算出来的,此时的hash函数一定是个递增阶梯函数

自然而然的引出了我认为最正确的答案,则是对数据集进行划分。

第二点是:具体怎么做

首先我们按照内存大小进行划分,按照面试官的提示,内存为8GB左右,我们估算一个k-v键值对大约占用100bytes,那么最差会有8.6kw个键值对,我们知道CPU的一二三级缓存速度非常快,即使是遍历也会极快,我们假设CPU的三级缓存有16MB,则我们对数据块的划分为16MB一个那么我们就拥有了512个块,我们可以近似认为在其中遍历只需要O(1)的时间,假设我们拥有一个足够均匀的hash函数能够使得key平均的分布在者512个块,那么我们查询的时间为O(1)+O(k);k为块的大小远小于整体数据量,此时删除和查询一个或者插入一个元素,近似时间都在O(1)完成。

第三点是:一些其他问题

第一点是,我们的hash有可能不能使得key均匀分布

第二点是,假设服务器还部署了其他应用,那么还要能对空闲的块进行回收合并

总结是:

大部分这种面试题如果当场做不出,也很难当场做出的,要多和面试官探讨

编程珠玑|读书笔记

  • 给定40亿个随机排列的32位整数的顺序文件,找出一个不在文件中的整数

首先先估算大小:

一个32位整数4bytes

40亿则是4*4.000.000.000bytes = 15625000KB ~= 15258MB ~= 15GB

如果采用bitmap还可以降低为2GB左右的内存

显然,现在随便一台高级点的计算机可以达到此内存级别,那么内存充足的情况下,直接对这个文件做map,然后顺序遍历一遍,空间复杂度为O(n),时间复杂度为O(n)

在内存不足情况下,但是有额外的外存空间,我们做归并排序,然后遍历,时间复杂度为O(n+nlogn)

还有一种做法是标准的二分查找:

我们给定一个分割标准(这里用整数的范围做标准),使得40亿个整数能够均匀的分布在两个文件

然后找到哪部分的文件不够20亿个,重复上一步骤直到整数范围上限和下限相同,则此时上限=下限=缺失的文件

  • 给定一个英语词典,找出其中所有变位词集合

我们为每一个英文字母映射成一个素数,遍历字典,找出英文字母对应的素数乘积值一样的,即是变位词。

这个正确性由算术基本定理保证

翻转单链表

package com.liangyumingblog;

public class LinkList
{
    private class Node
    {
        int value;

        Node next;

        public Node(int value, Node next)
        {
            this.value = value;
            this.next = next;
        }
    }

    private Node head = new Node(-1, null);

    private int size = 0;

    public void add(int value)
    {
        size++;
        Node current = head;
        while (current.next != null)
        {
            current = current.next;
        }
        current.next = new Node(value, null);
    }

    public Node getLast()
    {
        Node current = head;
        while (current.next != null)
        {
            current = current.next;
        }
        return current;
    }

    public Node removeLast()
    {
        Node current = head;
        while (current.next.next != null)
        {
            current = current.next;
        }
        Node tmp = current.next;
        current.next = null;
        tmp.next = null;
        return tmp;
    }

    public void reverse()
    {
        int length = 0;
        while (length != size)
        {
            insert(length, removeLast());
            length++;
        }
    }

    public boolean insert(int index, Node node)
    {
        if (index > size)
        {
            return false;
        }
        int now = 0;
        Node current = head;
        while (now != index)
        {
            ++now;
            current = current.next;
        }
        Node tmp = current.next;
        current.next = node;
        node.next = tmp;
        return true;
    }

    @Override
    public String toString()
    {
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append("[");
        Node current = head;
        while (current.next != null)
        {
            if (head != current)
            {
                stringBuffer.append(current.value);
                stringBuffer.append(", ");
            }
            current = current.next;
        }
        stringBuffer.append(current.value);
        stringBuffer.append("]");
        return stringBuffer.toString();
    }
}

 

趁着还没睡着,记一个今天学到的小小的分页trick

(totalrow+singlePageSize-1 )/singlePageSize

为什么说是个小trick呢

直观上我们做分页算法应该是这样的

return totalSize%singlePageSize!=0?totalSize/singlePageSize+1:totalSize/singlePageSize;

但是呢,最开始提到的算法,如果total/single有余数,由于int的直接截取的性质,所以totalrow/singlePageSize是向下取整了,但是这个余数加上(singlePageSize-1)再去除singlePagesize就刚好能够+1

否则因为(singePageSize-1)/SinglePageSize向下取整的性质,就变成了+0。所以说这里这个小Trick我还真没想到过,很巧妙

Sort

基本的冒泡(O(n^2)):

public static void bubbleSort(int[] arrays, int begin, int end)
    {
        assert arrays != null;
        for (int i = begin; i < end; i++)
        {
            for (int j = begin; j < end; j++)
            {
                if (arrays[i] > arrays[j])
                {
                    swap(arrays, i, j);
                }
            }
        }
    }

    private static void swap(int[] arrays, int a, int b)
    {
        int t = arrays[a];
        arrays[a] = arrays[b];
        arrays[b] = t;
    }

继续阅读“Sort”

字符串左移N位

第一种,简洁算法

根据矩阵运算中的\left(AB\right)^{\mathrm {T} }=B^{\mathrm {T} }A^{\mathrm {T} }可得

package com.liangyumingblog;

public class RollUpString
{
    private String string;

    public RollUpString(String s)
    {
        this.string = s;
    }

    public String rollup(int n)
    {
        StringBuilder s1 = new StringBuilder(string.substring(0, n));
        StringBuilder s2 = new StringBuilder(string.substring(n + 1));
        return s1.reverse().append(s2.reverse().toString()).reverse().toString();
    }
}

继续阅读“字符串左移N位”