max_depth_tree

Given a binary tree, find its maximum depth.

The maximum depth is the number of nodes along the longest path from the root node down to the farthest leaf node.

Subscribe to see which companies asked this question

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
int maxDepth(TreeNode* root) {
if(root == NULL)
return 0;
return max(1+maxDepth(root->left),1+maxDepth(root->right));
}
};

这道题目就是不断的递归左右子树,父的深度比儿子深度大1,左右儿子深度做比较,得出最大深度。
可以看出递归思想在解决树问题的重要性。

invert_tree

Invert a binary tree.

     4
   /   \
  2     7
 / \   / \
1   3 6   9

to

     4
   /   \
  7     2
 / \   / \
9   6 3   1

Trivia:
This problem was inspired by this original tweet by Max Howell:

Google: 90% of our engineers use the software you wrote (Homebrew),
but you can’t invert a binary tree on a whiteboard so fuck off.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Subscribe to see which companies asked this question
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if(root == NULL)
return NULL;
root->left = invertTree(root->left);
root->right = invertTree(root->right);
TreeNode *tmp = root->left;
root->left = root->right;
root->right = tmp;
return root;
}
};

这到题目不难,主要用了递归的思想,反转二叉树就是每个节点下的左右儿子,我们需要从他的最下面开始反转,这里用递归就很合适。

二叉树的一些特点

性质

  1. 第i层的节点数为 2^(i-1);
  2. 深度为k的节点数为2^k-1 (k>=1);
  3. n0=n2+1 n0为度为0的节点就是叶节点,n2为度为2的节点
  4. n个节点数,则深度为(log2n)+1
  5. ①如果i>1,双亲节点就是i/2;②2i>n结点i无左儿子,否则左孩子节点是2i;③2i+1>n则节点没有右孩子,否则右
    孩子为2i+1;

二叉树的遍历

二叉树有前序遍历,中序遍历和后续遍历。
示例图(来源)
如上图所示的一棵二叉树,对应的遍历结果分别是:

  1. 前序(NLR):A B D C E G H F I
  2. 中序(LNR):D B A G E H C F I
  3. 后序(LRN):D B G H E I F C A
  4. 层序:A B C D E F G H I

按照程序的思想去理解这三种遍历

前序遍历

1
2
3
4
5
6
7
void preOrderTraverse(Bitree T){
if(T == NULL)
return;
printf("%d",T->val);
preOrderTraverse(T->lchild);//先序遍历左子树
preOrderTraverse(T->rchild);//先序遍历右子树
}

中序遍历

1
2
3
4
5
6
7
void preOrderTraverse(Bitree T){
if(T == NULL)
return;
preOrderTraverse(T->lchild);//中序遍历左子树
printf("%d",T->val);
preOrderTraverse(T->rchild);//中序遍历右子树
}

后序遍历

1
2
3
4
5
6
7
void preOrderTraverse(Bitree T){
if(T == NULL)
return;
printf("%d",T->val);
preOrderTraverse(T->lchild);//后序遍历左子树
preOrderTraverse(T->rchild);//后序遍历右子树
}

推导遍历结果

已知前序遍历为ABCDEF 中序遍历为CBAEDF 求后序遍历结果

因为先序遍历可以知道根节点,中序遍历可以知道左右树。
所以A为根,CB为左子树,EDF为右子树。
CB当中B为根,B为A的左儿子,C为B的儿子,因为在中序遍历中C先打印做C为B的左儿子。
DEF中D为根,E为D的左儿子,F为右儿子。
图1
图1
图2
图2
图3
图3

已知中序是ABCDEFG,后续是BDCAFGE,求先序遍历的结果。

根节点可以通过后续得知,中序知道左右子树,所以可以求出先序。
结果为EACBDGF
注意:前序遍历和后续遍历结果知道不能求出中序,因为两者都是只能得出根节点,左右子树不能确定。

path_sum_III

You are given a binary tree in which each node contains an integer value.
Find the number of paths that sum to a given value.
The path does not need to start or end at the root or a leaf, but it must go downwards (traveling only from parent nodes to child nodes).
The tree has no more than 1,000 nodes and the values are in the range -1,000,000 to 1,000,000.

root = [10,5,-3,3,2,null,11,3,-2,null,1], sum = 8

      10
     /  \
    5   -3
   / \    \
  3   2   11
 / \   \
3  -2   1

Return 3. The paths that sum to 8 are:

1.  5 -> 3
2.  5 -> 2 -> 1
3. -3 -> 11

题目就是求和为8的向下的路径数
1,2,3,4,5是连续的五段路程不能跳过。如果想到站点5到达各地有几条路程,5->4->3->2->15->4->3->25->4->3
5->4,有四条路径。找和为8的路径只是多加了个条件。首先我们以root为节点,向下查找,再以root的左右儿子为节点查找,然后左右儿子又以自己的左右儿子为节点向下查找。所以用递归来完成。

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
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
int pathSum(TreeNode* root, int sum) {
int res = 0;
if(root == NULL)
return res;
return dfs(root,sum)+pathSum(root->left,sum)+pathSum(root->right,sum);
}
int dfs(TreeNode* root,int sum){
int res = 0;
if(root == NULL)
return res;
if(root->val == sum)
res++;
res+= dfs(root->left,sum-root->val);
res+= dfs(root->right,sum-root->val);
return res;


}
};

这里的深度搜索dfs和下面求平衡二叉树时的depth是类似的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution {
public:
bool isBalanced(TreeNode* root) {
if(root == NULL)
return true;
if(root->left == NULL&&root->right == NULL)
return true;
if(abs(depth(root->left)-depth(root->right))>1)
return false;
return isBalanced(root->left)&&isBalanced(root->right);
}
int depth(TreeNode* root){
if(root == NULL)
return 0;
return max(1+depth(root->left),1+depth(root->right));
}
};

Balanced_Binary_Tree

判断是否是平衡二叉树

QuestionEditorial Solution My Submissions
Total Accepted: 147366
Total Submissions: 408360
Difficulty: Easy
Contributors: Admin
Given a binary tree, determine if it is height-balanced.
For this problem, a height-balanced binary tree is defined as a binary tree in which the depth of the two subtrees of every node never differ by more than 1.
Subscribe to see which companies asked this question

平衡二叉树:它是一 棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
如何解决这道题目一个思路就是按照递归的思路去做。先去判读根节点的左右两边的深度差是否大于1,小于的话再去判断他的子树是否为平衡二叉树。这里就要借助递归。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution {
public:
bool isBalanced(TreeNode* root) {
if(root == NULL)
return true;
if(root->left == NULL&&root->right == NULL)
return true;
if(abs(depth(root->left)-depth(root->right))>1)
return false;
return isBalanced(root->left)&&isBalanced(root->right);
}
int depth(TreeNode* root){
if(root == NULL)
return 0;
return max(1+depth(root->left),1+depth(root->right));
}
};

一开始我觉得这种算法很顺手的,思考的时候也很简单。但是我后来在leetcode上测了下时间有快20ms,就觉得有问题,原来一看这里又两层递归就是递归里面套递归,所以花费时间很长时间在这里面。
这种算法的时间复杂度为nlogn所以大量时间花费在了算书的深度上。
然后上网查了下有如下的算法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution {
public:
bool isBalanced(TreeNode* root) {
// write code here
int high=0;
return isBalance1(root,high);
}
bool isBalance1(TreeNode* root,int &high)
{
if(root==NULL)
{
high=0;
return true;
}
int lhigh,rhigh;
if(!isBalance1(root->left,lhigh)||!isBalance1(root->right,rhigh))
return false;
if(lhigh-rhigh>1||lhigh-rhigh<-1)
return false;
high=(lhigh>=rhigh?lhigh:rhigh)+1;
return true;
}
};

这种算法就用了一层递归,高度的递归在算平衡里面已经解决了,所以速度要比上面的要快。

网络之间的连接

主要研究了一下一次完整的网络请求所经历的几个步骤对OSI模型的二三层,特别是在数据链路层传中的ARP进行了初步的认知。
大致可以看下这段文字

主机A的IP地址为192.168.1.1,MAC地址为0A-11-22-33-44-01;
主机B的IP地址为192.168.1.2,MAC地址为0A-11-22-33-44-02;
当主机A要与主机B通信时,地址解析协议可以将主机B的IP地址(192.168.1.2)解析成主机B的MAC地址,以下为工作流程:
第1步:根据主机A上的路由表内容,IP确定用于访问主机B的转发IP地址是192.168.1.2。然后A主机在自己的本地ARP缓存中检查主机B的匹配MAC地址。
第2步:如果主机A在ARP缓存中没有找到映射,它将询问192.168.1.2的硬件地址,从而将ARP请求帧广播到本地网络上的所有主机。源主机A的IP地址和MAC地址都包括在ARP请求中。本地网络上的每台主机都接收到ARP请求并且检查是否与自己的IP地址匹配。如果主机发现请求的IP地址与自己的IP地址不匹配,它将丢弃ARP请求。
第3步:主机B确定ARP请求中的IP地址与自己的IP地址匹配,则将主机A的IP地址和MAC地址映射添加到本地ARP缓存中。
第4步:主机B将包含其MAC地址的ARP回复消息直接发送回主机A。
第5步:当主机A收到从主机B发来的ARP回复消息时,会用主机B的IP和MAC地址映射更新ARP缓存。本机缓存是有生存期的,生存期结束后,将再次重复上面的过程。主机B的MAC地址一旦确定,主机A就能向主机B发送IP通信了。

也就是说我们通信的时候不管怎么样都是需要获取Mac地址的,在同一个网络下可以直接使用ARP协议发送一个数据包。但是在不同的子网络下就是交给网关去处理。比如子网A下的a主机放送给子网B下的c数据时该怎么办呢?需要用到ARP协议吗。答案是肯定的。首先a找到网关的时候就要发送数据给网关,这里需要知道网关的ip地址和网关的mac地址,获取网关的mac地址就需要用到ARP协议,然后网关需要找到发送数据目的地建立连接,那么就又需要和子网B的网关建立连接发送数据,这里需要用到arp吗?那就看需要看第二层传递方式如果是以太网就需要用到ARP,如果是其他的传递方式可能就不需要(反正知道如果是),所以决定是否需要ARP,是由二层封装方式决定的。但是到了这里怎么和公网相连呢?如果是以太网就可能做一次广播,广播给他所在局域网内的网关。可能会通过多次路由转换到达目的地。
A-R1-R2-R3-B
A想要找到B,则只知道B的ip地址和自己的ip地址,对于A来说,B不在本地的链路上(B与A不属于同一个子网)则数据就交给路由器(网关)来处理。如果网关mac地址在本地ARP缓存中就直接连,如果没有就发起arp请求。到了R2收到数据包进行拆解,要找的IP地址是B,又不在自己的子网下,就交给R1的网关R2来处理,R2收到后重复前面的步骤交给网关R3,R3收到数据发现是在自己的子网下,就交给B来处理。
但是这里的原地址和目标地址是不会变得。
如果路由器实现NAT功能的话就是以下模式:
就比如是这个样子的:
A(10.1.1.2)—-|
B(10.1.1.3) —|
C(10.1.1.4)—| -GW(1.1.1.1)-Internet -Server 2.2.2.2

A 使用TCP Port 1025 访问 server TCP 80
B 使用TCP Port 1025 访问 server TCP 80
C 使用TCP Port 2000 访问 server TCP 443

Gateway 上的NAT table 如下:
10.1.1.2 + TCP 1025 <—-> 1.1.1.1 + TCP 1025
10.1.1.3 + TCP 1025 <—-> 1.1.1.1 + TCP 1026
10.1.1.4 + TCP 2000 <—-> 1.1.1.1 + TCP 1027
通过以上IP地址+ TCP端口号的映射,Gateway 可以唯一区分任何一个host。

注意这里A的数据到了网关(路由器)之后,网关(路由器)会对数据进行处理,把发送端的ip地址从A的ip地址改为网关的公网ip地址,再去发送数据包给其他的路由器。其他的路由器如果收到数据包,如果仍需要转发的话,依然需要把发送端的ip改为本公网的ip地址。那他们以自己的公网ip发送之后怎么如何区分数据是给谁的呢(毕竟每个公网下的子网都有很多ip地址)?那就是用到了上面的NAT table,做一个映射关系。
所以路由器使用NAT功能和不使用的时候数据包中的源IP地址是有不同的,一个是永远不变,NAT功能的话转发时源IP地址改为自己的公网IP。
以上仅为个人认知,仅供参考,若存在纰漏,欢迎指出,因为网络这块内容不是三言两语能解释完的,但是大致的思路就是这样子。
推荐几篇文章加深理解:
阮一峰的网络协议入门(一)
阮一峰的网络协议入门(二)
关于NAT理解
关于ARP协议的知乎讨论
内网和外网之间的通信
MAC地址表、ARP缓存表以及路由表

CGI,FastCGI,PHP-CGI,PHP-FPM重识

CGI,FastCGI,PHP-CGI,PHP-FPM


之前研究了下一次完整的Web请求涉及到的过程,今天在学Java的JSP的时候就突然想起来想好好总结一下PHP的CGI有关的知识,之前看过好几遍CGI但是过一段时间总是忘记,又由于重新学习了一遍Web协议,这次再看也许会焕然一新。

CGI出现原因

浏览器通过http协议向服务器请求一个资源时,如果这个请求是静态(图片,html,mp3,pdf…),我们的HTTP
Server(Nginx,Apache)能直接根据地址通过HTTP相应(Response)请求。但是我们访问的资源不可能只是静态的,如果是想访问动态的资源时怎么处理呢?Nginx服务器本身是处理不了的,他只能交给(应用服务器)Application Server来处理。但有一个问题Nginx服务器怎么和应用服务器通信呢,大家互相都不认识。为了解决这个问题,CGI就出现了。

CGI

通用网关接口(Common Gateway Interface/CGI)是一种重要的互联网技术,可以让一个客户端,从网页浏览器向执行在网络服务器上的程序请求数据。CGI描述了服务器和请求处理程序之间传输数据的一种标准。

上面是Wiki上关于CGI的解释,也就是说他是一种协议。
我们看下CGI的特点,接着上面的当HTTP Service(nginx,Apache)接收到请求处理index.php的时候,他把这个请求交给了实现了CGI协议的CGI解释器进程,接下来解释器进程会解析php.ini文件,初始化执行环境,然后处理请求,再以规定CGI规定的格式返回处理后的结果,退出进程。下一个请求来时再创建新进程。这里存在一个问题就是当我执行完index.php后我想访问next.php时会重复前面的解析php.ini文件,初始化执行环境而且每次请求的时候他会启动一个进程,当多个请求同时来的时候会同时启动多个进程会给服务器带来压力。
综上CGI解释器进程的缺点是做一些重复工作,进程进来执行完后退出,利用率低。为了解决这几个问题出现了新的协议CGI的升级版Fast-CGI

Fast-CGI

FastCGI像是一个常驻(long-live)型的CGI,它可以一直执行着,只要激活后,不会每次都要花费时间去fork一次(这是CGI最为人诟病的fork-and-execute 模式)。它还支持分布式的运算, 即 FastCGI 程序可以在网站服务器以外的主机上执行并且接受来自其它网站服务器来的请求。

FastCGI是语言无关的、可伸缩架构的CGI开放扩展,其主要行为是将CGI解释器进程保持在内存中并因此获得较高的性能。众所周知,CGI解释器的反复加载是CGI性能低下的主要原因,如果CGI解释器保持在内存中并接受FastCGI进程管理器调度,则可以提供良好的性能、伸缩性、Fail- Over特性等等。

PHP-CGI

一开始一看名字我以为PHP-CGI是一个实现CGI协议的CGI进程,但其实不是,他是实现了FastCGI协议的进程。PHP-CGI是PHP内置的,PHP本身应该是没有实现CGI协议的进程(因为这个太落后了)。

PHP-CGI与PHP-FPM一样,也是一个fastcgi进程管理器,PHP-CGI的问题在于 1、PHP-CGI变更php.ini配置后需重启PHP-CGI才能让新的php-ini生效,不可以平滑重启 2、直接杀死php-cgi进程,php就不能运行了。(PHP-FPM和Spawn-FCGI就没有这个问题,守护进程会平滑从新生成新的子进程。) 针对PHP-CGI的不足,PHP-FPM应运而生。

PHP-FPM

PHP-FPM是一个PHP FastCGI管理器,是只用于PHP的,使用PHP-FPM来控制PHP-CGI的FastCGI进程,它负责管理一个进程池,来处理来自Web服务器的请求。可以在 http://php-fpm.org/download 下载得到。

特点:

PHP-FPM的使用非常方便,配置都是在PHP-FPM.ini的文件内,而启动、重启都可以从php/sbin/PHP-FPM中进行。更方便的是修改php.ini后可以直接使用PHP-FPM reload进行加载,无需杀掉进程就可以完成php.ini的修改加载。使用PHP-FPM可以使PHP有不小的性能提升。PHP-FPM控制的进程CPU回收的速度比较慢,内存分配的很均匀。

参考

  1. WEB请求处理三涉及到CGI部分
  2. segmentfault尹川的回答

SameTree

Same Tree


Given two binary trees, write a function to check if they are equal or not.
Two binary trees are considered equal if they are structurally identical and the nodes have the same value.
Subscribe to see which companies asked this question

题目就是判断两颗树是不是相同的树。
解决思路:依次去判断这棵树的每个节点,是否存在这个节点,每个节点的值是否一样,然后他的子节点是否相同是否遵循以上规则。这就可以用递归来做。
时间复杂度O(n),空间复杂度O(logn)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
bool isSameTree(TreeNode* p, TreeNode* q) {
if(p == NULL&&q == NULL)
return true;
if(p == NULL||q == NULL)
return false;
if(p->val != q->val)
return false;
return isSameTree(p->left,q->left)&&isSameTree(p->right,q->right);
}
};

指针实现二维数组

早上解决了昨天的那个队列的问题,结果发现错误的原因是出队列的那个函数存在着问题,具体的原因是那个队列的头是不应该存任何数据的,如果队列要换头的话就应该是front->next作为换头的语句Q->front->next=p->next 而不是之前的Q->front = p->next。中间调试了很久一直调试不出来,C出现的问题的时候,虽然可以调试,但是调试的时候很难看出问题,需要一步步仔细的检查。然后就是研究了下指针数组,数组指针,用指针实现数组。也就是昨天的那一块用数组的方式实现层序遍历。具体代码如下。
// 分配内存 struct TreeNode* ** traversalNode = (struct TreeNode* **)malloc(*returnSize * sizeof(struct TreeNode* *)); int **traversalVal = (int**)malloc(*returnSize * sizeof(int*)); *columnSizes = (int*)malloc(*returnSize * sizeof(int));
指针一开始不容易理解,但只要理解了,其实这三种格式都是一样的,就是在分配内存,先说最简单的就是最下面的那一行代码,这就是一个一唯数组,首先就是开一块能存储8个int的大小的空间,然后把指向该空间的首地址传递给 *columnSizes,然后我们访问数组的每一个元素就*columnSizes[0]~*columnSizes[7]访问。平常我们说数组就是指针,int a[5];*a, *(a+1);可以这样转化。当然我觉得*(*columnSizes+1)也可实现。
再来说上面的那几行,其实也是一样的,无非这里开辟内存的空间变成了int,然后这里面存了比如说有`8 * sizeof(int),同理是开辟了一块存储8int大小的地址这可以时说是我们二维数组中的第一维度的存储空间。然后把这个存储空间的首地址赋值给了int *traversalVal。但是这里要考虑到一个问题,我们为什么要这样做,和int a[9][10]有什么区别吗?好了问题来了,当我们这样做有什么不同吗?答案是有,目前我能想到的就是,这样做能够更好的利用空间。怎么说呢,他能保证数组的每块空间都能利用到。(因为我们在操作的时候只知道一个树的深度,但不知道树每层有多少个节点),也就是说我们只能确定a[n]数组的第一个维度。当我们需要确定数组的第二个维度的时候,只需要traversalVal[0] = (int)malloc(n * sizeof(int));这句话的意思就是说把存了这么多n*int存储空间的首地址指向给traversalVal[0];现在这块内容就变成了n个int的首地址了(就是说traversalVal[0]是这块内容的指针,指向这块内容的地址)。然后也就是说traversalVal[n]`指向的内容(内容只要是Int就行,多大的int不做限制)的地址。

实习的那两个月

两个月工作总结(9.22-11.30)

前言

暑假一直在找实习,可是一直碰壁,第一网上找公司,没有经验,唯一去的两家都是培训机构,无奈在家学习了一个暑假。9月初, 暑假结束,我从家里出来找实习,那个时候学校里还是有很多课的(我们这专业就是这么变态),但是我觉得这些课都没什么用,就毅然决定出来找工作了。当时想的无非是,自己在家看书,都是些书本上的东西,实战的还是有缺乏。然后网上就找到了一家公司A,进去面试了,面试官是个干了10年的大牛,算是这家小公司的CTO,笔试的都是些比较基础的,涉及到一些前端的知识点。那场面试呢,面试官考我的也是很基础的东西,首先就是开发环境,我毫不犹豫的说我是MNMP环境,然后他接着问,是怎么安装这些环境的。我就说都很简单啊,就是用Brew安装的,还有Mysql可以下载一个安装包安装。他继续追问,那如果我要安装在一个指定的目录下,怎么办。当时我,一脸懵逼,用惯了Brew,一切都是固定的目录文件夹,确实没有考虑过指定的文件夹,安装,现在想来无非就是编译安装,with指定一下位置。然后他继续追问了一些HTTP的问题,TCP/IP和HTTP的关系,我当时还说错了这两者的依附关系,说TCP/IP属于HTTP,现在想来我暑假那段时间都做了点什么事情啊。再然后就说了点,关于PHP的正则的一些问题,贪婪匹配和严格匹配吧,也没解答好。这是我面的第一家公司,后来面进了,干了两天就不干不了,那个公司只有一个技术大牛,然后前后端一起做,也就是PHP也要干前端的活,还有就是技术氛围太差了。


离开了第一家实习的公司不久,我就获得了一家150团队的中小型公司B的面试机会,去面试前我还准备好好复习一下面试的基本知识。然后第二天一大早我就去面试了,那家公司一进去就是两大排,中间是过道,我看了下给人一种蛮好的技术氛围。进入面试环节,面试官上来看了下我的简历,还是老规矩,自我介绍一下。我感觉每次在自我介绍环节,都是很尴尬的状态,我说我本不是计算机专业,大学读的专业偏硬件,但是自学这个有一段时间了,对技术充满了兴趣。(真的好干)然后就是说一些项目经验什么的,我说项目经验目前还是没有,但是自己做过一个小论坛,博客之类的东西,然后部署在阿里云服务器上,在线上环境,可以给你看下,之后他就略微的看了下。(当时我对我的作品还是比较满意,但是现在想想那东西真不值得拿出手)。面试官接着问了技术的问题,问我关于mysql的Left Join特点和Join 的区别,我就回答了他。紧接着,问了我关于接口和抽象类的区别,我就开始处于模糊的状态,我说抽象类和接口是一个可以继承多个,一个不可以。然后他回答说,想让我说的不是这个点,是使用时候的区别。我答不上来,学艺不精啊。现在想想抽象类和接口确实区别还蛮大的,但当时学的时候没有充分的去分析和总结,这些确实做的不够。这些问题问的都是最最基础的,之后就问了点关于环境的问题,问我用什么编程软件,什么环境下编程什么的…面试结束,对自己感觉只能打70分,我给自己找了个借口是暑假一直在研究C和数据结构,php面向对象和一些myql确实没有深入的研究。但现在看来是不不成立的,这些真的是借口。
但是终究,我还是进了这家公司,进去的时候一共有三个实习生,就我一个是本地的,其他都是江西的,一个已经干了一个月了,另外一个比我早来一天。

刚进来的时候,不知道做点什么,光搭环境就花了一天,公司用的环境是PHP7+Nginx+Mysql5.7+Centos6,就这点东西,我在Win下搭了大半个工作日,还是基于虚拟机的。后来也不知道咋弄的,坐在我旁边的同事HY就和我说,其实我们公司大多数环境都是用Docker的。额…然后又切换到Docker工作了一段时间。
一开始,其实我的项目经理给我的时间是大概两个礼拜适应环境(这是我后来知道的,他们的态度是两个礼拜适应不了走人),其实我压根就不需要这么长时间,因为Laravel框架我早就会了,只需要几天时间回顾一下就可以了,但是我没有主动去接收任务,我研究了下公司的源码,甚至还想看Laravel的源码,研究一下,回头看,那段看源码的时间,确实是效率不高但有花时间的一段时期。索性时间不长,大概就是两个礼拜。然后就被安排任务了,第一个任务是跟着ZHL一起迁移API,其实迁移API怎么说呢,说简单也简单,想简单了就是复制粘贴的事,但我还是有去了解那些业务逻辑的。第一个貌似是一个detail详情的API,做的时候也是糊里糊涂的,讨教了ZHL很久,最终才解决了,有了第一个,下面就得心应手了,接连做了好几个也不费力。之后就是做了一个金融的后台接口,再之后就是举报API了。金融后台因为学校考试的原因,本来项目经理考虑要给其他人做的,我接了下来,以为我想学更多的东西嘛。最后还是赶着完成了这个项目,第一次做这个确实感觉经验很缺乏,很多东西都是中途加上去,边和前端调试变修改的。再就是我感觉做的最失败的东西了,举报的API,听需求的时候,当时因为后台接口和APP接口有点类似,总以为一些需求不是自己的需求,也觉得这个东西很简单,但是做着做着却发现,越做越复杂了,这是我没有想到的,再到了最后要上线的时候,又发现一个功能没有做,这点真的是需求写的不明确造成的。还有就是把一段本来可以写的很简答的代码,复杂化了,我竟然用了一个循环去做,也是醉了,也不知道当时自己这么想的。反正种种原因吧,反正挺失败的,上线也被我拖到了晚上11点,本来预期是9点左右就能上完的。

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×