Long Luo's Life Notes

每一天都是奇迹

By Long Luo

今天重新刷了下手机,结果之前的备份都是2个月前的,刷完机我就想了下,我手机必备App是哪些呢?

列出了下面这张必备App清单:

新闻

  1. ZAKER: 查看新闻,根据你的爱好订阅文章
  2. 新浪微博:作为微博的重度用户,微博的媒体属性很强,大于其社交属性。我的不少朋友已经告别微博了,但是我还是喜欢微博。与其在微信上与熟悉的人聊天,不如在微博上和陌生人交流。
  3. 网易新闻:交互做的很好,163评论常有亮点。

SNS·社交

  1. QQ: 不用说。超级App,超好的用户体验,本土化,非常强大。
  2. 微信:不用说。腾讯系的很多产品都极大的提高了我们的工作效率和生活效率,一定要用好他们。
  3. 微博:我用微博主要是学习和了解别人的很多观点,还能够结交很多朋友。
  4. LINE:主要是微博和微信的熟人太多,有些东西不好在朋友圈里吐槽,但LINE没有一个熟人,所以可以很方便的在朋友圈里吐槽发泄:-)
  5. LinkedIn:工作商务类的,也有很多干货。

阅读·电子书

  1. 知乎日报:打发碎片时间,同时了解很多各行各业的知识。
  2. 知乎:重度知乎用户,可以看到更大的世界,拓展你的思维界限。
  3. 多看阅读:电子书,在阅读体验我个人觉得是最好的,书的资源很多而且也不是很贵。

理财

  1. 支付宝:不用说。
  2. 招行手机App:主要用的都是招行卡。
  3. 招行信用卡:平常能刷信用卡的地方绝对使用信用卡。

消费·购物

  1. 手机淘宝:Shopping
  2. 京东:买书,购物等
  3. 美团:看电影,吃饭,订餐等O2O

股票证券类

  1. 广发证券:目前炒股的账户在广发。
  2. 自选股:腾讯系出品的一款炒股软件,美中不足的是交易不支持广发证券。
  3. 东方财富:炒股和市场行情。

交通出行

  1. 百度地图:国内最好的地图产品,对于我重度地图爱好者是真爱。
  2. Google地图:在国内被百度地图秒杀,但国外的话就是Google地图了。
  3. 爱帮公交:公交地铁线路图,还可以离线查询。
  4. 12306:火车出行必备

电子邮件

  1. QQ邮箱:很好用的一个电子邮件客户端,同时对Gmail的支持性很好。
  2. 邮箱大师:网易出品的一个邮箱客户端,这2个都可以。

时间管理·便签

  1. FitMemo: 非常轻量级的一款便签类,美中不足的是好久没有更新了,不支持云同步功能。
  2. Any.Do:支持多平台同步,非常好的时间管理App,用了就知道。
阅读全文 »

By Long Luo

上次在迅雷面试的时候,遇到了一个算法题,题目是:

有一个很长很长的字符串,全部都是由大写字母组成,要求求出其中每个字母在这个字符串中出现的次数。 不允许使用STL中的方法。

当时拿到这个题目,我首先想到了以下几个方法:

  1. 穷举法,一个个比较,最后算出每个字母出现的次数,这种方法可行,但不轻巧与优雅。
  2. 每个字符与’A’想减,会得到一个,统计下这个值出现次数,和方法1类似。(事后回顾这个已经很接近了,但是还是没能完美解决。)

其实这个题目,事后回顾起来,是比较简单的,但遗憾的是,当时未能在规定时间内解答出来,导致未能通过面试,拿到Offer,在此将这个题目记录下来。

重新认真解答这个题目。

How?

其实这个题目有一个很简单的解决方法,新建一个数组,大小为26,将字符串中每个字符都与'A'相减,得到的就是每个字母在数组中的元素下标值,我们最后得到的这个数组就是每个字母在这个字符串中出现的次数。

根据以上分析,可以写出如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package com.algorithm.alphabetSort;

/**
* 有一个很长的字符串,其中都是一些字母,求其中每个字母出现的次数(大小写区分)。
*
* @author luolong
* @date: 2015-04-12 00:54:17
*
*/
public class AlphabetSort {
private static String str = "AWQEYIOAHDHDKKLDLAHFHJALAFHANNAFGJCXCKBZCQIEOPADHAZBZVCFGCSHDJCKCLDMDHFAKAIIAYQO";

private static int[] outArray = new int[26];

public static void main(String[] args) {
AlphabetSortString(str);
printSortArray(outArray);
}

public static void AlphabetSortString(String str) {
char cTemp;

for (int i = 0; i < str.length(); i++) {
cTemp = str.charAt(i);

outArray[cTemp - 'A']++;
}
}

private static void printSortArray(int[] arr) {
for (int i = 0; i < arr.length; i++) {
char c = (char) ('A' + i);
System.out.print("" + c + "=" + arr[i] + " ");
}
}

}

引申与扩展一

如果字符串不仅仅是大写字母,而是大小写字母都存在的情况下,那应该如何写呢?

其实也很简单,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
if (cTemp >= 'A' && cTemp <= 'Z') {
/**
* 大写字母
*/
outArray[cTemp - 'A']++;
} else if (cTemp >= 'a' && cTemp <= 'z') {
/**
* 小写字母
*/
outArray[cTemp - 'a' + 26]++;
} else {

}
阅读全文 »

By Long Luo

最近在学习Java多线程时,遇到了一个下面的笔试题,题目如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
编写一个程序,程序会启动4个线程,向4个文件A,B,C,D里写入数据,每个线程只能写一个值。 
线程A:只写A
线程B:只写B
线程C:只写C
线程D:只写D

4个文件A,B,C,D。

程序运行起来,4个文件的写入结果如下:
A:ABCDABCD...
B:BCDABCDA...
C:CDABCDAB...
D:DABCDABC...

网上搜索了下,好像还是一个Google笔试题,这个问题涉及到的知识点有:多线程, 并发, , 线程间通信

个人分析过程:

  1. 创建4个线程;
  2. 每个线程在输出时都需要加锁;
  3. 操作文件的代码要加锁;
  4. 一个线程完成之后,通知下一个要执行的线程;

根据以上分析,可以写出如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
package com.imlongluo.Practise;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Practise {

public static void main(String[] args) {

final Task task = new Task();

/** 创建4个线程,同时启动 */
/**
* Thread A
*/
new Thread(new Runnable() {

@Override
public void run() {
for (int i = 0; i < 10; i++) {
task.outputA();
}
}
}, " Thread A").start();

/**
* Thread B
*/
new Thread(new Runnable() {

@Override
public void run() {
for (int i = 0; i < 10; i++) {
task.outputB();
}
}
}, "Thread B").start();

/**
* Thread C
*/
new Thread(new Runnable() {

@Override
public void run() {
for (int i = 0; i < 10; i++) {
task.outputC();
}
}
}, "Thread C").start();

/**
* Thread D
*/
new Thread(new Runnable() {

@Override
public void run() {
for (int i = 0; i < 10; i++) {
task.outputD();
}
}
}, "Thread D").start();
}

/**
任务类
*/
public static class Task {
/**
创建一个Lock锁对象
*/
private Lock lock = new ReentrantLock();

private BufferedWriter bw1 = null;
private BufferedWriter bw2 = null;
private BufferedWriter bw3 = null;
private BufferedWriter bw4 = null;

private int ctl = 0;

/**
* Condition: 线程条件
*/
private Condition cond1 = lock.newCondition();
private Condition cond2 = lock.newCondition();
private Condition cond3 = lock.newCondition();
private Condition cond4 = lock.newCondition();

private boolean[] outputThisRound = { false, true, true, true };

public Task() {
try {
bw1 = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(
"/Users/luolong/Code/Android/workspace/MultiThreads/A.txt"))));
bw2 = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(
"/Users/luolong/Code/Android/workspace/MultiThreads/B.txt"))));
bw3 = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(
"/Users/luolong/Code/Android/workspace/MultiThreads/C.txt"))));
bw4 = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(
"/Users/luolong/Code/Android/workspace/MultiThreads/D.txt"))));
} catch (Exception e) {
e.printStackTrace();
}
}

public void outputA() {

lock.lock();

try {
while (outputThisRound[0]) {
System.out.println("outputA begin to await!");
cond1.await(); //阻塞A线程
}

if (ctl % 4 == 0) {
bw1.write("A");
bw1.flush();
} else if (ctl % 4 == 1) {
bw4.write("A");
bw4.flush();
} else if (ctl % 4 == 2) {
bw3.write("A");
bw3.flush();
} else if (ctl % 4 == 3) {
bw2.write("A");
bw2.flush();
}

outputThisRound[0] = true;
outputThisRound[1] = false;

System.out.println("outputA signal outputB!");
cond2.signal(); //唤醒B线程
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}

}

public void outputB() {

lock.lock();

try {
while (outputThisRound[1]) {
System.out.println("outputB begin to await!");
cond2.await();
}

if (ctl % 4 == 0) {
bw2.write("B");
bw2.flush();
} else if (ctl % 4 == 1) {
bw1.write("B");
bw1.flush();
} else if (ctl % 4 == 2) {
bw4.write("B");
bw4.flush();
} else if (ctl % 4 == 3) {
bw3.write("B");
bw3.flush();
}

outputThisRound[1] = true;
outputThisRound[2] = false;
System.out.println("outputB signal outputC!");
cond3.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}

public void outputC() {
lock.lock();
try {
while (outputThisRound[2]) {
System.out.println("outputC begin to await!");
cond3.await();
}

if (ctl % 4 == 0) {
bw3.write("C");
bw3.flush();
} else if (ctl % 4 == 1) {
bw2.write("C");
bw2.flush();
} else if (ctl % 4 == 2) {
bw1.write("C");
bw1.flush();
} else if (ctl % 4 == 3) {
bw4.write("C");
bw4.flush();
}

outputThisRound[2] = true;
outputThisRound[3] = false;
System.out.println("outputC signal outputD!");
cond4.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}

public void outputD() {
lock.lock();

try {
while (outputThisRound[3]) {
System.out.println("outputD begin to await!");
cond4.await();
}

if (ctl % 4 == 0) {
bw4.write("D");
bw4.flush();
} else if (ctl % 4 == 1) {
bw3.write("D");
bw3.flush();
} else if (ctl % 4 == 2) {
bw2.write("D");
bw2.flush();
} else if (ctl % 4 == 3) {
bw1.write("D");
bw1.flush();
}

outputThisRound[3] = true;
outputThisRound[0] = false;
ctl++;
System.out.println("outputD signal outputA!");
cond1.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}

以上。

如果大家还有其他更好的方法,欢迎一起讨论:-)

Created by Long Luo at 2015-04-09 22:14:02 @Shenzhen, China. Completed By Long Luo at 2015-04-09 23:46:29 @Shenzhen, China.

By Long Luo

最近去面试时,在一家小公司面试时,公司小BOSS给我出了一道算法题:

一个人爬楼梯,一步可以迈一级,二级,三级台阶,如果楼梯有 \(N\) 级,要求编写程序,求总共有多少种走法。

这个问题应该是一个很老的题目了,用中学数学来说,就是一个排列组合问题。当时拿到这个题目之后,首先想到使用递归的思想去解决这个问题:

N级楼梯问题可以划分为:\(N-1\) 级楼梯,\(N-2\) 级楼梯,\(N-3\) 级楼梯的走法之和。

先计算下0,1,2,3及楼梯有多少种走法:

1
2
3
1 --> 1
2 --> 11 2
3 --> 111 12 21 3

那么,根据以上的分析很容易写出如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public static int countNumber(int stepsNum) {
int sum = 0;

if (stepsNum == 0) {
return 0;
}

if (stepsNum == 1) {
return 1;
} else if (stepsNum == 2) {
return 2;
} else if (stepsNum == 3) {
return 4;
} else if (stepsNum > 3) {
return countNumber(stepsNum - 3) + countNumber(stepsNum - 2)
+ countNumber(stepsNum - 1);
}

return sum;
}

public static void main(String[] args) {

for (int i = 0; i <= 10; i++) {
System.out.println("楼梯台阶数:" + i + ", 走法有:" + countNumber(i));
}
}

再看看输出:

1
2
3
4
5
6
7
8
9
10
楼梯台阶数:0, 走法有:0
楼梯台阶数:1, 走法有:1
楼梯台阶数:2, 走法有:2
楼梯台阶数:3, 走法有:4
楼梯台阶数:4, 走法有:7
楼梯台阶数:5, 走法有:13
楼梯台阶数:6, 走法有:24
楼梯台阶数:7, 走法有:44
楼梯台阶数:8, 走法有:81
楼梯台阶数:9, 走法有:149

如果求解具体全部走法呢?

仅仅算出有多少种走法是很容易的,如果更近一步呢?基于这个基础,如何输出具体的走法呢?

我们可以使用Stack数据结构和递归的思想去完成这个题目:Stack<T>用于保存每一步的走法。

代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/**
* 一个人爬楼梯,一步可以迈一级,二级,三级台阶,如果楼梯有N级,编写程序,输出所有走法。
*
* @param args
*/
public static void main(String[] args) {
Stack<Integer> stt = new Stack<Integer>();

buileT(stt, 3);
}

public static void buileT(Stack<Integer> stt, int N) {
if (N >= 1) {
stt.push(1);
buileT(stt, N - 1);
stt.pop();
}
if (N >= 2) {
stt.push(2);
buileT(stt, N - 2);
stt.pop();
}
if (N >= 3) {
stt.push(3);
buileT(stt, N - 3);
stt.pop();
}
if (N == 0) {
for (int i : stt) {
System.out.print("Step:" + i + "-->");
}
System.out.println("完成");
}
}

以上。

———- Updated at 2022.04.21 ———-

这篇文章写于2015年,其实这个题就是 Leetcode 1137. 第 N 个泰波那契数 ,是 70. 爬楼梯 的变体,解决方法可参考 9种求斐波那契数(Fibonacci Numbers)的算法

Created by Long Luo at 2015-04-08 00:40:12 @Shenzhen, China. Completed By Long Luo at 2015-04-08 18:15:38 @Shenzhen, China. Updated By Long Luo at 2022年4月21日 14点51分 @Shenzhen, China.

By LongLuo

一、什么是404页面?

想必大家都有过这样的经历,好不容易看到搜索引擎出现了我们想要找了很久的内容,兴冲冲点进去,然而出现在屏幕上的却是一个大大404?仿佛在逗你玩?WTF?

不用怀疑,你想要的页面丢失了。

404错误信息通常是在目标页面被更改或移除,或客户端输入页面地址错误后显示的页面。

那么,404除了代表你所要浏览的页面丢失外,你是否知道它的产生原理呢?

不仅如此,在庞大的互联网中除了404还有哪些HTTP状态码?它们又分别代表着什么?

正如上面所述,404是一种标准的HTTP返回代码,官方称其为HTTP状态码,用来表示网页服务器HTTP的响应状态

由于网站日志通常会记录下HTTP状态码,因此通过查看网站日志中的HTTP状态码,便可清楚地看到网站服务器与客户端之间的信息交换情况。

1.1 HTTP状态码

下面是常见的HTTP状态码:

  • 200 - 请求成功
  • 301 - 资源(网页等)被永久转移到其它URL
  • 404 - 请求的资源(网页等)不存在
  • 500 - 内部服务器错误

1.1.1 HTTP状态码分类

HTTP状态码由三个十进制数字组成,第一个十进制数字定义了状态码的类型,后两个数字没有分类的作用。

HTTP状态码共分为5种类型:

分类分类描述
1xx信息,服务器收到请求,需要请求者继续执行操作
2xx成功,操作被成功接收并处理
3xx重定向,需要进一步的操作以完成请求
4xx客户端错误,请求包含语法错误或无法完成请求
5xx服务器错误,服务器在处理请求的过程中发生了错误

HTTP状态码列表:

状态码状态码英文名称中文描述
100Continue继续。客户端应继续其请求
101Switching Protocols切换协议。服务器根据客户端的请求切换协议。只能切换到更高级的协议,例如,切换到HTTP的新版本协议
200OK请求成功。一般用于GET与POST请求
201Created已创建。成功请求并创建了新的资源
202Accepted已接受。已经接受请求,但未处理完成
203Non-Authoritative Information非授权信息。请求成功。但返回的meta信息不在原始的服务器,而是一个副本
204No Content无内容。服务器成功处理,但未返回内容。在未更新网页的情况下,可确保浏览器继续显示当前文档
205Reset Content重置内容。服务器处理成功,用户终端(例如:浏览器)应重置文档视图。可通过此返回码清除浏览器的表单域
206Partial Content部分内容。服务器成功处理了部分GET请求
300Multiple Choices多种选择。请求的资源可包括多个位置,相应可返回一个资源特征与地址的列表用于用户终端(例如:浏览器)选择
301Moved Permanently永久移动。请求的资源已被永久的移动到新URI,返回信息会包括新的URI,浏览器会自动定向到新URI。今后任何新的请求都应使用新的URI代替
302Found临时移动。与301类似。但资源只是临时被移动。客户端应继续使用原有URI
303See Other查看其它地址。与301类似。使用GET和POST请求查看
304Not Modified未修改。所请求的资源未修改,服务器返回此状态码时,不会返回任何资源。客户端通常会缓存访问过的资源,通过提供一个头信息指出客户端希望只返回在指定日期之后修改的资源
305Use Proxy使用代理。所请求的资源必须通过代理访问
306Unused已经被废弃的HTTP状态码
307Temporary Redirect临时重定向。与302类似。使用GET请求重定向
400Bad Request客户端请求的语法错误,服务器无法理解
401Unauthorized请求要求用户的身份认证
402Payment Required保留,将来使用
403Forbidden服务器理解请求客户端的请求,但是拒绝执行此请求
404Not Found服务器无法根据客户端的请求找到资源(网页)。通过此代码,网站设计人员可设置”您所请求的资源无法找到”的个性页面
405Method Not Allowed客户端请求中的方法被禁止
406Not Acceptable服务器无法根据客户端请求的内容特性完成请求
407Proxy Authentication Required请求要求代理的身份认证,与401类似,但请求者应当使用代理进行授权
408Request Time-out服务器等待客户端发送的请求时间过长,超时
409Conflict服务器完成客户端的 PUT 请求时可能返回此代码,服务器处理请求时发生了冲突
410Gone客户端请求的资源已经不存在。410不同于404,如果资源以前有现在被永久删除了可使用410代码,网站设计人员可通过301代码指定资源的新位置
411Length Required服务器无法处理客户端发送的不带Content-Length的请求信息
412Precondition Failed客户端请求信息的先决条件错误
413Request Entity Too Large由于请求的实体过大,服务器无法处理,因此拒绝请求。为防止客户端的连续请求,服务器可能会关闭连接。如果只是服务器暂时无法处理,则会包含一个Retry-After的响应信息
414Request-URI Too Large请求的URI过长(URI通常为网址),服务器无法处理
415Unsupported Media Type服务器无法处理请求附带的媒体格式
416Requested range not satisfiable客户端请求的范围无效
417Expectation Failed服务器无法满足Expect的请求头信息
500Internal Server Error服务器内部错误,无法完成请求
501Not Implemented服务器不支持请求的功能,无法完成请求
502Bad Gateway作为网关或者代理工作的服务器尝试执行请求时,从远程服务器接收到了一个无效的响应
503Service Unavailable由于超载或系统维护,服务器暂时的无法处理客户端的请求。延时的长度可包含在服务器的Retry-After头信息中
504Gateway Time-out充当网关或代理的服务器,未及时从远端服务器获取请求
505HTTP Version not supported服务器不支持请求的HTTP协议的版本,无法完成处理
阅读全文 »
0%