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
- Ryan和Monica的取钱问题
-
Tip
学习至少一个技术技巧
重复的CRUD代码,可以抽象到公共的基类接口
泛型设计
Share
分享一篇有观点和思考的技术文章