大数据的一些面试题

1、海量日志数据,提取出某日访问百度次数最多的那个IP。

这道题就是用Hash + Heap的办法来做的典型问题。我们先遍历每个IP,对每个IP算一个Hash,这样每个Hash就均匀的Map到了Hash空间里。然后根据每个IP计算的Hash(IP)%100 将这个IP放到100个文件中。这种做法就保证了同一个IP一定会被分到同一个文件中,而且所有IP应该是均匀分布的。然后对于每个文件里出现IP的频率进行统计(可以用HashMap, 也可以用trie,每个Tire的leaf上存一个freq),然后用MinHeap排序找出每个文件的Top K,然后再合并所有的Top K,就得到了结果。

同样的办法可以用在其他的统计String的TopK频率等等类似的问题中。

2. 海量数据分布在100台电脑中,想个办法高效统计出这批数据的TOP10。
如果每个数据只出现在同一台电脑上,那么就可以用上面的方法解决,还省去了Hash的过程。但是如果同样的数据可能出现在不同的电脑中呢?
那么我们就要把相同的数据用Hash的办法放到同样的电脑中。相当于重新分布一下。
或者我们直接用另外一台电脑统计所有电脑中数据出现的次数(因为有重复,所以有可能一台电脑就可以放的下),然后再按频率进行排序。

同样的问题还可以用一些分布式的framework来做比如map reduce。把每个document分给mapper,mapper遍历所有的word,然后做成word->1这样的pair,然后经过shuffle以后变成word->{1,1,1,1..}这样的,然后我们只要在reducer里面分析这个组合就可以了。

3. 给定a、b两个文件,各存放50亿个url,每个url各占64字节,内存限制是4G,让你找出a、b文件共同的url?

同样的对每个url进行Hash,然后根据Hash(url)的结果将他们分成1000份(这样每一份是0.32G,如果hash算法好的话)。然后a 和 b的每个相对应的file进行比较看看有没有重复的(用hashmap就可以)因为如果是同样的hash算法的话,相同的url一定是分配到相同的file中的。

或者我们也可以给其中一个url生成一个bloom filter,然后用这个bloom filter来filter另外一个hash,如果没有重复,那么我们可以肯定没有重复,如果bloom filter的结果说可能有重复,那么我们再进到array里一个一个检查。

2.5亿个整数中找出不重复的整数的个数,内存空间不足以容纳这2.5亿个整数。
其实我们只需要把所有整数划分到能够放进内存里的bucket里面,比如说2^26 32M大小的file,然后每次check这个范围内的整数,用bitmap来看是否有重复的。 给每个integer assign一个bit,如果出现过就把这个bit设置成1,这样就知道这个bit是否见过了

给40亿个不重复的unsigned int的整数,没排过序的,然后再给一个数,如何快速判断这个数是否在那40亿个数当中?
同样只要用bitmap,每个bit assign一个数,只要那个对应的数被设置成1了,就表示那个数存在,是逻辑和算法上的,和计算机的物理特性没有关系。

外排序,外排序

有一个1G大小的一个文件,里面每一行是一个词,词的大小不超过16个字节,内存限制大小是1M。返回频数最高的100个词。

所以我们可以用内存作为缓冲区来进行外排序,将所有的词都按String比较大小的方法外排序。这样所有的string都已经排好序了,然后我们再一个一个统计所有string出现的次数,最后输出频率最高的那个。

OOD 设计

最基本的设计一个singleton,重点:private constructor,只有一个GetInstance的function

class Singleton {
  private Singleton() {
  }
  
  static sInstance = null;
  public static Singleton GetInstance() {
    if (sInstance == null) {
      sInstance = new Singleton();
    }
    return sInstance;
  }
}

然后第二个是设计一个停车场。

class Vehicle {
  int id;
  int type;
  int size;
  int ParkingTicket;

  int getParkingTicket(ParkingTicket ticket);
}

class Bus {
  size = 3;
}

class Car {
  size = 1;
}

class Motocycle {
  size = 1;
}

class ParkingLot {
  int id;
  List<ParkingSpot> parkingSpots;

  int getParkingSpot(int size) {
  }
}

class ParkingSpot {
  int id;
  int type;
  bool available;
}

class ParkingManager() {
  ParkingLot pk;
  List<ParkingTicket> ticketList;
  double balance;  

  Park(Vehicle v);
  Leave(Vehicle v);
  CollectTicket(Ticket);
}

class ParkingTicket() {
  int id;
  int spotId;
  int ArriveTime;
  int LeaveTime;
}

最后一个设计一个achievement system

class Object {
  int id;
  int type;
  String name;
}

class Pig extends Object {
  type = 1;
}

class Coin extends Object {
  type = 2;
}

class AchievementType {
  int id;
  String name;
  Map<Object, count> Requirement;
  Map<Object, count> award;
}

class Achievement {
  int player_id;
  int type_id;
  boolean finished;
  boolean claimed;
  int claimedTime;
}

class AchievementManager {
  Map<type_id Achievement> typeList; //maps the id to the type;
  checkAchievement(Player p) //check if any of the achievement in the player's achievement list is satisfied or not, and add new achievement for the next level is necessary. 
}

class Player {
  List<Objects> inventory;
  List<Achievement> achievementList;
  AchievementManager am; //each player should have a reference to the achievementManager.
  init();
  product(); //change the inventory, also we want to have achievementManager
  AddAchievement();
  claimAchievement;
}

class World() {
  AchievementManager am;
  List<Player> playerList;
  initAndRun();
}

Design an elevator:

class Elevator {
  List<Floor> floors;
  Cart cart;
  List<FloorButton> floorButtonList();
  List<CartButton> cartButtonList();

  start();
  move();//everytime a button is pressed, we make some decisions about which floor to go, and move the cart to the correct floor, and cart will update its status accordingly.
  reset();
  stop();
  fire();
  mantainance();
}

Button {
  boolean pressed;
  Press();
  Pop();
}

FloorButton extend Button {
  int floor;
  boolean direction;
}

CartButton extend Button {
  int floor;
}

class Cart {
  int status; //going up, going down, stop, fire..
  int floor; //if not moving, which floor is it on now;

  getStatus();
  openDoor();
  closeDoor();
  goToFloor();
  setStatus();
}


优步系统设计

How to design an excel

根据看到的面经,这题会分两个侧重点来问,当然get和set是最基本的, 1, 重点要处理各个cell之间的dependency,比如cell(1,3)是用公式算出来的cell(1,3)=cell(0,0)+cell(0,1)+cell(0,2),我会用两层哈希表表示整个表格(unordered_map> workbook),然后每个Cell中保存一个unordered_set parents;(所有计算当前Cell需要依赖的cells,上例就是cell(0,0),cell(0,1)和cell(0,2)) 和 unordered_set children; (所有依赖这个Cell通过公式计算出来的cells),每次改变cell的值就要对children和parents做相应的改变; 2, 重点是要处理add或delete一整行或一整列,我会用2d数组,vector> workbook, add的话就直接append,delete行的话就直接erase对应的行,delete列的话就根据列下标,对每行进行erase,好写,但是效率有点低,如果大家有更好的想法,可以一起讨论下。

首先这的想法可能是用一个2darray,但是我们我们不知道初始的大小,所以我们可以用list of list,这样的话还是会要用到多余的空间。即使这些空间没有存数据。所以最好的办法是用HashMap<Integer, HashMap> 来实现,这样的话我们就只会记录写过data的cell了。

还有一个问题是dependency,因为我们在excel里要实现cell(1,3)=cell(0,0)+cell(0,1)+cell(0,2),所以当cell(0,0) 变化的时候,我们要update cell(0,0). 所以每个map需要记录depend他的值(有点像observer pattern里的register,比如说cell(0,0)就要存一个Set,记录所有depend他的Cell (比如Cell(0,1)就要记录Cell(1,3)是他的children,另外要有detection,dependency不能有cycle,这个可以用DFS来做。

另外如果是删除或者添加一整行/列,这个可能就不好做,因为行只要删除对应的那个行就可以,但是列的话只能一个一个删除了。为了加快速度我们可以让每个cell都记录一下他的上面和下面的列(在插入这个新cell的时候update),这样的话删除的话就我们就只需要删除一个linkedlist了,但是这样的话插入就需要遍历所有的行,需要遍历所有的cell。如果我们用TreeMap的话,会更快,但是如果修改或者删除的话就是O(logN)而不是O(1)的复杂度了。

还有follow up问题是如果存其他数据,比如说image,甚至audio或者video的话,我们就只存一个url或者是path,等读取的时候我们再从file system或者CDN里面去读取。

如果这个document还放到网上去共享的话,那当user1在修改某个cell的时候,需要给cell加一个锁,可以用mutex来实现,具体就是在edit的时候要调用mutex.aquire(),操作完以后要调用mutex.release();这样可以保证不会有同时的两个人去access同样的data,当然mutex是multithread用的,而不是网上共享用的。但是同样的道理,我们可以用一个atomic的bit给每个cell来加锁和解锁(比如说这个bit可以放到redis里面的某个key,因为redis是single threaded,所以可以保证所有操作都是atomic的。

Mutex和semaphore的区别,mutex相当于是只有0和1的semaphore,但是要注意一点mutex的加锁和解锁是只能由同一个thread进行的。而semaphore的加和减是可以由不同thread执行,所以mutex用于加锁而semaphore用于协调几个thread的执行。

设计题2: 让我设计一个 Netflix/Spotify, follow up 很多 比如如何限制同一个用户多处登录不能同时看一个资源,如何实现根据用户的网速调整清晰度,怎么热门推荐等等。

对于设计登录系统,首先我们先要设计基本的login系统,首先支持Register/login,就需要一个基本的用户表,如果要支持更复杂一点的登录系统,包括verification,ban,inactive,removed,就需要一个status来记录每个user的状态。

如果还要支持用户可以从多个设备登录,不同的设备会有不同的session id, 但是却有相同的user——id,如果不想要同一个用户可以在多个设备上播放同样的资源,我们就要记录每个用户每个session正在play的资源,然后保证每个session不能play相同的song_ID或者video_id。如果要根据网络速度调整清晰度,我们就需要先测量网络的情况,让client端ping一下服务器,根据收到的ping再向服务器请求不同清晰度的资源。资源本身不应该存在服务器上,可以存在离客户端很近的CDN上。

然后要看播放器的类型,如果是app的话,这个app会需要向DNS server请求我们服务器的地址,然后我们服务器向clientload网页(如果是从浏览器的话,如果是app的话,就不需要load网页,直接去请求源地址就可以了)。然后网页再加载播放器(flash或者HTML5的), 最后再请求源地址,源地址应该是放在附近的CDN上面的,所以很快。

接下来是推荐系统,假设问怎么样设计一个推荐系统,推荐给用户Top10 most frequent played。最简单的用MinHeap,把所有的play过的video或者audio都记录一个frequency,这个记录可以放在内存里(可以是cluster的这种比如redis)这样便于快速的更新和存取。我们希望这个工作放在worker里做,这样可以不影响整个系统的through put。当worker完成了工作以后,就可以Update所有的client,这个工作也可以由一个worker来做,这里我们可以用push也可以用pull,也可以两者相结合,push的话只需要push给在线用户,pull的话对于刚上线的用户来更新最新结果。

外排序就是假设内存不够用,那么我们就将所有的数据分成小块,然后每个小块都可以放进内存里排序,排好了序的这些小块再进行merge sort。就得到了所有数据的排序结果。

还有一个follow up就是因为有很多用户在同时播放视频文件,有可能同时有很多人在看同一个视频,那么这个视频的freq就会有很多+1,怎么样保证所有的+1都记录下来呢?我们可以用一个aggregator,专门记录这种update,等update到了一定的数量或者一定时间做一次batch的update。

3.如何设计一个Uber。
首先登陆系统也是一样的设计。

而作为rider,我们需要找到当前所在的地方的附近所有的drivers,假设drivers的location都是已经存好的放在数据库里并且是不变的,我们要做一个这样的查找也是非常麻烦的。因为即使driver_id, X, Y 都是index过的,我们要查找X在某个limit之内和Y在某个Limit之内,也需要遍历所有的X和Y才能找到我们需要的driver。

有两种解决这个问题的办法,
一种叫做GeoHash
http://www.cnblogs.com/LBSer/p/3310455.html
只要把整个地图分成小块,然后给每个Point of Interest(也就是Driver)assign一个GeoHash,那么相同的GeoHash值就是在附近的。要处理边界问题,我们除了当前location的GeoHash,也处理周围的8个GeoHash的所有POI,这样就cover了边界情况了,另外这些所有的找出来的点我们再重新计算到当前location的距离,然后去掉一些离开比较远的。这样就找到了结果

一种叫做R Tree,其实是一种B Tree在二维上的拓展,我们把每个地图分成多个小方块,然后每个小方块里又有更多的小方块,这种RTree用来处理面或者线的问题更好,处理点的问题用GeoHash更好。
http://blog.csdn.net/v_july_v/article/details/6530142

作为Driver,我们可能需要push 一些Event(比如说有大型的音乐会啊之类的,我们同样也只想push到event附近的driver那里。

How to design a real time system: Driver每4秒update一次位置和速度方向信息,怎么样保证他们都被收到并且存下来呢。我们可以用一个distrubuted message Queue,类似kafka,producer把内容写到messageQueue来,我们的server有很多worker从message Queue里面拿到数据并且分析数据,然后记录下来,供rider来使用。有一个问题是如果我们把所有的这些都放在同一个地方,就会很拥挤,而大部分的Uber的ride都是在同一个城市里的,而且我们要求实时计算,所以delay可能会很严重,所以我们可以把这些数据都分成不同的shard,相同的城市可以用一个shard,这样的好处就是大部分的Uber Ride应该是同城市的,而down side是如果是cross city的话就会比较expensive,还有一点就是小城市和大城市的traffic差距很多,所以我们可以把很多小城市组合在一起。

设计一个black jack的游戏
首先我们要有牌
[language=”java”]
class Card {
int suit;
int value;
boolean face; //facing up or down
}

class Deck {
List cardList;
initDeck();
}

class CardBox {
List cardList;

void Shuffle();
void AppendDeck();
Card popCard();
}

Gamer() {
int gamer_id;
String name;
double money;
List handCards;
Strategy strategy;

void nextStep(); //we could call this makeDecision too, but it only calls corresponding makeDecision in the strategy.
CalculateHandScore();
AddCard();
ShowHand();
}

Player extends Gamer() {
List actionList; //record actions
Strategy playerStrategy = new PlayerStrategy(ActionList); //player strategy is based on the handcard and actionList(including dealer’s handcard);

Fold();
RecordAction(Action);
}

Dealer extends Gamer() {
CardBox cardBox;
int gameState;
Strategy dealerStrategy = new DealerStrategy(handCard); //dealer strategy only based on the handcard.

Reset();
DeclareWinner();
Run();
}

Interface Strategy {
void makeDecision(){};
}

PlayerStrategy implements Strategy() {
//decision should be made base on these
List actionList;
List handCard;
@Override
void makeDecision(){};
}

DealerStrategy implements Strategy() {
List handCard
@Override
void makeDecision(){};
}

Action() {
int id;
int type;
int time;
String info;
}

GameRoom() {
Dealer dealer;
List playerList;

init();
JoinPlayer();
LeavePlayer();
}

[/language]

优步面经

Email domain address typo, need to figure out and return correct domain name. e.g. yhaoo–> yahoo, giaml –> gmail, hmailot–> hotmail.
Solution: HashMap, key as sorted string, value as correct email domain.

其实是anagram

public String getEmail(String s) {
  Map<String, String> map = new HashMap<String, String>();
  //init the map with the string
  
  String[] email = {"yahoo", "gmail", "hotmail" ..};
  for (String e : email) {
    map.put(sortByChar(e), e);
  }
  if (map.containsKey(sortByChar(s)) {
    return map.get(sortByChar(s));
  }
  return null;
}

private String sortByChar(String s) {
  char[] charArray = s.toCharArray();
  Arrays.sort(charArray);
  return new String(charArray);
}

Elevator Design

Design Uber, web service, API, database.

given a > b, b > c, if pass in c > a return false, if a > c return true

感觉像是topological sort,实际上直接dfs就可以,因为只要找到了back edge,就说明顺序是不对的

如果对于cross edge,假设我们认为cross edge return false的话,就好办了,我们只需要按顺序搜,搜到了的话就return true,不然就return false。如果相反的话,我们就要把input反过来,如果搜到了就return false,不然就return true。这里假设input保证没有loop。

public boolean checkOrder(char c1, char c2, List<Char[]> orders) {
  Map<Character, Set<Character>> adjMatrix = new HashMap<Character, Set<Character>>();
  for (Char[] pair : orders) {
    char from = pair[0];
    char to = pair[1];
    Set<Character> set = null;
    if (!adjMatrix.containsKey(from)) {
      set = new HashSet<Character>();
      adjMatrix.put(from , set);
    } else {
      set = adjMatrix.get(from);
    }
    set.add(to);
  }
  Set<Character> visited = new HashSet<Character> visited;
  return dfsFind(adjMatrix, c1, c2);
}

private boolean dfsFind(adjMatrix, char start, char target) {
  if (start == target) {
    return true;
  }
  Set<Character> curr = adjMatrix.get(start);
  for (Character c : curr) {
    if (dfsFind(adjMatrix, c, target)) {
      return true;
    }
  }
  return false;
}

做tiny url
其实就是转换成base 62的数。

public String getUrl(int id) {
  if (id == 0) {
    return null;
  }
  List<Character> list = new ArrayList<Character>();
  for (char c = '0'; c <= '9'; c++) {
    list.add(c);
  }
  for (char c = 'a'; c <= 'z'; c++) {
    list.add(c);
  }
  for (char c = 'A'; c <= 'Z'; c++) {
    list.add(c);
  }
  StringBuilder sb = new StringBuilder();
  while (id != 0) {
    int idx = id%62;
    sb.insert(0, list.get(idx));
    id /= 62;
  }
  return sb.toString();
}

Spiral Array Print

public void printSpirialMatrix(int [][] matrix) {
  if (matrix == null || matrix.length == 0) {
    return
  }
  int sR = 0;
  int eR = matrix.length-1;
  int sC = 0;
  int eC = matrix[0].length-1;
  while(sR <= eR && sC <= eC) {
    //first row
    for (int i=sC; i<eC; i++) {
      print(matrix[sR][i]);
    }
    //right col
    for (int i=sR; i<eR; i++) {
      print(matrix[i][eC]);
    }
    //bottom row
    if (sR < eR) {
      for(int i=eC; i>sC; i--) {
        print(matrix[eR][i];
      }
    }
    //left col
    if (sC < eC) {
      for (int i=eR; i>sR; i--) {
        print(matrix[i][sC]);
       }
    }
    sR++;
    eR--;
    sC++;
    eC--;
  }
}

电面:
String Break
将一个String分成若干个substring,每个substring有大小限制k,且在末尾有固定格式代表这是第几个substring

Example: “this is a string” -> “this is (1/2)” “a string (2/2)”

这道题就是数学题了

如果总的长度是n,假设每个string 分别是k个那么大,但是由于需要在后面append长度信息。所以不可以直接除以k, 而要加上append的信息,那么我们假设append的信息的长度是l, 那么最多可以将整个string 分成power(10, l) 份。

l*2 + 3 + n/(10^l) <= k

int n = s.length();
int l = 1;
while(l*2 + 3 + n/(10^l) > k) {
  l++;
}
List<String> res = new ArrayList<String>();
//now we have l (the space to store extra)
int parts = Math.ceiling(n/(float)(k-l)); //make sure to use float otherwise int division will be used
int start = 0;
int count = 1;
StringBuilder sb = new StringBuilder();
for (int i=0; i<s.length() && start<s.length(); i++) {
  if (s.charAt(i) == ' ') {
    String tmp = s.substring(start, i);
    if (sb.length() + tmp.length() + 1 > k - 2*l - 3) {
      sb.append(String.valueOf(count));
      sb.append('/');
      sb.append(String.valueOf(parts));
      count++;
      res.add(sb.toString());
      sb = new StringBuilder();
    }
    sb.append(tmp);
    sb.append(" ");
    start = i+1;
  }
}
//what was left in sb;
if (sb.length() != 0) {
  sb.append(String.valueOf(count));
  sb.append('/');
  sb.append(String.valueOf(parts));
  res.add(sb.toString());
}
return res;
}

题目就是flatten json to a list of map, 有一段json,比如说如下:
{
“uuid”: “abc”,
“properties”: {
“sessionName”: “Test session name”
“waypoints”: [
{“uuid”: “def”, “properties”: {“latitude”: 3}}
]
}
}
把它转化成List<Map>, map里面uuid是key, properties是value。 所以结果应该像下面

[

{“uuid”: “abc”, “properties”: {“sessionName”: “Test session name”, “waypoints”: [“def”]}},

{“uuid”: “def”, “properties”: {“latitude”: 3}},

]
其实就是recursion求,每次把一个object放进map以后,要用一个recursive的function 处理一下每个attribute。如果是array的话要recursive的把array里的所有object都放进res里面,并且对每个object再进行recursion

public class Solution {
	public List<Map<String, Object>> flatternJSON(String jsonString) {
		JSONParser parser = new JSONParser();
		List<Map<String, Object>> res = new ArrayList<Map<String, Object>>();
		try {
			JSONObject jObj = (JSONObject)parser.parse(jsonString);
			Map<String, Object> map = new HashMap<String, Object>();
			String key = (String) jObj.get("uuid");
			Object value = jObj;
			map.put(key, jObj);
			recursiveProcess(jObj, res);
			return res;
		} catch (ParseException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}
	
	private void recursiveProcess(JSONObject jObj, List<Map<String, Object>>res) {
		Iterator iter = jObj.keySet().iterator();
		while(iter.hasNext()) {
			
			String key = (String) iter.next();
			Object obj = jObj.get(key);
			if (obj instanceof String) {
				continue;
			} else if (obj instanceof JSONArray) {
				JSONArray array = (JSONArray)obj;
				Iterator arrIter = array.iterator();
				JSONArray newArray = new JSONArray();
				while(arrIter.hasNext()) {
					JSONObject o = (JSONObject) arrIter.next();
					newArray.add(o.get("uuid"));
					Map<String, Object> map = new HashMap<String, Object>();
					map.put((String) o.get("uuid"), obj);
					res.add(map);
					recursiveProcess(o, res);
				}
				jObj.put(key, newArray);
				
			} else {
				recursiveProcess((JSONObject)obj, res);
			}
		}
	}
}
}  

1,Run-Length Encoding,就是把aaabbcccaaaa变成3a2b3c4a这样,要求encode后的信息要比原来的短。

	public String encode(String s) {
		  int start = 0;
		  StringBuilder res = new StringBuilder();
		  for (int i=1; i<=s.length(); i++) {
		    if (i == s.length() || s.charAt(i) != s.charAt(start)) {
		        if (i - start > 2) {
		          res.append(String.valueOf(i-start));
		          res.append(s.charAt(start));
		        } else {
		          res.append(s.substring(start, i));
		        }
		        start = i;
		    }
		  }
		  
		  return res.toString();
		}

首先暴力扫描之。然后问有什么问题,答曰decode的时候111aaa会encode成313a然后decode成313个a。提出每encode一个就插入一个特殊符号标记,他说可以。然后追加问是否可以不用特殊符号,答曰创建新的计数单位,比如用A代表1B代表2,然后aaa就变成Ca(you get the idea),这样就不需要插入额外符号了。

3. Coding Question,用的coderPad, 比较两个Map Object,Map的Value可能有两种情况
1. Strings; 2. Map objects

public boolean compare(Object obj1, Object obj2) {
  if (obj1 instance of String && obj2 instance of String) {
    return (String)obj1.equals((String)obj2);
  }
  
  if(obj1 instance of Map<String, Object> && obj2 instance of Map<String, Object>) {
    Map<String, Object> map1 = (Map<String, Object>) obj1;
    Map<String, Object> map2 = (Map<String, Object>) obj2;
    if (map1.size() != map2.size()) {
      return false;
    }
    Iterator iter = map1.keySet().iterator();
    while (iter.hasNext()) {
      String key = (String)iter.next();
      if (!map2.containsKey(key)) {
        return false;
      }
      if (!compare(map1.get(key), map2.get(key)) {
        return false;
      }
    }
    return true;
  }
}

Given an input string, reverse the string word by word. A word is defined as a sequence of non-space characters.

The input string does not contain leading or trailing spaces and the words are always separated by a single space.

For example,
Given s = “the sky is blue”,
return “blue is sky the”.

Could you do it in-place without allocating extra space?


public String reverse(String s) {
  //reverse the whole string
  int start = 0;
  int end = s.length()-1;
  StringBuilder sb = new StringBuilder(s);
  while (start < end) {
    swap(sb, start, end);
    start++;
    end--;
  }
  //reverse word by word separated by space
  int start = 0;
  for (int i=0; i=<sb.length(); i++) {
    if (i==s.length() || s.charAt(i) == ' ') {
      reverseBetween(sb, start, i-1);
      start = i+1;
    }
  }
  return sb.toString();
}

private void swap(StringBuilder sb, int i, int j) {
  char tmp = sb.charAt(i);
  sb.setCharAt(i, sb.charAt(j);
  sb.setCharAt(j, tmp);
}  

private void reverseBetween(StringBuilder sb, int s, int e) {
  while(s < e) {
    swap(sb, s, e);
    s++;
    e--;
  }
}

形式化来说,给N个未排序的数组,找出全局中位数。
应用场景是,有N个hosts,每隔一段时间就会报告一些参数,比如qps和平均延迟等,现有某个用于monitor的机器收集信息,并且返回特定时间段内同一参数所有数据的的中位数。
先给brute force,对所有数据排序然后找中位数。

一个stream找中位数的话,可以用两个heap,一个minHeap保存大一半的数,另外一个maxHeap保存小的一半的中位数。如果新来一个数的话,我们看他是比minHeap和maxHeap最大的数比较,然后加进其中一个heap,如果在两个之间的话,假设我们就放进minHeap里,然后需要平衡一下两个heap,保证minHeap和maxHeap的size是相同的。

class MedianFinder {
  PriorityQueue<Integer> minHeap;
  PriorityQueue<Integer> maxHeap;
  public MedianFinder() {
    this.minHeap = new PriorityQueue();
    this.maxHeap = new PriorityQueue(10, Collections.reverseOrder());
  }

  public void addData(int i) {
    if (i >= minHeap.getFirst()) {
      minHeap.add(i);
    } else (
      maxHeap.add(i);
    }
    //rebalance two heaps
    if (minHeap.size() - maxHeap.size() == 2) {
      int tmp = minHeap.removeFirst();
      maxHeap.add(tmp);
    } else if (maxHeap.size() - minHeap.size() == 2) {
      int tmp = maxHeap.removeFirst();
      minHeap.add(tmp);
    }
  }

  public int getMedian() {
    if (minHeap.size() == maxHeap.size()) {
      return (minHeap.getFirst() + maxHeap.getFirst())/2
    }
    return minHeap.size() > maxHeap.size() ? minHeap.getFirst() : maxHeap.getFirst();
  }
}

search element in a roated sorted array. leetcode
Word Ladder II

这道题的idea是先用bfs找到最短路径,然后dfs找出所有的结果。

第一轮: Happy number
第二轮: 罗马数字 -> 阿拉伯数字

Happy number就是按照happy number的算法一直算,但是要注意已经visited过了的要记下来,

罗马数字转换成阿拉伯数字的话,就是要一个Map,然后注意每次greedy pick最大的那个数append到结果的右边即可

1. 设计excel, 先实现基本功能再优化(比如存图片怎么弄)
2. 给一个tuple list, 按weight random select. 比如给 [(“hello”, 2.34), (“world”, 4.68)…] world出现的概率是hello的两倍.
第二题, reverse string的变种。 只reverse word不reverse punctuation。比如 “this,,,is.a word” -> “word,,,a.is this”
这道题可以考虑用两个stack,一个用来reverse word,另外一个保存punctuation。

	public String reverse(String s) {
		  Stack<String> wStack = new Stack<String>();
		  Queue<String> pQueue= new LinkedList<String>();
		  int start = 0;
		  boolean isChar = true;
		  int end = 0;
		  for (int i=0; i<s.length(); i++) {
		    char c = s.charAt(i);
		    if (!Character.isLetter(c)) {
		      if (isChar) {
		        wStack.push(s.substring(start, i));
		        start = i;
		        isChar = false;
		      } else {
		        continue;
		      }
		    } else {
		      if (isChar) {
		        continue;
		      } else {
		        pQueue.add(s.substring(start, i));
		        start = i;
		        isChar = true;
		      }
		    } 
		  }
		  //last word or punctuation
		  if (isChar) {
		    wStack.push(s.substring(start));
		  } else {
		    pQueue.add(s.substring(start));
		  }
		  //form the new string, assuming always start with a word
		  StringBuilder sb = new StringBuilder();
		   while (!wStack.isEmpty()) {
		     sb.append(wStack.pop());
		     if (!pQueue.isEmpty()) {
		    	 sb.append(pQueue.remove());
		     }
		   }
		   return sb.toString();
		}

3. bar raiser. 是一个front end engineer (我面的是backend)但要我把我的project给他讲清楚。结果不讲不知道一讲吓一跳,backend懂得比我还多
4. design一个uber eat。说是system design结果我感觉更像product design
5. engineering manager. 一个老中 刚来没多久。问了我许多问题结果感觉对我不大满意。比如问我写service到底理解到多deep。data storage平时接触没(确实没怎么 – 都在application层面 – 摊手)。

Delete and insert in a binary tree

class TreeNode {
  TreeNode left;
  TreeNode right;
  int val;
  public TreeNode(int v) {
    this.val = v;
  }
}

class Solution {
  public static void main(String[] args) {
    Solution s = new Solution();
    TreeNode root = new TreeNode(3);
    s.insert(root, 2);
    s.insert(root, 3);
    s.insert(root, 4);
    s.insert(root, 5);
    s.insert(root, 6);
 
    assert s.contains(root, 6) != null;
    s.delete(root, s.contains(root, 6));
    assert s.contains(root, 6) == null;
    System.out.println("Success");
  }
  
  public TreeNode insert(TreeNode n, int v) {
    if (n == null) {
      return new TreeNode(v);
    }
    
    if (v > n.val) {
      TreeNode t = insert(n.right, v);
      n.right = t;
    } else if (v < n.val) {
      TreeNode t = insert(n.left, v);
      n.left = t;
    }
    return n;
  }
  
  
  public TreeNode delete(TreeNode root, TreeNode n) {
    if (root.val == n.val) {
      if (root.left == null) {
        return root.right;
      }
      if (root.right == null) {
        return root.left;
      }
      TreeNode l = findLeft(root.right);
      int tmp = root.val;
      root.val = l.val;
      l.val = tmp;
      delete(root.right, l);
      return root;
    } else {
      if (n.val < root.val) {
        root.left = delete(root.left, n);
      } else {
        root.right = delete(root.right, n);
      }
      return root;
    }
  }
  
  public TreeNode contains(TreeNode root, int value) {
    if (root == null) {
      return null;
    }
    if (root.val == value) {
      return root;
    }
    if (value > root.val) {
      return contains(root.right, value);
    }
    return contains(root.left, value);
  }
 
  private TreeNode findLeft(TreeNode root) {
    if (root.left == null) {
      return root;
    }
    return findLeft(root.left);
  }
}

mirror tree

class Solution {
  public static void main(String[] args) {
    Solution s = new Solution();
    TreeNode root = new TreeNode(1);
    TreeNode l1 = new TreeNode(2);
    TreeNode r1 = new TreeNode(3);
    root.left = l1;
    root.right = r1;
    TreeNode l2 = new TreeNode(4);
    TreeNode r2 = new TreeNode(5);
    TreeNode r3 = new TreeNode(6);
    l1.left = l2;
    l1.right = r2;
    r1.right = r3;
    
    //api under test
    s.mirror(root);
    
    //assertion
    assert root.val == 1;
    assert root.left.val == 3;
    assert root.right.val == 2;
    assert root.left.left.val == 6;
    assert root.left.right == null;
    assert root.right.left.val == 5;
    assert root.right.right.val == 4;
    System.out.println("Success");
  } 
  
  public void mirror(TreeNode n) {
    if (n == null) {
      return;
    }
    
    TreeNode tmp = n.left;
    n.left = n.right;
    n.right = tmp;
    mirror(n.left);
    mirror(n.right);
  }
}

shorten url base 62

Matrix Region Sum
http://www.ardendertat.com/2011/09/20/programming-interview-questions-2-matrix-region-sum/

First preprocess so each matrix contains the sum from left most to the current index, then calculating the sum of a certain region will be the sum bottom right – bottom left – top right + top left.

http://www.ardendertat.com/2011/09/20/programming-interview-questions-2-matrix-region-sum/

subsets

class Solution {
  public static void main(String[] args) {   
    Solution s = new Solution();
    List<Integer> test = new ArrayList<Integer>();
    test.add(1);
    test.add(2);
    test.add(3);
    List<List<Integer>> res = s.subsets(test);
    for (List<Integer> l : res) {
      for(int i : l) {
        System.out.print(i);
        System.out.print(',');
      }
      System.out.print('\n');
    }
    System.out.println("Success");
  } 
  
  public List<List<Integer>> subsets(List<Integer> input) {
    List<List<Integer>> res = new ArrayList<List<Integer>>();
    if (input.size() == 0) {
      res.add(new ArrayList<Integer>());
      return res;
    }
    
    for (int i=0; i<input.size(); i++) {
      List<Integer> next = new ArrayList<Integer>(input);
      next.remove(i);
      List<List<Integer>> list = subsets(next);
      for(List<Integer> l : list) {
        l.add(0, input.get(i));
        res.add(l);
      }
    }
    return res;
  }
}

combination:

class Solution {
  public static void main(String[] args) {   
    Solution s = new Solution();
    List<Integer> test = new ArrayList<Integer>();
    test.add(1);
    test.add(2);
    test.add(3);
    List<List<Integer>> res = s.combination(test, 2);
    for (List<Integer> l : res) {
      for(int i : l) {
        System.out.print(i);
        System.out.print(',');
      }
      System.out.print('\n');
    }
    System.out.println("Success");
  } 
  
  public List<List<Integer>> combination(List<Integer> input, int k) {
    if (k > input.size()) {
      return null;
    }
    
    List<List<Integer>> res = new ArrayList<List<Integer>>();
    
    if (k == input.size()) {
      res.add(input);
      return res;
    }
    
    if (k == 0) {
      res.add(new ArrayList<Integer>());
      return res;
    }
    
    for (int i=0; i<input.size(); i++) {
      int curr = input.get(i);
      List<Integer> next = new ArrayList<Integer>();
      for (int j=i+1; j<input.size(); j++) {
        next.add(input.get(j));
      }
      List<List<Integer>> list = combination(next, k-1);
      if (list != null) {
        for (List<Integer> l : list) {
          l.add(0, curr);
          res.add(l);
        }
      } {
    }
    
    return res;
  }
}

1. regex match (假设*match to any number of character proceeding it while ? match any character) use dynamic programming.
public boolean regMatch(String s, String p) {
int [][] match = new int[p.length()+1][s.length()+1];
for (int i = 0; i<p.length(); i++) {
for (int j=0; jyes,
lockern->false.

6. HM闲聊,问项目哪里吸引我,为什么Uber。

1. 3个长度一样的array a1, a2, a3, 找出所有 A + B = C 的组合,A在a1里,B在a2,C在a3里;扩展到4个数组 a2, a2, a3, a4,找出A+B+C=D的组合。。 然后扩展到n各数组;做完了又给了一道题目,不难,忘了。。

这道题就可以从两个array开始,有两种办法,一种是将其中一个arry做成hashset,然后用另外一个array去一个一个尝试,或者将两个array都sort,然后一个从start开始,一个从end开始,分别从前往后和从后往前走,看能不能碰到target的值

public List<List> getSum(List a1, List a2, int target) {
Set set = new HashSet();
List<List> res = new ArrayList<List>();

for(int i=0; i<a1.length; i++) {
set.add(a1[i]);
}
for(int i=0; i<a2.length; i++) {
if (set.containsKey(target – a2[i])) {
List r = new ArrayList();
r.add(target – a2[i]);
r.add(a2[i]);
res.add(r);
}
}
return res;
}

public List<List> getArraySum(List a1, List a2, List a3) {
List<List> res = new ArrayList<List>();
for(int i=0; i<a3.length; i++) {
List<List> r = getSum(a3[i]);
res.addAll(r);
}
return res;
}

2. 一个系统里面,有user 在不断的login and logout, 现在给你几组user的(username, login_time, logout_time)的数据,打印出各个时间
点系统有几个active user;follow-up,修改我的实现,让我的函数支持 类似JS的callback机制.

//first sort by startTime, then by endTime. Use a heap to calculate the number of active users at the same time.

class User {
int uid;
int start;
int end;
}

class void printActiveUser(List users) {
Collection.sort(users, new Comparator(){
@Override
public int compare

3. 面我的是一个中国人神牛。因为我说我用c++,然后感觉他临时选了这道题给我。。 reader-writer problem. 用mutex 实现user-writer problem。 follow-up,问会有什么问题,其实会有starving 的情况发生,然后一起商量怎么处理.

ABnb 面经总结

输入是

[[‘John’, ‘Smith’, ‘john.smith@gmail.com’, ‘Los Angeles’, ‘1’],.
[‘Jane’, ‘Roberts’, ‘janer@msn.com’, ‘San Francisco, CA’, ‘0’],
[‘Alexandra “Alex”‘, ‘Menendez’, ‘alex.menendez@gmail.com’, ‘Miami’, ‘1’]]

需要返回

John|Smith|john.smith@gmail.com|Los Angeles|1
Jane|Roberts|janer@msn.com|San Francisco, CA|0
Alexandra “Alex”|Menendez|alex.menendez@gmail.com|Miami|1

public String parseCSV(List<List<String>> input) {
  if (input.size() == 0) {
    return "";
  }
  StringBuilder res = new StringBuilder();
  for(List<String> list : input) {
    StringBuilder sb = parseHelper(list);
    if (res.size() == 0) {
      res.append(sb);
    } else {
      res.append('\n');
      res.append(sb);
    }
  }
  return res.toString();
}

private StringBuilder parseHelper(List<String> list) {
  StringBuilder sb = new StringBuilder();
  for (String s : list) {
    s = s.substring(1, s.length()-1); //get ride of single quotes
    if (sb.length() == 0) {
      sb.append(s);
    } else {
      sb.append("|");
      sb.append(s);
    }
  }
  return sb;
}

一个大妈考得,说是一般在店面考,卧轨了。
csv parser
如果有逗号,转化成|
如果有引号,把不考虑引号里逗号,把引号里的内容去引号整体打印。
如果有两重引号,只去掉一重引号。

例子
aa, bb, “aa”,”aa,bb”, “aa””aa”””
输出
aa|bb|aa|aa,bb|aa”aa”

public class Solution {
	public String parseCsv(String s) {
		  StringBuilder sb = new StringBuilder();
		  int quotes = 0;
		  int start = 0;
		  int end = 0;
		  while (end < s.length()) {
		    char c = s.charAt(end);
		    if (c == '"') {
		      quotes++;
		    } else if (c == ',') {
		      if (quotes%2 != 0) { //within a quote
		        end++;
		        continue;
		      } else {
		        String tmp = s.substring(start, end);
		        if (sb.length() != 0) {
		          sb.append('|');
		        }
		        sb.append(processString(tmp)); //remove nested quotes
		        start = end+1;
		        end = start;
		      }
		    }
		    end++;
		  }
		  //last string
		  String tmp = s.substring(start, end);
		  if (sb.length() != 0) {
	          sb.append('|');
	      }
	      sb.append(processString(tmp)); //remove nested quotes
		  return sb.toString();
		}

		private String processString(String s) {
		  int quotes = 0;
		  for(int i=0; i<s.length(); i++) {
		    if (s.charAt(i) == '"') {
		      quotes++;
		    }
		  }
		  if (quotes == 0) {
			  return s;
		  }
		  int start = 0;
		  int end = s.length()-1;
		  StringBuilder tmp = new StringBuilder(s);
		  if (quotes == 2) {
			  while(tmp.charAt(start) != '"') {
				  start++;
			  }
			  while(tmp.charAt(end) != '"') {
				  end--;
			  }
			  tmp.deleteCharAt(end);
			  tmp.deleteCharAt(start);
			  
			  return tmp.toString();
		  }		  
		  while(quotes > 2) {
			  while(tmp.charAt(start) != '"') {
				  start++;
			  }
			  while(tmp.charAt(end) != '"') {
				  end--;
			  }
			  tmp.deleteCharAt(end); 
			  tmp.deleteCharAt(start);  
			  quotes -= 2;
		  }
		  return tmp.toString();
		}

}

实现一个mini parser, 输入是以下格式的string:”324″ or”[123,456,[788,799,833],[[]],10,[]]”
要求输出:324 or [123,456,[788,799,833],[[]],10,[]]
也就是将字符串转换成对应的格式的数据.
其实题不算难,楼主答得不好,面完就知道挂了

这道题如果用java来做的话,因为java 是strict type的,所以不能既return array 又return string的。所以我们建立一个特殊的class, 然后用stack来做。注意处理,的情况

class Node {
  int val;
  bool isArray;
  List<Node> list;
  public Node (int v) {
    this.val = v;
    this.isArray = false;
    this.list = null;
  }
}

public List<Node> miniParser(String input) {
  Stack<Node> stack = new Stack<Node>();
  //-1 => [, -2 => ], -3 => ,
  for (int i=0; i<input.length(); i++) {
    char c = input.charAt(i);
    if (Character.isDigit(c)) {
      if (stack.isEmpty() || stack.peek().isArray || stack.peek.val == -1) {
        Node n = new Node((int)(c-'0'));
      } else if (stack.peek().val == -3) {
        n = stack.pop();
        n.val = (int)(c-'0');
      } else {
        n = stack.pop();
        n.val = n.val*10 + (int)(c-'0');
      }
    } else if (c == '[') {
      Node n  = null;
      if (!stack.isEmpty() && stack.peek() == -3) {
        n = stack.pop();
      } else {
        n = new Node(-1);
      }
      n.val = -1;
      stack.push(n);
    } else if (c == ',') {
      Node n = new Node(-3);
      stack.push(-3);
    } else if (c == ']') {//if c == ']'
      Node n = new Node(0);
      n.isArray = true;
      while(!stack.isEmpty() && stack.peek().val != -1) {
        n.list.add(stack.pop());
      }
      stack.pop();
      stack.push(n);
    }
    List<Node> res = new ArrayList<Node>();
    while(!stack.isEmpty()) {
      res.add(0, stack.pop());
    }
    return res;
  }
}

1. text justification, leetcode problem

public List<String> textJustification(List<String> s, int limit) {
  List<StringBuilder> list = new ArrayList<StringBuilder>();
  StringBuilder sb = new StringBuilder();
  for(int i=0; i<s.length(); i++) {
    String tmp = s.get(i);
    if (sb.length() == 0) {
      sb.append(tmp);
    } else if (sb.length() + tmp.length() + 1 < limit) {
      sb.append(" ");
      sb.append(tmp);
    } else {
      list.add(sb);
      sb = new StringBuilder();
      i--;
    }
  }
  List<String> res = new ArrayList<String>();
  for (StringBuilder sbi : list) {
    int space = 0;
    for(int i=0; i<sbi.length(); i++) {
      if (sbi.charAt(i) == " ") {
        space++;
      }
    }
    if (spaces == 0) {
      while(sbi.length() < limit) {
        sbi.append(" ");
      }
    } else {
      while(sbi.length() < limit) {
        int i = 0;
        while(i < sbi.length()) {
          if (sbi.charAt(i) != " " && sbi.charAt(i-1) == " ") {
            sbi.insert(i, " ");
            i++;
          }
          if (sbi.length() >= limit) {
            break;
          }
          i++;
        }
      }
    }
   res.add(sbi.toString());
   }
 return res;
}

2. 比较两个document 是否相似
实际上是edit distance

public int editDist(String s, String t) {
  int[][] dist = new int[s.length()+1][t.length()+1];
  for (int i=0; i<=s.length(); i++) {
    for (int j=0; j<=t.length(); j++) {
      if (i==0 && j==0) {
        dist[i][j] = 1;
      } else if (i==0) {
        dist[i][j] = j;
      } else if (j==0) {
        dist[i][j] = i;
      } else {
        if (s.charAt(i-1) == t.charAt(j-1)) {
          dist[i][j] = dist[i-1][j-1];
        } else {
          dist[i][j] = Math.min(dist[i-1][j], dist[i][j-1], dist[i-1][j-1]) + 1;
        }
     }
  }
  return dist[s.length()][t.length()];
} 

3. 如何做国际化.
4. Google reader design

实现function:
1. subscribe / unsubscribe feeds
2. mark 整个feed topic as read / unreader

table schema :
user table:
id name … join_date

Feed table:
id name created_date

Post table: ( 1 feed includ many posts)
id feedId created_date

Post_status table:
id userId, postId, status (read/unread) created_time

Regular Expression Matching

//assume there is no continuous “*”s

public boolean regExpMatch(String s, String p) {
  boolean [][] isMatch = new boolean[s.length()+1][p.length()+1];
  for(int i=0; i<s.length(); i++) {
    for(int j=0; j<p.length(); j++) {
      if (i==0 && j==0) {
        isMatch[i][j] = true;
      } else if (i==0) {
        char c = p.charAt(j-1);
        if (c == *) {
          isMatch[i][j] = isMatch[i][j-2];
        } else {
          isMatch[i][j] = false;
        }
      } else if (j == 0) {
        isMatch[i][j] = false;
      } else {
        char c1 = s.charAt(i-1);
        char c2 = p.charAt(j-1);
        if (c2 == "*") {
          char c = p.charAt(j-1);
          if (c == c1) {
            isMatch[i][j] == isMatch[i-1][j];
          } else {
            isMatch[i][j] == isMatch[i][j-2];
          }
        } else if (c2 == '.') {
          isMatch[i][j] = isMatch[i-1][j-1];
        } else {
          isMatch[i][j] = c1 == c2 ? isMatch[i-1][j-1] : false;
        }
     }
     return isMatch[s.length()][p.length()];
   }

一个数组,选出不相邻子序列,要求子序列和最大,
[4,9,6]=10
[4,10,3,1,5]=15

public int findIntervalMax(int [] input) {
  int [] max = new int[input.length];
  for(int i=0; i&lt;input.length; i++) {
    if (i==0) {
      max[i] = input[i];
    } else if (i==1) {
      max[i] = input[i];
    } else {
      max[i] = Math.max(max[i-2] + input[i], max[i-1]);
    }
  }
  return max[input.length-1];
}

find maximum square with all 1's 

public int maxSquare(int [][] matrix) {
  if (matrix.length == 0) {
    return 0;
  }
  int max = 0;
  int [][] maxArea = new int[matrix.length][matrix[0].length];
  for (int i=0; i&lt;matrix.length; i++) {
    for (int j=0; j&lt;matrix[0].length; j++) {
      if (matrix[i][j] == 0) {
        maxArea[i][j] = 0;
      } else {
        maxArea[i][j] = Math.min(maxArea[i-1][j-1], Math.min(maxArea[i-1][j], maxArea[i][j-1]) + 1;
      }
      max = Math.max(max, maxArea[i][j]);
    }
  }
  return max;
}

题目是给定一个word list 和一个target word,要求输出在word list 里跟target
word的edit distance 相差不大于k的所有words。我写了一个基于edit distance的解
法(见下面),程序是要从头到尾都要写,包括main函数和test数据。写完后他问有没有
更好的解法,我说可以用trie,但是我不觉得我能在剩余时间里写得出来基于trie的解
法,就大致说了一下我认为的思路。在此也求大牛给一个基于trie解法的code。

简单的解法就是用edit distance一个一个扫。

如果用trie的话,可以用trie来预处理dictionary,如果发现一个prefix相对于target的edit distance 是m,而他下面的所有的长度小于n的数只要符合m+n < k的话都可以直接放进结果里,这样可以省掉一些计算。

另外还有一种办法就是先找出所有与target edit distance 是1的string,然后每次比较,将match的放进result里。

Given a method decode(testEncStr) which will return the decoded int id if testEncStr is decodeable or will throw an exception (or return null) if not, implement a new method decodeFind(String badEncStr) which takes a string and returns the decoded int id

给一个2d array,要求写一个顺序访问这个2d array的Iterator,包括hasNext()与next()。注意2d array的每行中元素的个数可能不一样,也可能为空。跪在这最后一面上了。
二面的时候他忘了问culture, 所以加了一面。最后跪在最后一面上了,估计是culture不fit

class Iterator {
int col;
int row;
List<List<Integer>> array;
public Iterator(List<List<Integer>> init) {
int col = 0;
int row = 0;
this.array = init;
}

public boolean hasNext() {
if (this.array == null || this.array.size() == 0) {
return false;
}
if ((col + 1) < this.array.get(row).size()) {
return true;
} else {
int r = row + 1;
while(r < this.array.size()){
if (this.array.get(r).size() > 0) {
return true;
}
r++;
}
return false;
}
}

public int getNext() {
if (col + 1 < this.array.get(row).size()) {
col++;
return this.array.get(row).get(col);
}
row++;
while(row < this.array.size()) {
if (this.array.get(row).size() > 0 ) {
col = 0;
return array.get(row).get(col);
}
row++;
}
return null;
}

Often, we want to encode raw IDs in our database by hiding them behind some
2-way decodeable hash. So, a URL which would have at one time been:

https://www.airbnb.com/rooms/848662
becomes

https://www.airbnb.com/rooms/kljJJ324hjkS

We decode the ID kljJJ324hjkS_ to 848662 on our backend and serve the
relevant content. at some point, we start getting 404 errors from clients
requesting a certain URL of the form

https://www.airbnb.com/rooms/kljjj324hjks_

This can happen if certain clients, email services, or url shorteners "
sanitize" the url. Unfortunately, this change breaks decoding and the
resource cannot be found.
To assess how big of a deal this is, we may want to recover the IDs of the
targets that were 404ing.

Your task:
Given a method decode(testEncStr) which will return the decoded int id if
testEncStr is decodeable or will throw an exception or return null.
depending on the language) if not, implement a new method decodeFind(String
badEncStr) which takes a string and returns the decoded int id.

其实就是找出所有可能的大小写组合。

public int decodeFind(String badEncStr) {
List list = recursivelyGetAllPossibleStr(badEncStr);
for (String s : list) {
if (decode(s) != null) {
return decode(s);
}
}
return null;
}

private List recursivelyGetAllPossibleStr(String s) {
List res = new List ();
if (s.length() == 0) {
res.add(“”);
return res;
}
char c = s.charAt(0);
List next = recursivelyGetAllPossibleStr(s.substring(1));
if (Character.isLetter(c) {
char tmp = Character.toLowerCase(c);
for (String n : next) {
res.add(tmp + n);
}
tmp = Character.toUpperCase(c);
for (String n :next) {
res.add(tmp + n);
}
} else {
for (String n : next) {
res.add(c + n);
}
}
return res;
}

Web Service

Failure rate = number of users who failed to use the service/total user who used the service.

How to collect the data for failure rate? User send a log to server when it visited our website

What happened wen you enter http://www.google.com in your browser and hit enter.
first go to DNS and get the IP address of http://www.google.com
DNS lookup and prepare reply
DNS send reply back.
send request to the webserver.
prepare webpage reply.
send web page reply.

Process webpage.

request player

request file

Failure analysis:

DNS failure :
WebServer failure: Reverse Proxy (add more servers). Reduce the size of webpage (Simplify content, Rewrite JS code). Compress/merge images. Lazy Load, batch connection
More Cacheable pages. Change dynamic webpage into static webpage.
Limit request

Process File download
1. send request to the file server
2. Network error (file server failure?),
3. fail to establish connect,
4. fail to find file,
5. Network error when responding.

CDN:
Distributed File server.address is provided by the web server, no need to go to the DNS.

系统设计:登录系统 (account system)

1. User Scenario : Register/Update/Remove Login/Logout Balance/VIP/ProService
2. Constraints :
Assume 10,000,000 daily active user.
average login time: 1.2 (people may login multiple times per day).
100,000,000 DAU in 3 month.
Login: assume 15% user will login. 1,500,000
normal login frequency 1,500,000 / 24 * 2 (because most people will not login during night time etc..)
peak login in 3 * normal login frequency.
3. Data:
class User {
int userID //primary key
string name; (fixed length)
string password; (encrypted)
List sessionList
int state;
}

function insert, delete, update,
status(unverified, active, baned, inactive)

login/logout -> user need to logout after a period of time. login from pc and ipad

class Session {
int sessionID
int userID
int deviceCode; (ipad, pc)
long time_out
}

denormalize session List

Notice that Session and User are very similar, so we can use inheritance.

class Table {
private List table;
public function update
public function insert
public function remove
}

class SessionTable extends Table{
List table;
}

class UserTable extends Table {
List table;
}

proServeice/money: need to be protected by transaction.

Zenifits 面经

这是一家做HR/payroll 软件的公司。好像题目还比较难

下面是搜集的online assessment的题目
1. longest chain:
类似word ladder,对于一个单词删掉任何一个字母,如果新单词出现在给的词典里 那么就形成一个 chain: old word -> new word -> newer word, 求最长长度(return int) 比如给vector w = {a,ba,bca,bda,bdca} 最长是4: bdca->bda->ba->a;

思路和word ladder一样,我们可以先sort这个set, 然后从最长的开始。(或者我们建立一个hashMap, 单词长度作为key,单词存进一个list作为value, 这样也可以从最长的开始),然后遍历删除每个字母检查是否在字典中,如果在的话可以把max++, 然后把单词从字典中删除(如果从长度最大的单词一次往下遍历,删除是合理的,因为这个单词肯定已经在最长的list里算过了), 最后return max即可。

2 N queen 变种
题目巨长输入格式是
1
2
就是说 第1行第1个是queen 第2行第2个是queen,并保证输入的数字不重复,这样可以得出一个结论:同一行 同一列不会出现2个queen。
题目要求是求出 对于每个Queen, 最大的威胁次数,威胁指只要一个queen所能移动的范围内有别的queen就算威胁 P.S.同一方向上有2个queen威胁你 只算最近的那个。
由于同一行 同一列不会出现2个queen。(由于输入限制)所以只要考虑对角线 和逆对角线。
举个例子: 棋盘是:
100 —- 1号 queen
010 —- 2号 queen
001 —- 3号 queen
1号和3号queen的受威胁次数都是1, 2号是2(被1和3) 所以答案是2

简单的做法就是每个queen都遍历两个对角线,只要碰到有一个threat,threat就++, threat最多只能有四个。

我还能想到另外一种做法,就是用mask,以上面的例子为例,每一行做一个mask,然后当mask往下移动一行的话就需要left shift(right shift for another diag)再和下一行queen的位置or作为新的mask, 如果mask和当前queen的位置and是大于1的话,说明有thread,从上到下扫一遍只能找到上面的diagnal,对于每一行要记录当前行从上到下的thread,然后再从下到上用同样方法扫一遍就可以了。

Onsite题:
1.Design a service that returns a unique integer for each request.
可以用timestamp + machineid + thread_id + cnt

2.find median of two sorted array
这道题是leetcode题,注意边界条件,先讨论General情况,然后考虑边界条件。注意start, end和k的关系,start和end应该是坐标,而k是从start开始算的数。

brain teaser
一副扑克牌52张,你从里面抽5张,看了牌之后你放回去一张,剩下的4张按顺序排放
给你的朋友看,你和你的朋友实现约定好如何encode/decode这四张牌,问如何decode、encode才能让你的朋友猜出来放回牌堆的是哪张牌
首先我们可以知道的是5张牌一定是有两张同花色的,然后我们挑小的那一张放进去,大的那一张留下用来表示花色。最后剩下的3张做permutation,可以有6种方法,然后用这六个编码表示小的那张牌到大的那张牌的offset。

游戏理论

这类问题的特点就是
1. 只有两个玩家。
2. 两个玩家轮流走,最后有一个赢或者输的条件
3. 每个玩家每步都有确定的输赢,不存在中间状态。
这类问题共同的做法就是每个玩家每步存在两种可能性,要么是wining position,要么是losing
每个状态有如下的属性:
1. 结束状态是losing position
2. 如果玩家可以按规则到达一个losing position,那么当前他就处在winning position
3. 如果没办法到达losing postion,所有可能的move都只能移动到winning position,说明当前是losing position,写出代码来就是
boolean isWin(position pos) {
try all possible newPos moves
if !isWin(newPos)
return true;
return false;
}

给个例子,如果我们有11个硬币,每次只能拿1,3,4个数,怎样判断先手是否能赢。(拿最后一个硬币的算赢)。
照上面的例子,如果拿到0个的时候,这时候就处在losing position了。所以写出代码
boolean isWin(position pos, int[] coins) {
if (pos W
1 – => W
2 – => L
3 – => W
4 – => W
5 – => W
6 – => W
7 – => L
发现规律,对于连续n个-
如果分割成L + L或者W + W,当前是L, 如果一个L一个W,当前是W。如果循环所有可能分割,只要其中有一个W,那当前就是W
所以说可以用递归很容易找到每个连续的——这样的是W还是L,然后再考虑如果有多个连续的—-的情况,如果L的个数是一个,那么第一个走的玩家一定是L,反之如果有两个,那么第一个走的玩家是W的。以此类推,只要有奇数个L那么第一个玩家是输,不然的话是赢。

计算每个连续—的话,可以用dynamic programming 的方法来优化。

6.algorithm game,两个玩家从一组数里轮流取数,取过就从数组拿走,如果某个玩家取
数后所有已经取出的数和超过给定值则胜出,要求判断第一个拿是否能赢写函数
boolean isWin(Set choosable,target)
这道题属于游戏问题,topcoder里面有讲这类问题
https://www.topcoder.com/community/data-science/data-science-tutorials/algorithm-games/
idea就是如果你能走到losing position,那么你现在就处于winning position,不然说明你只能走到winning position,那说明你现在属于losing position,用递归写这道题就是

isWin(Set choosable, target){
if (choosable.isEmpty()){
return false;
}
for(int i : choosable) {
if (i >= target)
return true;
choosable.remove(i);
if (!isWin(choosable, target-i)) {
return true;
}
choosable.add(i);
}
return false;
}

G的几道面试题

1. card shuffler:shuffle的程序是一个简单的array,array里的值代表当前位置卡片的下一个位置
e.g 当前卡片位置ABCDE shuffle array是01234的话下个卡片位置还是ABCDE,43210的话下个卡片位置是EDCBA。
问给定一个shuffle array,不断用这个array去shuffle,能否得到最初的card deck,能得话要多少次。

我能想到的做法是一个一个的字母找,看多少次能回到原来的位置(或者不能回到原来的位置),记录这个次数为t[i], 然后求所有t[i]的LCM最小公倍数,
求最小公倍数的方法是先求最大公约数(用辗转相除法),然后再用a*b/gcd(a,b) 求最小公倍数。

public int cardShuffler(int[] cards, int[] shuffle) {
    int[] steps = new int [cards.length];
    for(int i=0; i<cards.length; i++) {
      steps[i] = stepsBack(i, shuffle);
      if (steps[i] == -1)
          return -1;
    }   
    return LCM(steps);
}

private int stepsBack(int start, int[] shuffle) {
  int i = start;
  int res = 0;
  Set visited = new HashSet();
  do {
    i = shuffle[i];
    if (visited.contains(i))
      return -1;
    visited.add(i);
    res++;
  } while(i != start);
  return res;
}

  private int LCM(int[] steps) {
    int res = steps[0];
    for(int i=1; i<steps.length; i++) {
      res = res*steps[i]/GCD(res, steps[i]);
    }
    return res;
}

private int GCD(int a, int b) {
  int n1 = Math.max(a,b);
  int n2 = Math.min(a,b);
  while(n1%n2 != 0) {
    int tmp = n1%n2;
    n1 = n2;
    n2 = tmp;
  }
  return n2;
}

2.给定一个binary search tree,返回range内所有key,key可以有重复。
感觉这道题比较简单,就是二分法递归就可以。
public List getKeys(TreeNode root, int start, int end) {
if (root == null)
return new List();
if (start > root) {
return getKeys(root.right, start, end);
}
if (end < root) {
return getKeys(root.left, start, end);
}
List left = getKeys(root.left, start, root.key);
List right = getKeys(root.right, root.key, end);
left.add(root.key);
right.addAll(right);
return left;
}
3. 把一个数拆成任意个平方和的最小拆法。
f(0) = 0;
f(n) = for(i = 1 to sqrt(n))f(n-i*i) + 1;
应该可以用DP优化。
public class Solution {
public int findMinSqrSum(int n) {
int [] min = new int [n+1];
min[0] = 0;
for (int i=1; i<n; i++) {
int m = min[i-1]+1;
for(int j = 1; j <= (int)Math.sqrt(i); j++) {
m = Math.min(m, min[i-j*j]+1);
}
min[i] = m;
}
return min[n];
}
}

1. BST求两个节点和为某个值的数目,2sum变种
简单的做法就是遍历所有的数,将它们存在一个hashset里,因为bst没有duplicate,然后再遍历一遍,找出2sum即可。
还有更优的方法就是用BST的sorted性质,做两个iterator,一个从头开始找next,一个从尾部开始找prev,
public Boolean TwoSumBST(TreeNode root, int target) {
NextIter nIt = new NextIter(root);
PrevIter pIt = new PrevIter(root);
TreeNode n = nIt.next();
TreeNode p = pIt.prev();
while(n.val < p.val) {
if (n.val + p.val == target) {
return true;
} else if (n.val + p.val < target) {
n = nIt.next();
} else {
p = pIt.prev();
}
}
return false;
}

public class NextIter {
Stack stack;
TreeNode p;
public NextIter(TreeNode root) {
stack = new Stack();
TreeNode p = root;
while(p != null) {
stack.push(p);
p = p.left;
}
}

public TreeNode next() {
TreeNode rtn = null;
if (p == null) {
p = stack.peek().right;
rtn = stack.pop();
}
while(p != null) {
stack.push(p);
p = p.left;
}
return rtn;
}
}

public class PrevIter {
Stack stack;
TreeNode p;
public PrevIter(TreeNode root) {
stack = new Stack();
TreeNode p = root;
while(p != null) {
stack.push(p);
p = p.right;
}
}

public TreeNode prev() {
TreeNode rtn = null;
if (p == null) {
p = stack.peek().left;
rtn = stack.pop();
}
while(p != null) {
stack.push(p);
p = p.right;
}
return rtn;
}

public Boolean hasNext() {
return stack.isEmpty();
}
}
第二题Number of Islands。用BFS或者DFS都可以
2. 给字符串,写压缩算法,解压算法已有,例如aaabbbbcccc->aaa4xb4xc,需要考虑3aaaaa->35xa会出问题,
首先分析一下这个题,只有在连续字母超过3个的时候压缩才有意义,另外如果最前面如果是一个数字的话,比如说3aaaaa应该要压缩成3a4xa这样,而不能压缩成35xa。
之后就主要是implementation的问题了

3. 给一堆query,求一个小时内出现次数最多的1000个query,用lg(n)的方法。
对于每个entry,记录query的次数,同时维护一个min heap, 就是最小值在最上面,里面所有的值都比顶点值大的heap,然后每次query都给相应的entry 记录次数++,并且将其插入heap中,如果heap的size超过1000, 那么就拿掉最顶上的那个值(最小值),一个小时以后,heap里存的就是前1000个最大的数

4. 类似系统设计,不是设计一个具体的东西,就是问了很多类似GFS,MapReduce类似的概念.

5. 判断一个word的任何permutation是不是palindrome
能够形成palindrome的条件如下
如果word含有奇数个字母,那么只能有一个奇数个字母,其他都是偶数个字母
如果word含有偶数个字母,那么应该所有字母都是偶数个。

电面
1. Given a sorted array of floats, find the index of the number closest to x:
Example: {1.2, 2.5, 9.3} x = 5, return 1
那这道题其实就是2sum closest,从两边向中间扫,然后计算最小差值。

2. Of the pairs of words in the dictionary that have no letters in common, find one that maximizes the product of the words’ lengths.
这道题好像也没有什么特别的解法,就是一个一个扫O(n^2)

3. bob, joe, bob, jane, bob, joe, jack

bob = 3
joe = 2

topN(2) = bob, joe

interface TopN {
void insert(String query);
List getTop(int n);
}

我能想到的做法就是维护一个Hashmap记录每个String出现的次数。
当需要找topN时,建立一个N size的min heap,然后把所有的数往里放,如果超出了size的话就remove最小的那个,最后这个heap里存的就是topN个element了。只能想到这个做法。

写一个shuffle数组的算法
给一个数组A[],里面的数是无序的,比如 A[]={1,2,3,7,3,2,5}
shuffle完之后,使之满足A[0]=A[2]<=A[3]..
比如一种可能的结果是 A[]={1,7,3,5,2,2,1}

void shuffle(int A[], int n)
{
}

这道题的算法有两种:一种是先将数组分成两个部分,一部分是全部小于median的,另一部分是全部大于median的,然后两两交换位置插入。求没有sort的array的median的方法有很多种。
1. sort,O(logn)
2. 用minheap找第k最小元素,将所有的元素先build heap,是O(n), 然后call getMin k times,就可以得到第k小的元素。时间复杂度是 (O(n + klogn),但是要用掉一个O(n)的空间来存heap
3. 用maxheap,方法类似,先建立一个k大的maxheap,然后将所有元素往maxheap里面放,并且删掉最大的元素,最后maxheap里就是第k大的元素。
4. Quick Select:和quick sort类似,先选择一个pivot,然后找到这个pivot的位置,之后再根据这个pivot和k的关系来recursive的寻找,如果piovt的选择是随机的,那么average run time应该是O(n)的。
5 还有改进的Quick Select方法,可以保证worst case的runtime是O(n), 具体的方法是将array分成5个一组的group(最后一个group可能会没有5个元素),然后每个group找median,放入一个array叫m中,然后用recursive的方法找到这个m的median,用这个median作为pivot来partition这个array, 这样可以保证分出的array是(比较)balanced

另外一种算法是扫描所有奇数项,假设扫描到k了,我们看a[k], a[k-1]和a[k+1]的关系,
1.如果a[k]比他们都大,则不需要做事,直接跳到下一个奇数项
2.如果a[k]比他们都小,或者比其中一个小,则交换那个更大的值。
如果仔细想想,当k已经交换完了以后,k+1之前的数都满足了,而下一个技术项k+1是不可能交换一个比k+1更大的数到k+1的,所以k+1之前就都满足条件了,那么往后就会一直是对的。

Google 店面:
Given these operations:
Update triplets:
: indicates that A is the direct manager of B
: indicates that A and B have the same direct manager.

: indicates that A is the direct manager of B
Query triplet:
: print true/false A is in the management chain of B
这道题应该是先建立一个有向图,然后找是否在manager chain的话就是用dfs或者bfs就可以

public class GraphNode {
char name;
List childeren;
public GraphNode(char c) {
name = c;
children = new ArrayList();
}
public void addChildren(GraphNode n) {
children.add(n);
}
}

//add to graph is simple.
//dfs search for if A is in the management chain of B
public boolean isInManagementChain(GraphNode A, GraphNode B) {
if (isEmpty(A.children)) {
return false;
}
for(GraphNode n : A.children) {
if (n.name == B.name) {
return true;
}
if (isInManagementChain(n, B)) {
return true;
}
}
return false;
}

2. Binary Tree Maximum Path Sum
但是需要是不相邻的node才可以算, 那这道题的做法和Maximum Path Sum其实是一样的,只不过我们除了要传当前root的最大值,还要加上不包括root本身的最大值,这样才好比较。然后用一个global的max来记录大小。

7/29最新google电面,面完接着发面经,攒RP,求下一轮电面或者onsite!!!
通话质量不好,有杂音,好在面试官不是三哥/三姐,口音比较清楚,而且把问题写在了doc上.
两个题
1. longest consecutive numbers
]lc原题,但要考虑重复,而且numbers无序, 并且要输出最长的numbers,
example:
1, 2, 3, 4, 6, 7, 8, 9, 10, 11 → 6, 7,8, 9, 10, 11

11, 10, 9, 8, 7, 6 4, 3, 2,1 ->11, 10, 9, 8, 7, 6
1, 2, 3, 1, 2, 3, 4, 5 -> 1, 2, 3, 4, 5

1, 2, 3, 4, 3, 4, 5, 6, 7 -> 3, 4, 5, 6, 7
这道题的做法应该是用一个hashmap记录所有的数字发生的频率,然后挨个数字扫描,对于每个数字尽量向两边扩充,看能够扩充多少,如果大于max的话,就记录下这些numbers。

2.第1题的follow-up
numbers变成二叉树,找longest consecutive numbers
example:
1
2 3
5 3

→ 1, 2
树的题一向做的不好,感觉和树这种数据结构不来电,花了挺长时间,最后面试官说简化只要求最长的length就好,因为时间紧迫,随便写了一个递归就交了,不知道有没有bug,但是从面试官反应来看,应该写的不是太没水平~~

二叉树的话其实做法和Max Path Sum的做法差不多,让每个节点的children返回到他为止最长的连续numbers,然后用一个global的max比较记录。

public List findLCNinBT(TreeNode root) {
List max = new ArrayList();
}

private List _findLCN(TreeNode root, max) {
if (root == null) {
return new List();
}
List left = extend(_findLCN(root.left, max), root.val);
List right = extend(_findLCN(root.right, max), root.val);
if (left.size() > right) {
if (left.size() > max.size()) {
max = left;
}
return left;
}
if (right.size() > max.size()) {
max = right;
}
return right;
}

private List extend(List range, int i) {
if (range.isEmpty()) {
range.add(i);
}
else if (range.get(0) == i || range.get(0) == i+1)
range.add(0, i);
}
else if (range.get(size()-1) == i || range.get(size()-1) == i-1)
range.add(i)
} else {
range = new ArrayList();
range.add(i);
}
return range;
}

Onsite:

第一轮:
多叉树,每个节点是一个整数,求书中最长递增路径比如5,6,7,8,9
看似简单其实有不少坑,感觉就是跪在这一轮了,所以后边面的反而很放松。。

这道题其实跟之前的这道是一样的,只不过这次不是Binary Tree而是multi tree了。每个node应该要传递包括他自己的递增和递减两个序列给他的parent,并且用一个global的List来保存最长的序列。

第二轮:
四叉树压缩黑白图片,一个图片递归分成2×2四部分,如果一个区域颜色一样就设为叶子节点,算黑像素比例
follow up是给两个图片,把白色视为不透明,黑色视为透明,重叠在一起,返回一个图片,都用四叉树表示
这个递归不难,感觉做的不错,最后出了一个小bug,在他提示下改了

可以用dfs做,每往下一层,代表的百分比就/4
public double findPercentage(TreeNode t) {
Percentage total = new Percentage();
return dfsFindPrcentage(t, 0, total);
}

public class Percentage {
public double per;
}

private void dfsFindPercentage(TreeNode t, int level, Percentage total) {
if (t.isLeaf()) {
if (t.isBlack()) {
total += 1/(Math.pow(4, level);
}
return;
}
for(TreeNode n : t.children) {
dfsFindPercentage(n, level+1, total);
}
}

重叠的话也是用recursion做。
如果发现其中一个node是黑的leaf的话,直接将另外一个copy过来即可。如果是白色leaf的话,那么就保留白色leaf。

public TreeNode _dup(TreeNode t1, TreeNode t2) {
if (t1.isLeaf()) {
if (t1.isBlack()) {
return t2;
}
return t1;
}
if (t2.isLeaf()) {
if (t2.isBlack()) {
return t1;
}
return t2;
}
TreeNode res = new TreeNode();
for(int i=0; i<4; i++) {
res.children.add(_dup(t1.children.get(i), t2.children.get(i)));
}
return res;
}

一堆密码箱,每个密码都是四位0-9之间,算一个暴力破解String,包含所有可能的四位序列,让这个序列尽量短.
我们就用枚举的方法,尽量让重复的序列大,然后先试3个重复的,然后2个 1个。 用一个hashset来记录已经访问过的数字。
其实这是一种greedy algorithm,由于初始值不同,有可能不是最短的序列。

最后一轮,求sum(n^i) 就是1+n+n2+n3+…+n^N, 快速写了一个O(N)之后让我优化,其实这个二分O(lgN)很容易想的,但是当时用了很长时间表现不太好。
这道题可以用dp做
public long getPowerSum(int n, int i) {
int[] dp = int[i+1];
int total = 0;
for(int j=0; j<=i; j++) {
if (j == 0) {
dp[j] = 1;
} else {
dp[j] = dp[j-1]*n;
}
total += dp[j];
}
return total;
}

第二题是个矩阵,每个节点是一个计算机,计算机之间传一个文件的cost是节点值x路径长度,选择所有计算机为接收端,求所有文件传输的cost
感觉要用2次dp一次算出从上面和左边的,一次算出下面右边的。

input: 一个文件,包含了很多单词,可以全部装入内存一个target number : t
output: 一个单词的最小set,这些单词的出现的频率的总和大于等于t
比如,文件中 A 500次,B 300, C 200, D100, t = 1000. 结果为 A,B, C.

因为可以放进内存里,所以我们就用一个hashmap来记下所有的单词出现的频率, 然后用这个所有单词的list build一个max heap,最常出现的放在max heap的顶上,这样我们每次从heap里拿最大的那个值,加进total里并且和target比较即可。buildheap是O(n), 每次拿出一个heap的最大值是olog(n). 记下所有单词是O(n),所以最差情况还是O(n);
public List letterSum(List words, int t) {
Map<String, Integer> map = new HashMap<String, Integer>();
List sList = new ArrayList();
for(String s : words) {
if (map.containsKey(s)) {
map.put(s, map.get(s)+1);
} else {
sList.add(s);
map.put(s, 1);
}
}
maxHeapify(sList, map); //use List as Heap;
int total = 0;
List res = new ArrayList();
while(!sList.isEmpty()) {
String s = pop(sList, map);
res.add(s);
total += map.get(s);
if (total >= target)
return res;
}
return res;
}

private List maxHeapify(List list, Map<String, Integer> map){
for(int i=list.length()-1; i>= 0; i++) {
int leftChild = i*2 + 1;
int rightChild = i*2 + 2;
int r = i;
while(r != -1) {
r = maxHeapifyHelper(list, map, r);
}
}
}

private int maxHeapifyHelper(List list, Map<String, Integer> map, int i) {
int leftChild = i*2 + 1;
int rightChild = i*2 + 2;
if (leftChild < list.length() && rightChild < list.length()) { if (list.get(leftChild) > list.get(i) || list.get(RightChild > list.get(i)) {
return swap(list, leftChild, rightChild, i);
}
} else if (leftChild < list.length()) { if (map.get(list.get(leftChild)) > map.get(list.get(i))) {
swapHelper(list, leftChild, i);
return leftChild;
}
} else if (rightChild < list.length()) { if (map.get(list.get(rightChild)) > map.get(list,get(i))) {
swapHelper(list, rightChild, i);
return rightChild;
}
}
return -1;
}

private List swap(List list, Map<String, Integer> map, int i, int j, int root){
if (map.get(list.get(i)) > map.get(list.get(j))) {
if (map.get(list.get(i)) > map.get(list.get(root))) {
swapHelper(list, root, i);
return i;
}
} else {
if (map.get(list.get(j)) > map.get(list.get(root)) {
swapHelper(list, root, j);
return j;
}
}
return -1;
}

private void swapHelper(List list, int i, int j) {
int tmp = list.get(i);
list.set(j, list.get(i);
list.set(i, tmp);
}

private String pop(list, Map<String, Integer> map) {
swap(list, 0, list.size()-1);
int res = list.remove(list.size()-1);
maxHeapifyHelper(list, map, 0);
return res;
}

也可以用selection algorithm去解决的这个问题。每次随机找一个pivot,然后将pivot放到正确位置,统计之前的所有的数,看是不是可以满足target,如果可以满足的话,就继续找,直到找到一个可以满足的数并且之前一个正好是不满足的数。就是答案了。

店面:
第一题,上来问了一下hashing解释一下。说为啥能constant time get数据。 我心想index过去就有了。让后blabla。
完了叫我写hashcode function, 之前看到eclipse里面hashCode function的提示。 于是就写这个了。 s[0]*31^(n-1) + s[1]*31^(n-2) … + s[n]
然后就缩这个是O(n), 就不是constanttime :( , 那就算到s[0] 到s[10] 吧。

第二题,一道Math题。就是求float number 的squre root: public float sqrt(float f, int p), precision是表示小数点后位数(2就要两位一致)。我就先找到result两边的integer标为l , r。 然后就一阵二分法。问题是, 判断precision和大于一小于一时出错了。然后一阵改。。。。。表示很无奈。这种math, corner cases特别多的没准备好。说好的array, string,说好的tree,说好的recursive呢,都没有。。

public float sqrt(float t, int p) {
float res = (float)sqrt((int)Math.floor(t));
float diff = 1.0;
float step = diff/10;

for(int i=0; i<p; i++) { float l = res + diff; float r = res; float mid =(l + r)/2; while(l >= r) {
if (mid * mid == t)
return mid;
else if (mid * mid > t) {
l = mid – step;
} else {
r = mid + setp;
}
}
step /= 10;
diff /= 10;
res = l;
}
return res;

Google On site
1. binary search.
大概就是,一个数组, 1112223334445556677888…当中少2个数字,找到就行了。
写完大概还有20分钟,让他几个test case,向了4个case,把code path都过了一边,然后没时间了。
既然已经排过序,直接找一遍就是O(n).但是因为这个数组是排序的,所以我们可能可以用更好的办法(binary search O(logn)

public int[] find2Missing(int[] input, int start, int end, int[] res) {
if (start > end)
return res;
int mid = (start + end)/2;
if (input[mid] < input[mid+1]-1) { res = recordRes(input[mid] + 1, res); } if (input[mid] > input[mid-1])+1 {
res = recordRes(input[mid] -1, res);
}
if (res[0] != 0 && res[1] != 0) {//found
return res;
}
res = findMissing(input, start, res);
if (res[0] != 0 && res[1] != 0) {//found
return res;
}
res = findMissing(input, end, mid);
return res;
}

private int[] recordRes(int r, int [] res) {
if (res[0] == 0) {
res[0] = r;
} else {
res[1] = r;
}
return res;
}

大概就是一个数组
1,4,2,6….
每次调用一个函数,按照数组里面的数字的大小,返回相应的Index。
比如, 上面的例子就是
1/13 的概率返回0,
4/13的概率返回1.
首先我们把所有的和都加起来,比如说上面的例子就是13。
然后按rand一个1到13之间的数t,然后看t落在哪个index的区间,

给一个image, 中心对称一下。 LZ一直写的Python,他让我用java,因为输入是byte [] image,
有点忘记java了。 先大概10分钟写完,后来发现题目意思理解错误,Image 每个像素是一个bit,我以为是
一个byte。 然后擦光重写,最后虽然写完了,他看了一边,觉得没有bug。然后就提问了一下,结束。

这道题就是reverse每行,然后reverse每列,就得到了中心对称的图形。

Topological sort的题目:
Topological sort有两种做法,一种是用DFS,就是一般的DFS加一个visited set记录已经访问过的节点,一旦到达了一个根节点(他没有子节点或者子节点都被访问过)就放入一个result的array里,最后这个array里的顺序全部倒过来就是topological sort了。当然这种算法有个问题就是要先确定这个graph是acyclic的。

还有一种方法是先找到一个没有in edge的节点,将这个节点放入result里面,然后搜索这个节点的所有子节点,找下一个没有in edge的节点。如果找不到这样一个节点,说明有cycle。不然的话应该可以拿走图里面的所有节点。如果是这种做法的话,我们就需要一个in edge的matrix和一个out edge的matrix,out edge我们用来找下一步搜索的节点,in edge的用来remove节点并放入结果list

除了排dependency(pre-requisite的课程),还有一类字典的题可以用topological sort
有一个字典因为某种原因每个字符都被替换成一个别的字符了(但还是一一对应),
但是单词的顺序没有改变,比如
cat
coffee
common
变成了
dkc
dbhhzz
dbllbq

所以我们要做的就是先从这个单词的顺序中得到一个字母的topological sort, 两两比较,得出一个DAG
List<List> graph = new ArrayList<List>();
for (int i = 0; i=words.size()-2; i++) {
String w1 = words.get(i);
String w2 = words.get(i+1);
int j = 0;
int k = 0;
while(j < w1.length() && k < w2.length()) {
if (w1.charAt(j) = w2.charAt(k) {
i++;
j++;
} else {
if (graph.get(w1.charAt(j)-‘a’) == null) {
graph.get(w1.charAt(j) – ‘a’) = new ArrayList();
}
graph.get(w1.charAt(j) – ‘a’) = w2.charAt(k) – ‘a’;
}
}
}
//now generate a in graph out of the out graph.
List<List> inGraph = new ArrayList<List>();
for (int i=0; i<graph.length(); i++) {
for(int j = 0; j<graph.get(i).length(); j++) {
int k = graph.get(i).get(j);
if (inGraph.get(k) == null) {
inGraph.add(k, new ArrayList());
}
inGraph.get(k).add(k, i);
}
}

List = topSort(graph, inGraph);

private List topSort(

1. 二叉树的序列化和反序列化,节点的value是String类型
2. 找两个排好序的list的共同元素
5 -> 6 -> 6 ->8
4 -> 4-> 6 -> 6 -> 8
答案是 6 -> 6 -> 8

这道题的做法是可以这样,用两个指针指向这两个list的head,然后比较,如果相等的话,就把两个数都放进结果里,然后同时向后移动, 如果不相等,我们就只把比较小的那个向后移动。这样的话我们只会移动O(m+n)次。

follow up如果一个list很小一个list很大有没有办法优化。如果用上面的办法的话就其实没有什么差别了。

Onsite:

merge K lists变形。变简单,你可以定义一个class来描述输入list.
这道题就是两种做法,一种是建立一个k个的数大小的heap,然后每次都都从这个min heap的顶上拿最小的那个node,然后将这个list的下一个数放进heap里,直到拿完为止。
还有一种办法就是两两list merge。 假设每个list都是n个数长,那么时间复杂度应该是O(nlogK).

第二轮: 讨论算法,没有写代码。输入是一串字符和一个字典。找出字典里面包含所有输入字符的最短序列。

比如:输入时”ca”, 字典里面有 [“cat”,”tac”,”act”,”sdf”,”asdf”]
那么返回: “cat”,”tac“, “act”.
面试官期望的应该是用tree来做的,但是我没想到最优的tree结构。
唯一能想到的tree的结构就是所有的string 都sort一下,然后和原string hash到一个hashMap里,然后建trie。但是这个方法也不一定快。

給一個車牌號碼(美國的),以及一個dictionary,請找出dictionary裡含有所有該車牌號碼裡的所有英文字母(case insensitive)的最短字串.
車牌 RO 1287 [“rolling”, “real”, “WhaT”, “rOad”] => “rOad”
follow up:
(1) 如果dictionary裡有上百萬個字,該如何加速
(2) 如果dictionary有上百萬個字,然後給你上千個車牌號碼,要你回傳相對應的最短字串,該如何optimize?
所以我们如果要搜索所有的string的话,用strstr的话就是O(n*m), n是字典的大小,m是每个word的长度。可不可以做到O(n)呢?其实我们可以把26个字母转换成一个bit mask,这样每次比较每个单词是否包含特定的字母就只需要用一次位运算就可以了。这种方法的问题就是没法处理duplicate的情况,除非我们用一个array来统计每个字母出现的次数。

给你一组Treenode,他们每个有一个id,一个parentId,一个value,让你输出所有subtree的sum of value
注意这个是没有children node的,只有parentId。 她先问是top down还是bottom up,因为只有parentId所以是bottom up, 然后输出吧, 其实就是层遍历,但是重点是如何找每一层的node,什么时候跳出循环,她hint了我,然后写的时候出了java api的bugs。。不过还是立刻改过来了,然后问运行时间,结果中途电话还断了一次。。居然面试还能超时5分钟。。。也是醉了

周四Google电面完,周五收到onsite,周六来发下面经。

可能是因为我做得慢,总共就做了一道题,就是一个游戏。
input string:“+++–++-+”
游戏规则:每人每次可以 flip “++” to “–“(必须是相邻的)

第一问:generate all possible moves
第二问:determine if this player can will
Extra:这两个问题的implementation的Big-O

下个人没法flip了这个人就赢;all possible moves for next flip

对于all possible moves,我们可以用recursion dfs的方法,选定一个++,flip成–, 用剩下的string继续recursive直到找不到连续的++为止。O(n!)的时间复杂度。

当没有人可以flip了,这个人就输了,所以最后一个人的position是losing position。而只要上一步任意一步可以走到losing position,那么这个position就是winning position(因为假设player一定会挑optimized办法),不然他就是losing position。

用dp的话其实不太好用,我们可以用dp解决连续个“+++”的问题, 然后再用这个结果来解决整个string的问题。
boolean [] isWin = new boolean [s.length()];
for (int i = 0; i<isWin.length(); i++) {
if (i == 0 || i == 1) {
isWin[i] = false;
} else {
isWin[i] = false; // if cannot go to any losing position, it is losing position
for (int j = 0; j <= i-2; j++) {
if (isWin[j-1] && isWin[i-j-2]) || (!isWin[j-1] && !isWin[j-1]) {
isWin[i] = true;
break;
}
}
}
}
然后我们统计input里所有的连续++,,如果isWin的数目是奇数个,那么就是先手赢,不然的话就是输

1. array, 找一个point,两边总和相等, 很简单,要注意负数情况
其实就是两边扫,先计算一下所有的element的和,然后从左边开始扫。扫到一个数就加进左边的sum,并从右边的sum减去,直到搜到两边相等为止。

implement 2048

假设有一个队列每个人都有不同的高度。给定所有人的高度和每个人之前比他高的人的个数,要求rebuild 这个array。

按照比他之前的高度的人数开始往结果里放,先放0的,如果有多个0的那么就按高度顺序往里放,然后再放1.2。。。的

class Person {
  int height;
  int front;
  Person(int h, int f) {
    this.height = h;
    this.front = f;
  }
}
public int[] rebuild(int[] height, int[] front) {
  
  List<Person> list = new ArrayList<Person>();
  for (int i=0; i<height.length; i++) {
    list.add(new Person(height[i], front[i]);
  }
  //sort the people by front, then by height.
  Collections.sort(list, new Comparator<Person>() {
    @Override
    public int compare(Person p1, Person p2) {
      if (p1.front != p2.front) {
        return p1.front - p2.front;
      }
      return p1.height - p2.height;
    }
  );
  //insert the persons based on the sorted order
  List<Person> result = new ArrayList<Person>();
  for (int i=0; i<list.size(); i++) {
    Person p = list.get(i);
    if (p.front == 0) {
       result.add(p);
    } else {
       int count = 0;
       for(int j=0; j<result.size(); j++) {
         if (p.height < result.get(j).height) {
           count++;
         }
         if (count == p.front) {
           result.insert(j+1, p);
           break;
         }
       }
     }
   }

一个白人小哥,上来直接在google docs上粘贴了题目.
write a function to add operator(+-*/) to a string so it equals to a target value
for example
123,6 -> 1+2+3
12345, 27-> 1+2*3+4*5

然后我问了数字顺序能不能变,答说不能;问了要不要括号,答说不能用括号。
最开始就想到了类似leetcode basic calculator,后来又想到了用backtracking,但是backtracking不是很熟,写了半天也没写对,时间到了就问了几个问题就thank you了


public boolean getResult(String s, int target) {
  
}

public boolean helper(String s, int target, int pos) {
  if (pos == s.length) {
    return calculator(s) == target;
  }
  StringBuilder sb = new StringBuilder(s);
  if (helper(sb.toString(), target, pos+1)) {
    return true;
  }
  sb.insert(pos, '+');
  if (helper(sb.toString(), target, pos+2)) {
    return true;
  }
  sb.setCharAt(pos, '-');
  if (helper(sb.toString(), target, pos+2)) {
    return true;
  }
  sb.setCharAt(pos, '*');
  if (helper(sb.toString(), target, pos+2)) {
    return true;
  }
  sb.setCharAt(pos, '/');
  if (helper(sb.toString(), target, pos+2)) {
    return true;
  }
  return false;
}

//takes in a string of equation and return result
private int calculator(String s) {
  List<String> list = new ArrayList<String>();
  int start = 0;
  for (int i=0; i<=s.length(); i++){
    char c = s.charAt(i);
    if (!Character.isDigit(c) || i == s.length()) {
      String tmp = s.substring(start, i);
      list.add(tmp);
      start = i+1;
      if (i < s.length()) {
        String op = new String(c);
        list.add(op);
      }
    }
  }
  
  Stack<String> cal = new Stack<String>();
  for (int i = 0; i<list.size(); i++) {
    String t = list.get(i);
    if (t.equals("*")) {
      int r = String.ValueOf(cal.pop()) * String.ValueOf(list.get(i+1));
      cal.push(Integer.parseInt(r));
      i++;
    } else if (t.equals("/")) {
      int r = String.ValueOf(cal.pop()) / String.ValueOf(list.get(i+1));
      cal.push(Integer.parseInt(r));
      i++;
    } else {
      cal.push(t);
    }
  }
  //process the + and -
  int r = 0;
  while (!cal.isEmpty()) {
    int n = Integer.parseInt(cal.pop());
    if (cal.isEmpty() || cal.pop().equals("+")) {
      r += n;
    } else {
      r -= n;
    }
  }
  return r;
}

warm up 问题:求 int(log X). 第二题, 给一堆strings 和一个input string, 在input里找出minimum unused char.

求logX就是用binary search在0到X之间搜索一个数字n^10 x or n^10 == x
public int getLog(int X) {
int start = 0;
int end = X;
while(start X) {
end = mid-1;
} else {
start = mid+1;
}
}
return start;
}
2. 国人小哥。特别nice!给一个数字的array,两个数字间只用+或者* 算出最大的值。
当然可以用dfs try all possible combinations and get max。但是因为只需要知道max。所以可以用dynamic programming 的办法来记录每个的最大值和最小值(*)的话可能会由最小值变成最大值。等等

public int getMax(int[] input) {
int[] max = new int [input.length];
int[] min = new int [input.length];
for (int i=0; i<input.length; i++) {
if (i==0) {
max[i] = input[i];
min[i] = input[i];
} else {
max[i] = Math.max(input[i] + max[i], input[i] * max[i], input[i] * min[i]);
min[i] = Math.min(input[i] + min[i], input[i] * max[i], input[i] * min[i]);
}
}
return max[input.length-1];
}
3.烙印大叔。 给一个array 和一个 target求出 array里有几组tuple相加是小于等于target的。 第二题是一个array里面只有0-9的digits, 有一个target, 判断是否存在一种组合可以等于target。eg: [6,3,1,0,5] target=78,这个return True. 63+10+5 = 78 如果target= 636 return True. 631+0+5 = 636

public int tupletLessThan(int[] array, int target) {
Arrays.sort(array);
int count;
for (int i=0; i<array.length-2; i++) {
int start = i+1;
int end = array.length-1;
//fix end, and add all the numbers that sums to less than target
while(start < end) {
while (array[start] + array[end] + array[i] < target) {
count++;
start++;
}
end–;
start = i+1;
}
}
return count;
}

public boolean findTarget(int[] array, int target) {
int curr = 0;
}

private boolean helper(int[] array, int target, int start) {
if (target < 0) {
return false;
}
if (start == array.length) {
return target == 0;
}
for (int i=start; i<array.length; i++) {
int r = generateNumber(array, start, i);
if (helper, array, target-r, i+1) {
return true;
}
}
return false;
}

private int generateNumber(int[] arr, int start, int end) {
int res = 0;
for (int i=start; i<=end; i++) {
res *= 10;
res += arr[i];
}
return res;
}
4.烙印小哥。 rearrange array,使得相邻两个字符是不一样的。
首先统计所有字符的出现次数,然后每次都拿出现次数最多的那个数字。填进下一个空白的array。
class Node {
char c;
int freq;
public Node(char i, int j) {
this.c = i;
this.freq = j;
}

public void decr() {
freq–;
}
}
public char[] rearrage(char[] arr) {
Map map = new HashMap();
for (char c : arr) {
if (map.contains(c)) {
map.put(c, map.get(c) + 1);
} else {
map.put(c, 1);
}
}
PriorityQueue priority = new PriorityQueue(100, new Comparator(){
@Override
public int compare(Node n1, Node n2) {
return n1.freq.compareTo(n2.freq);
}
});
Iterator iter = map.iterator();
while(iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
priority.add(new Node(entry.getKey(), entry.getValue()));
}

char[] out = new char[arr.length];
int pos = 0;
while(!priority.isEmpty()) {
Node first = priority.remove();
if (pos == 0 || first.c != out[pos-1]) {
out[pos] = first.c;
pos++;
first.decr();
if (first.freq != 0) {
priority.add(first);
}
} else {
Node second = priority.remove();
out[pos] = second.c;
second.decr();
if (second.freq != 0) {
priority.add(second);
}
priority.add(first);
}
}
return out;
}
5. 俄罗斯小哥?长得像格格巫。。。LRU~~.

国人小哥shadow,白人geek主面。问题:将一个image mirror一下。image是用bit表示的。白人小哥不太理人。
如果是用bit表示的,我们只需要按bit reverse每个数就可以。
public int[] reverseImage(int[] image) {
int[] res = new int [image.length];
int p = 0;
for (int i : image) {
res[p++] = reverse(i);
}
return res;
}

private int reverse(int i) {
int res = 0;
int mask = 1< 0) {
res *= 2;
if (mask & i > 0) {
res + 1;
}
mask >> 1;
}
return res;
}

国人小哥,不知道结果怎么样,不过感觉就在一直提醒我,给我感觉挺好的。问题是一个文件,一个set的word,让你找出出现次数最多的word。
想起来感觉只有把所有word扫一遍,统计所有word出现的次数,同时可以找出出现最多的那个。

Map<String, Integer> map = new HashMap<String, Integer>();
for (String w : words) {
  if (map.containsKey(w)) {
    map.get(w).put(w, map.get(w)+1);
  } else {
    map.put(w, 1);
  }
  int maxCount = 0;
  String rtn = null;
  if (map.get(w) > maxCount) {
    maxCount = map.get(w);
    rtn = w;
  }
  return rtn;
}

白人lead,问题:总结一下就是, 有n个点,[0,1,2],输出序列为:[0,1,2,1,0,2,0] 满足: 0->1, 1->0, 0->2, 2->0, 1->2, 2->1, 求得最短输出序列。

感觉这道题只能一个一个的试,用DFS

这题我的想法是这样。。。

要求的这个序列,如果将两点之间看做一条单向边的话,其实这个序列就相当于将一个完全图遍历每个边,且每个边只遍历一次,就是一笔画问题。。
因为是完全图,一定存在解,然后直接dfs就行。。。然后观察一下dfs的解。很容易找到规律。所以最终是可以得到这题的构造解的。

所以实际上就是找一条DFS的路径,能够cover所有的edge,并且最短。

class Edge {
  int from;
  int to;
  @Override
  public int hashCode() {
  }
  @Override 
  public boolean equals() {
  }
}
Set<Edge> visited = new HashSet<Edge>();
List<List<Integer>> adjMatrix = ArrayList<List<Integer>>();
for(int i=0; i<arr.length; i++) {
  adjMatrix.add(i, new ArrayList<Integer>());
  for(int j=0; j<arr.length; j++) {
    if (i != j) {
      adjMatrix.get(i).add(j);
    }
  }
}

int n = arr.length;
int start = arr[0];
List<Integer> res = new ArrayList<Integer>();
dfsFindPath(adjMatrix, n, start, res, visited);
return res;
}

public void findPath(List<List<Integer>> adjMatrix, int n, int start; int res, Set<Edge> visited) {
  if (res.size() == n*(n-1)) {
    return res;
  }
  List<Integer> next = adjMatrix.get(i);
  for(int i : next) {
    Edge e = new Edge(start, i);
    if (!visited.contains(e)) {
      visited.add(e);
      res.add(i);
      findPath(adjMatrix, n, i, res, visited();
      visited.remove(e);
      res.remove(i);
    }
  }
}

午饭,国人小哥很好。一直跟我聊天。这轮不算在面试之中。
白人,感觉人很好。问题是: longest consecutive sequence in array。followup: longest consecutive sequence in tree,但是是简化版,只要求从parent 到root的longest sequence。
如果是array的话,就把他转换成一个Set,然后对于每个数向两边extend,对于tree的话,因为只是root 到leaf的,所以只要用一个函数return每个节点的最大值,然后用一个global的track,就可以。

白人小哥。问了我一曾经onsite的时候面过的题目。就是一个matrix存value。两种操作,一种是query(x1,y1, x2, y2)返回此区间的value的和。 第二种是update(x, y, v),要求更新matrix的值。followup 分别是如果你想要query快,如果你想要update快。如果你想即让query快,又想要update快。怎么办?

如果想要qurey快,那么就直接存一个每个坐标左上边的所有数的和的矩阵,如果update快,就要直接update原矩阵。

如果要两个都快,就取一个折中的办法,存每一行的col 0 到当前col的和,这样update 和 query都是
O(n)的复杂度了。
感觉整体下来面试不难,但是我每一轮都没有bugfree,每当我说我想要过一个例子来看一下有没有bug,面试官都是觉得没时间了,然后就帮我指出来哪里可能有问题。这个也是我担心的一点。

1. 华人大叔。题目:Given a large integer array, count the number of triplets whose sum is less than or equal to an integer T.
大叔英文很地道, 全程态度比较认真,不冷不热。一开始猛然间以为是3-Sum的题,仔细想想不太一样,细节问题挺多。最后写了一个O(n2lgn)的算法,然后问大叔更简便的有木有,大叔迟疑了片刻说应该有数学相关的取巧办法。。

这道题其实就是先排序,选定一个数,从之后的所有数里面找两个数sum小于target,这个也是先选定一个数,另一个二分查找就可以。

2. 华人小哥。题目:Given an array of Ad (profit, start_time, end_time) and a timespan [0, T], find the max profit of Ads that fit the timespan.
小哥进来就说了句中文:怎么样啊?顿觉好温暖,中文寒暄了几句就用英文开始了。先说了穷举法O(2^n), 然后说了贪心法(不是最优解),最后用DP解决。小哥态度灰常好,给了很有用的提示, 就像自家人啊。

感觉有点像knapsack problem,我们应该建立一个maxProfit[i][j], i start from 0 to T and j start from 0 to Ads[size];

for(int i=0; i<T; i++) {
for(int j=0; j ads[j].time) {
maxProfit[i][j] = Math.max(maxProfit[i-ads[j].time]][j-1] + ads[j].profit, maxProfit[i][j-1]);
} else {
maxProfit[i][j] = 0;
}
}
}
return maxProfit[T][ads.length];

3. 俄罗斯妹子。题目:M x N large board, with only white and black blocks, all black blocks are connected (vertically or horizontally), find the minimum rectangle boundary that contains all black blocks.
妹子笑的好甜,全程一直在笑,还好心地给了提示和假定。感觉交流互动非常好,可惜最后差了一点,没能想出O(n^2)以内的算法。

我们首先要找到第一个black de block,然后对black的block做DFS或者BFS,然后找出上向左右能碰到的最大的black block,然后就能找到矩形的边界了。
4. 美国小哥。题目:Design a algorithm to initialize the board of Candy Crush Saga. With M x N board, Q types of candies. (Rules: no 3 for run after initialization, must contain at least one valid move at the beginning)
小哥说话很和气,先让我介绍了一个project,于是兴致勃勃地讲了做过的一个游戏。他于是拿出手机给我看了这个,说那就出一道游戏题吧。。游戏可以参考(
http://candycrushsaga.com/how-to-play),这道题很开放,没有固定模式和答案,感觉答的还不错。

第一面,一个很屌很拽的三哥。上来就聊social newtwork,讨论里面的connection怎么去assign weight,没什么经验,直接各种脑洞,也不知道说得有没有道理。然后让写一个从一个social graph中找出N个朋友的function,这里我一直以为要用着那个edge的weight,结果开始往最长路径上去想,结果就GG了。后来就写了一个BFS到N个的时候就停止,结果被指出来一堆bug,比如visit的节点忘记加入set了,没处理null的情况,没处理N为非正数的情况啥的。主要还是一上来就被问懵了,这一面感觉就跪了。

List findNFriend(Person p, int n) {
if (n <= 0) return null;
if p == null return null;
List res = new ArrayList();
Stack stack = new Stack();
stack.add(p);
while(stack.isEmpty() && res.size() < n) {
Stack nextLevel = new Stack();
while(stack.isEmpty() && res.size() < n) {
Person np = stack.pop();
res.add(np);
for(Person n : np.friends) {
nextLevel.push(n);
}
}
stack = nextLevel;
}
return res;
}

第二面,一个中年白人。第一个题返回一个字符串中出现次数最多的字符,然后把这个题用MapReduce重新写一遍。第二个题写二叉树的最深深度。第三个题是leetcode上那个把二叉树每一层的节点水平的连接起来。第四个题写N!中0末尾0的个数。这一面感觉没什么bug的地方,感觉一般。
Map Reduce:
Map(String key, String Value) {
foreach word in value {
outputTemp(word, 1);
}
}

Reduce(String key, List value) {
int sum = 0;
foreach int in valueList {
sum++;
}
outputFinal(key, sum);
}

0的个数取决于2和5因数的数量,2的数量一定是大于5的,所以我们应该去找5的数量。N有多少个5的数量呢?个位数是每5个数就有一个,10位数则是每25位有一个,100位数是每125位有一个,这样推进下去。
int fives = 5;
int numZero = 0;
while(n >= fives) {
numZero += n/fives;
n /= fives;
fives *= 5;
}
return numZero;

第三面,一个很geek的三哥。实现SQL里面的group by的functionality,用最直接的方法实现后,更改一下文件的存储方式从而实现更快速的方法。最后拓展到distributed storage上面去,有多个machine在运行你的database,问怎么设计文件存储的方式,从而使你的query尽可能快的完成。这一面感觉一般。

第四面,一个年轻白人。自己设计接口,使得支持两个funciton:onUpdate(timestamp, price) 和 onCorrect(temistamp, price). 可以理解为有一个时间流,每一个timestamp都对应一个股票的时间,每次调用一次onUpdate的时候,就对我们设计的那个类更新对应的timestamp和price, onCorrect就是修改之前的一个timestamp的price。最后,我们的类要能返回latest price, max price 和 min price。一开始题目描述的太模糊了我都不知道到底要干啥,墨迹半天才知道是想设计一个类,然后中途也写的乱七八糟的,用了两个Deque来存储一个递增和一个递减的序列,类似窗口题的方法。当onCorrect的时候就去看队列里面有没有对应的timestamp,有的话移除然后重新入队。感觉这面面的也不是太好。

class Stock{
int id;
String name;
Double latestPrice;
int lastTimeStamp;
Double maxPrice;
Double minPrice;
Map map = new HashMap();
public stock() {
this.map = new HashMap();
maxPrice = Double.MIN_VALUE;
minPrice = Double.MAX_VALUE;
}
onUpdate(int timestamp, double price) {
map.put(timestamp, prices);
if (price this.maxPrice) {
maxPrice = price;
}
if (timestamp > lastTimeStamp) {
this.lastTimestamp = timestamp;
lastPrice = price;
}
this.map.add(timestamp, price);
}

onCorrect(int timestamp, double price) {
}

}

今天下午4:15面的,估计是个白人大哥,人挺耐心但自己水平太差被虐的体无完肤。。。
问一个二维数组表示的n*n的矩阵,找出一条连续的最长的路径的长度。
比如
7 8 6
9 4 5
2 3 1.
最长是2,3,4,5,6,返回长度5.
想到是DP但初始状态不知道怎么确定,以为和lc的 Minimum Path Sum 很像,结果他说不一定从(0, 0)开始就崩溃了。。。写了个暴力算法,让优化,估计GG了。。

O(1)的delte,update,add