Fighting!

潜行者的沉默

Android studio 是Google官方定制的Android开发工具,俗话说工欲善其事必先利其器,充分利用开发工具不仅能大大提高工作效率,同时也是个人能力的一种体现。在此提供关键字和官方文档方便查阅和记忆

阅读全文 »

题目链接:Pascal’s Triangle

Given numRows, generate the first numRows of Pascal’s triangle.
For example, given numRows = 5,
Return

1
2
3
4
5
6
7
[
[1],
[1,1],
[1,2,1],
[1,3,3,1],
[1,4,6,4,1]
]

这道题比较简单,属于基础的数组操作。知道规律就好做了,它的规律如下:
1.每行的元素个数等于行数
2.每行的第一个元素和最后一个元素等于1
3.第三行起,每行除第一个和最后一个元素外之间的元素第i行第k个元素 = 行数i-1行第k个元素 + 行数i-1行第k-1元素
4.隐藏条件:当行数i和元素j的下表相等时 ,i行的第j个元素等于1
具体代码如下:

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
//方法一
public static List<List<Integer>> generate(int numRows){
List<List<Integer>> triangle = new ArrayList<>();
if(numRows <= 0){
return triangle;
}

for (int i = 0; i < numRows; i++) {
List<Integer> row = new ArrayList<>();
for (int j = 0; j < i+1; j++) {
if(j ==0 || j == i){
row.add(1);
}else {
row.add(triangle.get(i-1).get(j-1) + triangle.get(i-1).get(j));
}
}
triangle.add(row);
}
return triangle;
}


//方法二
public static int[][] pascal(int rowNums){
if(rowNums<=0)
return new int[1][1];

int[][] arr = new int[rowNums][];
for (int i = 0; i < rowNums; i++){
arr[i] = new int[i+1];
for (int j = 0; j < i+1; j++){
if(j == 0 || j == i){
arr[i][j] = 1;
}else {
arr[i][j] = arr[i-1][j-1] + arr[i-1][j];
}
}
}
return arr;
}

算法时间复杂度应该是O(1+2+3+…+n)=n(n-1)/2=O(n^2),空间上只需要二维数组来存储结果,不需要额外空间。

题目链接 Plus One

Given a non-negative number represented as an array of digits, plus one to the number.
The digits are stored such that the most significant digit is at the head of the list.

这道题很简单,就是考加法的进位运算问题对一个数组进行加一操作,具体思路:
维护一个进位,对每一位进行加一,然后判断进位,如果有继续到下一位,否则就可以返回了,因为前面不需要计算了。有一个小细节就是如果到了最高位进位仍然存在,那么我们必须重新new一个数组,然后把第一个为赋成1(因为只是加一操作,其余位一定是0,否则不会进最高位)。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static int[] plusOne(int[] digit){
int one = 1;
int sum = 0;
for(int i = digit.length -1 ; i >= 0; i--){
sum = digit[i] + one;
digit[i] = sum % 10; //取余
one = sum / 10; //取整
if(one == 0){ //无需进位,则返回即可
return digit;
}
}
int[] res = new int[digit.length + 1]; //当最高位仍需进位则重新new一个数组
res[0] = 1;
return res;
}

因为只需要一次扫描,所以算法复杂度是O(n),n是数组的长度。而空间上,一般情况是O(1),但是如果数是全9,那么是最坏情况,需要O(n)的额外空间。

hexo官方给了一些迁移的方法,不过它上面介绍的方法都是把博客文章从hexo系统迁移到其他博客系统的方法。然而我们这里要讨论的是:

当我们更换电脑的时候我们应该怎么办?

所以默认你已经成功利用hexo和github发布博客,如果还没有,网上很多资料。

阅读全文 »

一道排序体题需要考与面试官沟通的点

  • 有没有可能包含大量的重复元素
  • 是否近乎有序
  • 数据取值范围是否有限
  • 是否需要稳定排序
  • 是否用链式存储
  • 数据大小足够装载内存里

算法面试优秀不意味技术面试优秀

  • 考虑项目经历和项目中遇到的实际问题
  • 印象最深的bug
  • 面向对象
  • 设计模式
  • 网络相关:安全相关、内存相关、并发相关
  • 系统设计:scalability

技术面试优秀不意味着能够拿到Offer

创建自己的项目

  • 自己做小应用
  • 自己解决小问题
  • “不是项目”的项目:书籍的整理
  • 技术分享,blog、github

了解过去自身的思考行为方式

  • 遇到最大的挑战
  • 犯过的错误
  • 遭遇的失败
  • 最享受的工作内容
  • 遇到冲突处理方式
  • 做的最与众不同的事儿

准备好问面试官的问题

  • 整个小组的大概运行模式是怎么样的
  • 整个项目组的后续规划是如何
  • 这个产品中某个问题的解决方案
  • 为什么选择某些技术?标准?
  • 对某技术很感性局,是否有机会深入

算法面试没这么难

  • 没必要啃完《算法导论》
  • 高级数据结构和算法面试提及概率很低(红黑树、计算几何、B-Tree、数论、斐波那契堆、FFT)

准备范围

*关注基础算法和数据结构,非“有意思”的题目
*

  • 各种排序算法
  • 基础数据结构和算法实现:堆、二叉树、图…
  • 基础数据结构使用:链表、栈、队列、哈希表、图、Trie…
  • 基础算法:深度优先、广度优先、二分查找、递归…
  • 基本算法思想:递归、分支、回溯搜索、贪心、动态规划…

解决算法问题的整体思路

  • 注意题目中的条件
  • 当没有思路时(测试用例,暴力解法——>优化)
  • 优化算法(算法思路、数据结构、空间换时间、预处理数据、瓶颈除找解决方案)
  • 实际编写问题,极端条件的判断(数组为空、字符串为空、数据为NULL),代码规范(变量名、模块化、复用性)

一. git关联本地与远程分支

1
2
$ git branch --set-upstream-to=origin/dev2.2 master
Branch master set up to track remote branch dev2.2 from origin.

远程分支在前,本地分支在后。
关联之后就可以正常的pull代码了。

二.查看所有分支(本地、远程)

1
2
3
4
5
6
$ git branch -a
* master
remotes/origin/HEAD -> origin/master
remotes/origin/dev
remotes/origin/dev2.2
remotes/origin/master

三.当前关联的远程分支

1
2
$ git branch -vv
* master beb7725 [origin/dev2.2: behind 1] 首页本地记录上次关闭之前的状态

四.git merge 和 git merge –no-ff

Alt text
根据这张图片可以看出
Git merge –no-ff 可以保存你之前的分支历史。能够更好的查看 merge历史,以及branch 状态。
git merge 则不会显示 feature,只保留单条分支记录。

五.merge 与 rebase 的区别

1.Git merge 会生成一个新得合并节点,而rebase不会
比如:

1
2
3
      D---E test
/
A---B---C---F master

使用merge合并, 为分支合并自动识别出最佳的同源合并点: git merge master

1
2
3
      D--------E
/ \
A---B---C---F----G test, master

操作会舍弃 master 分支上提取的 commit,同时不会像 merge 一样生成一个合并修改内容的 commit,相当于把 master 分支(当前所在分支)上的修改在 deve 分支(目标分支)上原样复制了一遍:git rebase test

1
A---B---D---E---C'---F'   test, master

使用git pull时默认是merge, 加 –rebase参数使其使用rebase方式

1
git pull --rebase  

2.rebase和merge的不同适用场景

rebase场景:如果修改了某个公用代码的BUG,这个时候就应该是把所有的OEM版本分支rebase到这个修复BUG的分支上来,在rebase过程中,Git会要你手动解决代码上的冲突,你需要做的就是把修复BUG的代码放到目标分支代码里面去。rebase的结果是:所有的分支依然存在

merge场景:因为成员的代码开发工作已经完成了,也不需要再保留这个分支了,所以我们可以把这个成员分支merge到主分支上,当然冲突在所难免,手工解决的工作肯定逃不掉,但是利大于弊不是吗。merge以后,分支就不存在了,但是在git的所有分支历史中还能看到身影。

一般我们把别的分支合并到master时用merge,而把master合并到别的分支时会用到rebase的原因,这是因为master分支一般commit会比较频繁。

所以每次下拉代码fetch之后用rebase的原因就是:
本地commit之后,fetch远端代码,此时,远端代码可能会被若干人修改会有若干个commit,而本地就一个commit,然后git rebase的时候,是默认rebase 远端代码,此时会将本地commit应用到远端代码,也就只需要解决一次冲突,并且rebase之后没有新的commit,很友好。但是,如果使用merge,则会产生新的commit。

六.网上推荐的工作流一般是用fetch+rebase (相比pull+merge工作流更干净,不容易出错)

比如dev是你的公共开发分支*

git checkout dev # 本地切到公共分支

git pull # 将本地的dev更新

git checkout -b bug_101026 # 新建一个主题分支(一个bug,一个功能什么的)
… # 改动.. commit.. 测试…

git fetch origin # 更新upstream

git rebase origin/dev # 将你的commits移到的末尾

git checkout dev # 切换到公共分支

git pull # 更新公共分支

git rebase bug_101026 # 将你的主题分支加到公共分支的末尾

git push # 推送

Java中方法调用参数传递的方式是传值,尽管传的是引用的值而不是对象的值。(Does Java pass by refrence or pass by value)

基本数据类型与引用数据类型

  • 8种基本数据类型

  • 引用数据类型:类、接口类型、数组类型、枚举类型、注解类型。

区别:

  • 基本数据类型在被创建时,在栈上给其划分一块内存,将数值直接存储在栈上。

  • 引用数据类型在被创建时,首先要在栈上给其引用(句柄)分配一块内存,而对象的具体信息都存储在堆内存上,然后由栈上面的引用指向堆中对象的地址。

例如,有一个类Person,有属性name,age,带有参的构造方法,

Person p = new Person(“zhangsan”,20);

在内存中的具体创建过程是:

1.首先在栈内存中位其p分配一块空间;

2.在堆内存中为Person对象分配一块空间,并为其三个属性设初值””,0;

3.根据类Person中对属性的定义,为该对象的两个属性进行赋值操作;

4.调用构造方法,为两个属性赋值为”Tom”,20;(注意这个时候p与Person对象之间还没有建立联系);

5.将Person对象在堆内存中的地址,赋值给栈中的p;通过引用(句柄)p可以找到堆中对象的具体信息。

相关知识:

静态区: 保存自动全局变量和 static 变量(包括 static 全局和局部变量)。静态区的内容在整个程序的生命周期内都存在,由编译器在编译的时候分配。

堆区: 一般由程序员分配释放,由 malloc 系列函数或 new 操作符分配的内存,其生命周期由 free 或 delete 决定。在没有释放之前一直存在,直到程序结束,由OS释放。其特点是使用灵活,空间比较大,但容易出错

栈区: 由编译器自动分配释放,保存局部变量,栈上的内容只在函数的范围内存在,当函数运行结束,这些内容也会自动被销毁,其特点是效率高,但空间大小有限

文字常量区: 常量字符串就是放在这里的。 程序结束后由系统释放。

程序代码区:存放函数体的二进制代码。

Alt text

在Java中,所有的对象变量都是引用,Java通过引用来管理对象。然而在给方法传参时,Java并没有使用传引用的方式,而是采用了传值的方式。
例如下面的badSwap()方法:

1
2
3
4
5
6
public void badSwap(int var1, int var2)  
{
int temp = var1;
var1 = var2;
var2 = temp;
}

当badSwap方法结束时,原有的var1和var2的值并不会发生变化。即使我们用其它Object类型来替代int,也不会有变化,因为Java在传递引用时也是采用传值的方式。(译者注:这里是关键,全文的核心是:1. Java中对象变量是引用 2. Java中方法是传值的 3. 传方法中参数时,传递的是引用的值)
代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public void tricky(Point arg1, Point arg2)  
{
arg1.x = 100;
arg1.y = 100;
Point temp = arg1;
arg1 = arg2;
arg2 = temp;
}
public static void main(String [] args)
{
Point pnt1 = new Point(0,0);
Point pnt2 = new Point(0,0);
System.out.println("X: " + pnt1.x + " Y: " +pnt1.y);
System.out.println("X: " + pnt2.x + " Y: " +pnt2.y);
System.out.println(" ");
tricky(pnt1,pnt2);
System.out.println("X: " + pnt1.x + " Y:" + pnt1.y);
System.out.println("X: " + pnt2.x + " Y: " +pnt2.y);
}

执行main()的输出如下:

1
2
3
4
X: 0 Y: 0  
X: 0 Y: 0
X: 100 Y: 100
X: 0 Y: 0

这个方法成功地改变了pnt1的值,但pnt1和pnt2的交换却失败了!这是Java参数传递机制里最让人迷惑的地方。在main()中,pnt1和pnt2是Point对象的引用,当将pnt1和pnt2传递给tricky()时,Java使用的正是传值的方式,将这两个引用的传给了arg1和arg2。也就是说arg1和arg2正是pnt1和pnt2的复制,他们所指向的对象是相同的。详情可见下面的图示:

![Alt text](http://images.techhive.com/images/idge/imported/article/jvw/2000/05/03-qa-0512-pass1-100158781-orig.gif "Optional title") 在作为参数传递后,对象至少有两个引用指向自己
在main()中,引用被复制并以传值的方式进行传递,对象本身并不会被传递。因此,tricky()方法中pnt1所指向的对象发生了变化。因为传递的是引用的复制,因此引用的交换既不能引起对象的交换,更不会使原始引用发生变化。如图2所示,tricky()交换了arg1与arg2,但不会影响pnt1和pnt2。因此若想交换原始引用pnt1和pnt2,那么不能通过调用方法的方式来实现。
![Alt text](http://hi.csdn.net/attachment/201201/31/0_13279907114A2K.gif "Optional title") 在作为参数传递后,对象至少有两个引用指向自己

总结:####

  1. Java中对象变量是引用
  2. Java中方法是传值的
  3. 传方法中参数时,传递的是引用的值

结论

  1. 不管有木有出现异常,finally块中的代码都会执行
  2. 当try或catch中有return时,finally仍然会执行
  3. 当try或catch中有return时,finally是在return前执行的(此时并没有返回运算后的结果,而是先把运算结果保存起来,而后再去执行finally,此如果finally若有rentrn,则程序结束,若无则操作后跳回try或catch继续执行)
  4. finally中最好不要包含return,否则程序会提前推出,返回值不是catch或catch的值

注意

  • 例1
    1
    2
    try{} catch(){}finally{} return;
    显然程序按顺序执行。
  • 例2
    1
    2
    3
    4
    try{ return; }catch(){} finally{} return;
    程序执行try块中return之前(包括return语句中的表达式运算)代码;
    再执行finally块,最后执行try中return;
    finally块之后的语句return,因为程序在try中已经return所以不再执行。
  • 例3
    1
    2
    3
    4
    5
    try{ } catch(){return;} finally{} return;
    程序先执行try,如果遇到异常执行catch块,
    有异常:则执行catch中return之前(包括return语句中的表达式运算)代码,再执行finally语句中全部代码,
    最后执行catch块中return. finally之后也就是4处的代码不再执行。
    无异常:执行完try再finally再return.
  • 例4
    1
    2
    3
    try{ return; }catch(){} finally{return;}
    程序执行try块中return之前(包括return语句中的表达式运算)代码;
    再执行finally块,因为finally块中有return所以提前退出。
  • 例5
    1
    2
    3
    try{} catch(){return;}finally{return;}
    程序执行catch块中return之前(包括return语句中的表达式运算)代码;
    再执行finally块,因为finally块中有return所以提前退出。
  • 例6
    1
    2
    3
    4
    5
    try{ return;}catch(){return;} finally{return;}
    程序执行try块中return之前(包括return语句中的表达式运算)代码;
    有异常:执行catch块中return之前(包括return语句中的表达式运算)代码;
    则再执行finally块,因为finally块中有return所以提前退出。
    无异常:则再执行finally块,因为finally块中有return所以提前退出。

    finally块中改变返回值的特殊情况####

这里涉及到java的引用传递和值传递,首先要明白java全部都是传值的。对于基本数据类型的值是存在栈中的,它是将数据完完整整的拷贝,生成一个新的变量值;对于引用类型的数据既对象它的数据存在于堆中,栈中保存的是指向数据的引用地址,它是将对象的引用地址值拷贝了,所以修改了对象内容,但地址值是不变的。
测试代码code1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class FinallyTest {
public static void main(String[] args) {

System.out.println(new FinallyTest().test());;
}

static int test()
{
int x = 1;
try
{
x++;
return x;
}
finally
{
++x;
}
}
}

输出2
测试代码code2

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

public class FinallyTest2
{
public static void Main(string[] args)
{
/*测试test1*/
List<string>relist=test1();
foreach (var item in relist)
{
Console.WriteLine(item);
}
Console.ReadLine();
}
private static List<string> test1()
{
List<string> strlist = new List<string>();
strlist.Add("zs");
strlist.Add("ls");
strlist.Add("ww");
strlist.Add("mz");
try
{
strlist.Add("wq");
return strlist;
}
finally
{
strlist.Add("yyy");
}
}
}

测试输出结果:
zs
ls
ww
mz
wq
yyy

所以可得
如果finally中没有return语句,但是改变了要返回的值,这里有点类似与引用传递和值传递的区别,分以下两种情况:

1)如果return的数据是基本数据类型或文本字符串,则在finally中对该基本数据的改变不起作用,try中的return语句依然会返回进入finally块之前保留的值。

2)如果return的数据是引用数据类型,而在finally中对该引用数据类型的属性值的改变起作用,try中的return语句返回的就是在finally中改变后的该属性的值。

最终结论:
任何执行try或者catch中的return语句之前,都会先执行finally语句,如果finally存在的话。如果finally中有return语句,那么程序就return了,所以finally中的return是一定会被return的,编译器把finally中的return实现为一个warning。

0%