• ARTS习惯(8)


    Algorithm

    每周至少做一个Leetcode算法题

    第1道

    【来源】

    《剑指Offer》10#

    Leetcode 191#

    【题目】

    设计一个函数,输入整数n,输出n的二进制表示中1的个数

    例子

    输入:8
    输出:1
    解释:8的二进制表示1000
    

    【解答】

    如果第一直觉是用除k取余法,那证明你的基础不错,进制转换应该学的不错。只是,除法的效率远远差于位运算。

    若采用右移n,掩码固定的方案,不能解决n为负数时带来的死循环问题。

    采用固定n,掩码左移的方案可以很好解决以上问题,见解法1,此方案的可以优化的地方是循环的次数

    不断的将整数右边的1反转为0,反转的次数就是1的个数,此方案完善了循环次数稍多的问题

    【示例代码】

    package com.pengluo.hht_offer.T10_BitOperation;
    
    public class NumberOf1InBinary {
    
    
        /**
         * 解法1.掩码法
         * 时间复杂度:O(1)
         * 空间复杂度:O(1)
         *
         * @param number
         * @return number的二进制表示中1的个数
         */
        public int getNumberOf1(int number) {
            int count = 0;
            int flag = 1;
            while (flag != 0) {
                if ((number & flag) != 0) {
                    count++;
                }
                flag <<= 1;
            }
            return count;
        }
    
        /**
         * 解法2:定义法
         *   (n-1) & n 的效果就是将数字最后的1反转为0
         *  整数n的二进制表示中,最右边的1的位置为m,按照定义n-1的二进制表示,m位置坐左边的表示和n的一致,右边则全为1,
         *
         *  时间复杂度:O(1)
         *  空间复杂度:O(1)
         *
         * @param n
         * @return 二进制表示中1的个数
         */
        public int getNumberOf1ByDefiniton(int n) {
            int count = 0;
            while (n != 0) {
                count++;
                n &= n - 1;
            }
            return count;
        }
    
        /**
         * 变式1:n是否是2的整数次方
         * 解法:题目等价于求n的二进制表示中有且仅有1个1,用n-1和n做与运算就可以将1变成0
         * @param n
         * @return
         */
        public boolean isPowOfTwo(int n) {
            int count = getNumberOf1ByDefiniton(n);
            if (count == 1) {
                return true;
            }else {
                return false;
            }
        }
    
        /**
         * 变式2:输入source和target两个整数,返回source变成target需要改变的二进制的位数
         * 输入 source = 10, target = 13
         * 输出 3
         *
         * 解法:1.source和target做异或
         *      2.统计异或结果中1的个数
         *
         * @param source
         * @param target
         * @return
         */
    
        public int NumberOfChange(int source, int target) {
            int temp = source^target;
            return getNumberOf1ByDefiniton(temp);
        }
    
    }
    
    

    总结

    • 位运算的效率高于乘除法

    • (n-1)& n 达到将1反转为0的思路很使用

    Review

    阅读并点评至少1篇英文技术文章

    【原文】:Head First Java 2nd Edition CH15

    【点评】:

    • client 和 server之间的连接

      • socket 连接

        • 一个端口port只能绑定一个应用

        • port:16位无符号的数字,0~1023,为已知的应用分配了,不能使用,比如:HTTP,FTP协议

          1024~65535,程序员可以随意使用

      • client发送信息

        • // 1.和server连接
          Socket socket = new Socket("127.0.0.1", 5050);
          // 2.建立character转byte的桥梁
          PrintWriter writer = new PrintWriter(socket.getOutputStream());
          // 调用API
          writer.println("write testing");
          
      • client接受信息

        • // 1.和server建立连接
          Socket socket = new Socket("127.0.0.1", 5050);
          // 2.将底层的字节流转化为字符流
          InputStreamReader isr = new InputStreamReader(socket.getInputStream());
          // 3.缓存字符流
          BufferedReader reader = new BufferedReader(isr);
          // 使用API
          String message = reader.readLine();
          
    • 多线程

      • Thread class

        • // 和普通类一样,一个Thread类可以创建多个instance
          Thread one = new Thread();
          Thread two = new Thread();
          
      • Runnable接口

        • public class MyRunnable implements Runnable {
              @Override 
              public void run() {
                  // Runnable 唯一定义的方法
                  laugh();
              }
              
              // 自定义方法
              public void laugh() {}
          }
          
          
      • thread 线程执行(call stack)

        // Runnable is to thread what job is to worker.
        MyRunnable runnable = new MyRunable();
        Thread t1 = new Thread(runnable);
        Thread t2 = new Thread(runnable);
        // 线程启动后,自动执行runnable的run()方法,这是t的栈调用的最下面的方法
        t1.start();
        // 线程之间的切换,是由JVM按thread schduler自动完成,
        // 线程在running runnable blocked 三种状态进行切换
        t2.start();
        // Thread类的静态方法,休眠2s内,进入由ruuning态变成block,其他线程可以执行,2s后,进入runnable态
        // sleep的一个作用是强制让其他线程跑起来
        Thread.sleep(2000);
        
        
      • 并发问题

        • Ryan和Monica的取钱问题
          • synchronized加锁
          • atomic process
          • Object's lock
        • Lost Update问题
        • synchronized的问题
          • 影响性能表现
          • 拖慢你的程序运行
          • 最糟的是死锁问题(线程a,b分别拿着对方需要的锁的钥匙,互相等待)
          • 为了减少并发编程的死锁问题,推荐了Java Threads by Scott Oaks

    Tip

    学习至少一个技术技巧

    重复的CRUD代码,可以抽象到公共的基类接口

    泛型设计

    Share

    分享一篇有观点和思考的技术文章

    CodingTour大神的ARTS专栏

  • 相关阅读:
    文本标记
    第一个HTML文档
    HTML入门
    bootstrap fileinput 文件上传
    DPDK rte_hash 简述
    glib学习笔记-基本知识
    linux常用网络命令
    libevent学习过程
    C语言 singleton模式
    oracle命令行导出、导入dmp文件
  • 原文地址:https://www.cnblogs.com/PengLuo22/p/14269892.html
Copyright © 2020-2023  润新知