The problem looks simple and you will find it not. You might use a complicated approach to conquer it, for example, I convert it into a graph problem, some one else used divide-and-conquer scheme. That seems absolutely over-kill for such an explicit problem with simple data organization.
Yes, it's over-kill indeed. The official method is much simpler than I expected and I spent quite a while to think about the correctness of this simpler approach.
public class Solution {
public int candy (int[] ratings) {
ArrayList<ArrayList<Integer>> weakNeighbors = new ArrayList<ArrayList<Integer>>();
//First Pass
for (int i = 0; i < ratings.length; i++) {
ArrayList<Integer> weaks = new ArrayList<Integer>();
if (ratings.length == 1) {
return 1;
} else if (i == 0) {
if (ratings[i] > ratings[i+1]) {
weaks.add(i+1);
}
} else if (i == ratings.length - 1) {
if (ratings[i] > ratings[i-1]) {
weaks.add(i-1);
}
} else {
if (ratings[i] > ratings[i-1]) {
weaks.add(i-1);
}
if (ratings[i] > ratings[i+1]) {
weaks.add(i+1);
}
}
weakNeighbors.add(weaks);
}
//DFS
boolean[] visited = new boolean[ratings.length];
int[] shares = new int[ratings.length];
Stack<Integer> stack = new Stack<Integer>();
for (int i = 0; i < visited.length; i++) {
if (! visited[i]) {
dfs(weakNeighbors, ratings, visited, shares, i, stack);
}
}
int result = 0;
for (int i = 0; i < shares.length; i++) {
result += shares[i];
}
return result;
}
public void dfs(ArrayList<ArrayList<Integer>> weakNeighbors, int[] ratings,
boolean[] visited, int[] shares, int root, Stack<Integer> stack) {
stack.push(root);
while (! stack.isEmpty()) {
int next = stack.pop();
visited[next] = true;
ArrayList<Integer> weaks = weakNeighbors.get(next);
if (weaks.isEmpty()) {
shares[next] = 1;
} else {
//If has unvisited neighbor, push it back to stack and push its neighbors
boolean neighborVisited = true;
for (int i : weaks) {
if (! visited[i]) {
neighborVisited = false;
}
}
//Compute its share
if (neighborVisited) {
int highest = 0;
for (int i : weaks) {
if (shares[i] > highest) {
highest = shares[i];
}
}
shares[next] = highest + 1;
} else {
visited[next] = false;
stack.push(next);
for (int i : weaks) {
if (! visited[i]) {
stack.push(i);
}
}
}
}
}
}
}
It's complicated, but it works. The general idea is:
u->v indicating rating u is higher then rating v and of course u and v must be neighbors.dfs algorithm to traverse the graph, compute the shares of each rating by following rules:
The idea is a bit complicated but straight forward. The main problem of my implementation is originally I used recursive dfs to traverse the graph and it sucks on some test cases: stack over flow error. Then I convert it into an iterative version and it becomes more complicated but works perfectly finally.
I don't like this solution though it's better than nothing. There is another much more decent and simpler solution online.
The general idea of this solution is:
This approach is so concise that I'm tended to double its correctness. Well its correct and following is my idea about its correctness: