102. Binary Tree Level Order Traversal
Given a binary tree, return the level order traversal of its nodes' values. (ie, from left to right, level by level).
For example: Given binary tree [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
return its level order traversal as:
[
[3],
[9,20],
[15,7]
]
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> wrapList = new LinkedList<>();
Queue<TreeNode> queue = new LinkedList<>();
if(root==null) return wrapList;
queue.add(root);
while(!queue.isEmpty()){
int size = queue.size();
List<Integer> subList = new LinkedList<>();
for(int i =0;i<size;i++){
if(queue.peek().left!=null){
queue.add(queue.peek().left);
}
if(queue.peek().right!=null){
queue.add(queue.peek().right);
}
subList.add(queue.poll().val);
}
wrapList.add(subList);
}
return wrapList;
}
}
采用队列的形式,遍历一层的同时,也要存储下一层
- 新建队列,存放根节点
- 队列不为空循环下面过程:3-5
- 每次放入当前节点的左节点和右节点;
- 子list加入当前节点,并出队列
- 将子list放入队列
注意:queue.peek(); queue.poll(); Queue queue = new LinkedList<>(); queue.add(Node);
Aggregate calls to microservices in a single location, the API Gateway. The user makes a single call to the API Gateway, and the API Gateway then calls each relevant microservice.
With the Microservices pattern, a client may need data from multiple different microservices. If the client called each microservice directly, that could contribute to longer load times, since the client would have to make a network request for each microservice called. Moreover, having the client call each microservice directly ties the client to that microservice - if the internal implementations of the microservices change (for example, if two microservices are combined sometime in the future) or if the location (host and port) of a microservice changes, then every client that makes use of those microservices must be updated.
The intent of the API Gateway pattern is to alleviate some of these issues. In the API Gateway pattern, an additional entity (the API Gateway) is placed between the client and the microservices. The job of the API Gateway is to aggregate the calls to the microservices. Rather than the client calling each microservice individually, the client calls the API Gateway a single time. The API Gateway then calls each of the microservices that the client needs.
Real world example
We are implementing microservices and API Gateway pattern for an e-commerce site. In this system the API Gateway makes calls to the Image and Price microservices.
In plain words
For a system implemented using microservices architecture, API Gateway is the single entry point that aggregates the calls to the individual microservices.
Wikipedia says
API Gateway is a server that acts as an API front-end, receives API requests, enforces throttling and security policies, passes requests to the back-end service and then passes the response back to the requester. A gateway often includes a transformation engine to orchestrate and modify the requests and responses on the fly. A gateway can also provide functionality such as collecting analytics data and providing caching. The gateway can provide functionality to support authentication, authorization, security, audit and regulatory compliance.
元素删除:
public class DeleteCollection {
public static void main(String[] args) {
List<String> list = new LinkedList<String>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
list.add("e");
for(int i=0;i<list.size();i++){
//循环删除集合中的元素
list.remove(i);
}
System.out.println("还剩余的元素个数:"+list.size());
}
}
上述的代码按照思路应该是对的,输出的结果也应该是0 看下面实际输出的结果: 还剩余的元素个数:2 你也许会问为什么呢?因为集合的大小是动态变化的,当你删除一个元素之后,元素中的序号又重新排列,原来第二个应该删除的元素现在排在了第一个元素的位置,真正删除的却是第三个元素,依次类推,删除的是第一个、第三个、第五个、、、、如果在原来删除的代码中加入语句:System.out.println("即将删除的元素:"+list.get(i));即可验证。
加入上述语句后输出的结果:
即将删除的元素:a
即将删除的元素:c
即将删除的元素:e
还剩余的元素个数:2
解决办法:
究其原因是因为你要删除的元素往前面移动了,而你的i保存的值依旧往后走,所以如果让i不往后走,往前走一个,即可删除本来排在第二个位置的元素现在排在了第一个位置上的元素。
更改后的核心代码:
for(int i=0;i<list.size();i++){
System.out.println("即将删除的元素:"+list.get(i));
list.remove(i);
i--;
}
结果:
即将删除的元素:a
即将删除的元素:b
即将删除的元素:c
即将删除的元素:d
即将删除的元素:e
还剩余的元素个数:0
JAVA中循环遍历list有三种方式for循环、增强for循环(也就是常说的foreach循环)、iterator遍历。
1、for循环遍历list
for(int i=0;i<list.size();i++){
if(list.get(i).equals("del"))
list.remove(i);
}
这种方式的问题在于,删除某个元素后,list的大小发生了变化,而你的索引也在变化,所以会导致你在遍历的时候漏掉某些元素。比如当你删除第1个元素后,继续根据索引访问第2个元素时,因为删除的关系后面的元素都往前移动了一位,所以实际访问的是第3个元素。因此,这种方式可以用在删除特定的一个元素时使用,但不适合循环删除多个元素时使用。
2、增强for循环
for(String x:list){
if(x.equals("del"))
list.remove(x);
}
这种方式的问题在于,删除元素后继续循环会报错误信息ConcurrentModificationException,因为元素在使用的时候发生了并发的修改,导致异常抛出。但是删除完毕马上使用break跳出,则不会触发报错。
3、iterator遍历
Iterator<String> it = list.iterator();
while(it.hasNext()){
String x = it.next();
if(x.equals("del")){
it.remove();
}
}
这种方式可以正常的循环及删除。但要注意的是,使用iterator的remove方法,如果用list的remove方法同样会报上面提到的ConcurrentModificationException错误。
-
循环删除list中特定一个元素的,可以使用三种方式中的任意一种,但在使用中要注意上面分析的各个问题。
-
循环删除list中多个元素的,应该使用迭代器iterator方式。
业务程序员如何提升
作为程序员,我们在选择公司时甚至会不加思考的认为进大厂更有利于我们以后的发展。大厂不仅能给我们带来丰厚的薪资,更能让我们接触到一批技术大牛,有了技术大牛的指导与栽培,我们成为大牛似乎也是顺理成章的事情,此外,大厂的IP也会给我们再次就业加分添彩。大厂技术架构成熟而稳定,小厂技术杂而不精。大厂在经历了多年发展之后,技术体系不断完善成熟,为了提高工作效率,专门的人做专门事,也就是因为如此,大部分的程序员会被成熟的技术体系框在一个小圈子里,如果自身没有学习与钻研的精神,思想很容易会被这个圈子所束缚,从而沦为了业务程序员。
在日常的工作中,我们经常会听到”写业务快写废了“,”除了增删改查一无所知“等等的吐槽。沦为业务程序员不仅会让技术无法匹配自己的工作年限,还会影响到后期跳槽面试的表现。慢慢的将会被淘汰。为了定义清楚什么是业务程序员,我将技术分为三大类:外围技术、业务能力、工作经验
一. 外围技术
这里的外围技术指的是即便我们不会,不了解其原理,也不会影响到我们对需求的实现。例如服务器的维护、存储计算分布式系统的搭建与维护、各类中间件、网络安全知识等,我们并不需要懂,这不会影响到我们项目的开发与上线。
回忆一下我们项目开发的流程,创建一个web项目,申请线上机器(机器各种环境都已经配置好了,完全不用担心),设计数据库表提交工单(DBA负责创建,分配DB节点,数据备份冗余安全等问题不需要我们考虑),配置监控、日志功能,接入公司MQ、Redis,RPC,统一配置平台等。一切配置之后,就可以进行业务代码的编写,编码完成之后经由测试通过发布平台进行发布。服务成功启动,为了能让外部用户访问,然后配置下NG。
到此整个项目完成,除了编写业务代码,其他都是企业已有的成熟的技术体系,配置即用,作为程序员貌似完全没有必要去学习这些外围技术。此外,大厂都有一个通病,就是每一个公司几乎都有自己的一套完备的外围技术,换工作时很大一部分成本也是在学习这些技术如何使用。
二. 业务能力
项目环境搭建完成之后,就可以开始业务代码的编写。业务能力也相当的重要,但是与技术的关联性并不高,不再展开。针对业务逻辑不同层次的程序员实现也不同。一般的程序员就是对业务代码进行简单拼凑。优秀的开发者会使用一些设计模式的思想将业务代码进行抽象,使应用低耦合性、高稳定、可扩展。但不管设计好坏,两者都可以实现最终的业务需求功能。而领导主要关注结果,不会因为后者设计的有多好会对后者有所褒奖。技术水平一般却能与技术好的人得到一样甚至更好的待遇也是促使很多人沦为业务程序员的一大原因。
三. 工作经验
在编码过程中,我们会经常碰到一些并发、线程安全、事务、分布式、一致性等问题,虽然这些都是在写业务代码的时候碰到的,但是我更倾向于将这部分技术归为工作经验。因为这些技术只要在项目中接触到,就会去被动学会,属于必备技能,关键问题是在项目开发中有没有碰到过,碰到的越多,掌握的就越多,工作经验就越足,这也就是为什么程序员工作年限越长越值钱的原因。
总结
业务能力除了在公司内部工作时用来讨论,在面试(面试同业务方向除外)或者技术分享上几乎见不到他的影子。而在面试中最重要的便是工作经验,工作经验的考察主要体现在基本技能的考核和项目两点上,懂得外围技术的核心思想也是面试的加分项。所以作为程序员我们要认清楚自己的角色,不要将自己置身于从技术角度来讲最无关紧要的业务代码中。我们应该有自主学习的能力,对于所使用的的中间件等外围技术不能仅仅停留在会用的阶段,去了解了内部实现机制,真正的去认识它,不仅仅能让我们可以和业务程序员拉开差距、在面试再就业时有一个较大的优势,还有利于我们编写出更加健壮稳定的代码。
都说程序员是一碗青春饭,虽然有点夸张,但这并不是空穴来风,等我们慢慢老去,我们开始不能熬夜,不能加班,市场竞争力就会变低。突然有一天意识到自己还是一个程序员,想去搭建一个网站自主创业的时候才发现离开了公司的技术体系外,自己除了能写业务代码外,什么也做不了。所以趁着还年轻,保持积极主动的学习心态,不要让大厂完善的技术体系限制了自己的成长。