当前位置: 首页 > news >正文

【读书笔记】《编程珠玑》第二章之算法设计的重要性

这一章的前言部分很值得一看,特别是作者提到的另他印象深刻的一句话

A problem that seems difficult may have a simple, unexpected solution.

这章的原版名"Aha! Insight",aha moment 的论述在心理学的领域早就诞生了,而且这个概念是有他的逻辑的,真是非常的神奇。Google 一下能看很多这方面的文章,知乎也有挺多的,似乎能总结出一些方法论呢,对咱平凡人思考问题解决问题确实有技巧性的提高,另外也很符合《Programming Pearls》所倡导的理念。

想想这章总结咱可以放前头了,特简单直白。原文是这么说的:

Unlike the advanced methods, the aha! insights of algorithms don't come only after extensive study; they're available to any programmer willing to think seriously before, during and after coding. 

有点『曲不离口拳不离手』的意思,是不?好了,让我们看看这章主要阐述的问题吧!

问题

  1. 给定一个最多包含40亿个随机排列的32位整数的顺序文件,找出一个不在文件中的32位整数(在文件中至少缺少一个这样的数,为什么?)。在具有足够内存的情况下,如何解决该问题?如果有几个外部的“临时”文件可用,但是仅有几百字节的内存,又该如何解决该问题?

  2. 将一个n元一维向量左旋转i个位置。例如,当n=8且i=3时,向量abcdefgh旋转为defghabc。简单的代码使用一个n元的中间变量在n步内完成该工作。你能否仅使用数十个额外字节的存储空间,在正比于n的时间内完成向量的旋转?

  3. 给定一个英语词典,找出其中的所有变位词集合。例如,“pots","stop"和“tops"互为变位词,因为每个单词都可以通过改变其他单词中字母的顺序来得到。

 

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~思考5分钟先~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

 

好了,现在看看难吗?不是非常难吧。我相信解决方案多少都能有的,先说说我的。问题1,这里偷个懒可以直接用第一章的位图来解决,这需要一定的内存。问题2,因为左旋转数已知故按目标位替换。问题3,5分钟内没想出来,但可以摸出一种极其极其低效的方法,找出所有排列,然后比对,最后组合。当然希望你能够发现,最后一种方法在算法可行性的考虑上是不过关的:(

解决方案

对于问题1,作者给出的方案是从每个整数的32位的视角来考虑二分搜索。像对待位图那样通过每一位的0 or 1来设计算法,将数据二分。具体如下图所示:

总归是 2^32 = 4 294 967 296 > 4 000 000 000,一定是少数字的。现在就说假设2^32的全集为E,现有的40亿个数是C,少的那个集是D,我们总有:C + D = E。

那么经过0/1探测器二分后的数据集假设为 A 和 B,这里利用递归的思路,再通过服从单调递减的选集策略,筛选出至最后一个集合是空集,那这次空集的标记就是D集中的了,记录每次探测(分组)的标记就可以得到那个缺少的数了,代码如下:

package chpt2;

import com.google.common.primitives.Ints;

import java.text.DecimalFormat;
import java.util.LinkedList;
import java.util.List;

/**
 * Created by wqi on 2016/10/23.
 */
public class Question2 {
    private final char FLAG_A = '1';// 探测标志
    private final char FLAG_B = '0';// 如上

    private String D="";// 没出现的数
    private int count = 0;// 相当于探测bit位的游标
    private int digit;// 样本的二进制长度

    public static void main(String[] args) {
        int[] shu = {0, 1, 2, 3, 4, 5, 7};
        Question2 q = new Question2();
        List<Integer> integers = Ints.asList(shu);
        q.digit = 3;
        q.findAbsent(integers);
    }

    private List<Integer> group(List<Integer> E, char flag) {
        List<Integer> result = new LinkedList<>();
        for (int i : E) {
            String s = Integer.toBinaryString(i);
            DecimalFormat df = new DecimalFormat(generatePattern(this.digit));
            char c = df.format(Integer.valueOf(s)).charAt(this.count - 1);
            if (c == flag) {
                result.add(i);
            }
        }
        return result;
    }

    private String generatePattern(int count) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < count; i++) {
            sb.append(0);
        }
        return sb.toString();
    }

    public void findAbsent(List<Integer> C) {
        count = Integer.toBinaryString(C.size()).length();
        List<Integer> A = this.group(C, FLAG_A);
        List<Integer> B = this.group(C, FLAG_B);
        if (C.size() == 0) {
            String s = new StringBuilder(D).reverse().toString();
            System.out.println("the absent number is " + Integer.parseInt(s, 2));
            return;
        }
        if (A.size() > B.size()) {
            D += FLAG_B;
            this.findAbsent(B);
        } else {
            D += FLAG_A;
            findAbsent(A);
        }
    }
}

大体的逻辑实现如上,也还有待完善的地方,例如:

没有把输入样本都转为32位整数(so lazy);

没有充分考虑数集的稀疏程度和数组的长度做到弹性的匹配,比方说{242,32342432,6744566,...}就这种无规律来10个,也就是探测的次数没有和位数近似的场景。想了下,最后根据已知的输入整数位数对String D进行补0或者补1就可以了。

 

 

 

 

因为都是一边在这写一边实现的,时间长了有点累,另外说个写读书笔记的感受,就是这样读书的进度快不起来,但有助于消化书本的知识同时也加深理解。

 

本章还有两题,以及课后的习题讲解没涉及,待续。。。

转载于:https://www.cnblogs.com/benma/p/5990804.html

相关文章:

  • Web:AJAX的网络请求
  • Lambda表达式详解(转载)
  • JMeter 配置元件之计数器Counter
  • signalr-源码
  • iOS开发之内购-AppStore
  • matplotlib —— 添加文本信息(text)
  • linux下压缩包的解压
  • [Java][Liferay] File system in liferay
  • 用for、while、do-while循环输出10句“好好学习,天天向上!”
  • 常见标签的全称
  • 【EntityFramework Core】实体实例化注入
  • apiCloud中的API对象
  • 静态链接
  • 多大开始玩EV3
  • HTTP/2探索第一篇——概念
  • [分享]iOS开发 - 实现UITableView Plain SectionView和table不停留一起滑动
  • 《Javascript数据结构和算法》笔记-「字典和散列表」
  • 【Redis学习笔记】2018-06-28 redis命令源码学习1
  • Javascript编码规范
  • JavaScript学习总结——原型
  • k8s 面向应用开发者的基础命令
  • Web设计流程优化:网页效果图设计新思路
  • 两列自适应布局方案整理
  • 如何编写一个可升级的智能合约
  • #define、const、typedef的差别
  • #vue3 实现前端下载excel文件模板功能
  • #我与Java虚拟机的故事#连载15:完整阅读的第一本技术书籍
  • $.ajax()
  • $.ajax,axios,fetch三种ajax请求的区别
  • (03)光刻——半导体电路的绘制
  • (06)Hive——正则表达式
  • (1)(1.9) MSP (version 4.2)
  • (10)STL算法之搜索(二) 二分查找
  • (13)[Xamarin.Android] 不同分辨率下的图片使用概论
  • (Java岗)秋招打卡!一本学历拿下美团、阿里、快手、米哈游offer
  • (三)elasticsearch 源码之启动流程分析
  • (生成器)yield与(迭代器)generator
  • (十七)Flask之大型项目目录结构示例【二扣蓝图】
  • (四)模仿学习-完成后台管理页面查询
  • (学习日记)2024.01.19
  • (转)c++ std::pair 与 std::make
  • (转)linux 命令大全
  • .bat批处理(五):遍历指定目录下资源文件并更新
  • .desktop 桌面快捷_Linux桌面环境那么多,这几款优秀的任你选
  • .NET 6 Mysql Canal (CDC 增量同步,捕获变更数据) 案例版
  • .NET CF命令行调试器MDbg入门(一)
  • .Net FrameWork总结
  • .NET 反射的使用
  • .net 写了一个支持重试、熔断和超时策略的 HttpClient 实例池
  • .NET 中让 Task 支持带超时的异步等待
  • .NET简谈设计模式之(单件模式)
  • .NET精简框架的“无法找到资源程序集”异常释疑
  • .Net中wcf服务生成及调用
  • /使用匿名内部类来复写Handler当中的handlerMessage()方法
  • @SpringBootApplication 包含的三个注解及其含义