[LeetCode] Candy

There are N children standing in a line. Each child is assigned a rating value.

You are giving candies to these children subjected to the following requirements:

  • Each child must have at least one candy.
  • Children with a higher rating get more candies than their neighbors.

What is the minimum candies you must give?

我一開始的思路是:

首先,將ratings數組按rating的值進行排序分層,做成一個multimap,每個rating作爲key,value是對應的所有數組下標,用一個list封裝。

之後,再根據rating的值從低到高給各個孩子分配最少的糖果數,初始值爲1,並且保證如果該孩子的rating比其鄰居的高,則必須比鄰居的糖果數要多1。

因爲要實現對rating的排序分層,我使用了TreeMap。

	public int candy(int[] ratings) {
		if (ratings.length == 0) return 0;
		int[] candies = new int[ratings.length];
		int count = 0;
		// Key: rating value; Value: all the corresponding child indices
		TreeMap<Integer, ArrayList<Integer>> tm = new TreeMap<Integer, ArrayList<Integer>>();
		for (int i = 0; i < ratings.length; i++) {
			if (!tm.containsKey(ratings[i])) {
				ArrayList<Integer> list = new ArrayList<Integer>();
				list.add(i);
				tm.put(ratings[i], list);
			} else {
				ArrayList<Integer> list = tm.get(ratings[i]);
				list.add(i);
			}
		}

		// traverse the tree map by the rating level
		Set<Integer> set = tm.keySet();
		for (int rating : set) {
			ArrayList<Integer> list = tm.get(rating);
			for (int index : list) {
				candies[index] = 1;
				// check if the candies are more than the left neighbor
				if (index - 1 > 0 && ratings[index] > ratings[index - 1]
						&& candies[index] <= candies[index - 1]) {
					candies[index] = candies[index - 1] + 1;
				}
				// check if the candies are more than the right neighbor
				if (index + 1 < ratings.length
						&& ratings[index] > ratings[index + 1]
						&& candies[index] <= candies[index + 1]) {
					candies[index] = candies[index + 1] + 1;
				}
				count += candies[index];
			}
		}
		return count;
	}

按照這樣的思路,總共需要遍歷原來rating數組一次來構造TreeMap,然後再遍歷一次TreeMap,計算分配給每個孩子的糖果數,並計算其總數。總共兩輪遍歷,感覺時間複雜度應該是O(N),可是爲什麼結果還是超時?

琢磨了一段時間,發現TreeMap由於具有排序時間,並不像HashMap那樣具有O(1)的插入時間,而是像其他具有排序性質的數據結構一樣,需要O(lgN)的時間才能完成插入操作。所以,這個實現的性能瓶頸在於構造TreeMap,一共需要O(N*lgN)的時間。

所以得另外轉換思路了。網上有一種巧妙的解法,只需要兩次對數組進行兩次掃描。

1)從左往右掃描,如果某個孩子的糖果數少於rating低的左鄰居,那麼就讓他持有比左鄰居多1的糖果數。

2)從右往左掃描,如果某個孩子的糖果數少於rating低的優鄰居,那麼就讓他持有比右鄰居多1的糖果數。

然後對數組求和即可。

	public int candy(int[] ratings) {
		if (ratings.length == 0)
			return 0;
		int[] candies = new int[ratings.length];
		for (int i = 1; i < ratings.length; i++) {
			if (ratings[i] > ratings[i - 1])
				candies[i] = candies[i - 1] + 1;
		}
		for (int i = ratings.length - 2; i >= 0; i--) {
			if (ratings[i] > ratings[i + 1])
				candies[i] = Math.max(candies[i], candies[i + 1] + 1);
		}
		int ret = ratings.length;
		for (int i : candies) {
			ret += i;
		}
		return ret;
	}

當然,這裏最後一步求和其實不需要另外掃描一遍數組,完全可以跟第二次掃描合併。

另外網上還發現了一種有趣的一維DP解法:

1)如果比前一個孩子rating高,必須比前一個孩子多給1個糖;

2)如果跟前一個孩子rating相同,給一個糖就可以了(並不需要跟前一個孩子所持糖果數相等)

3)如果比前一個孩子rating低,給一個糖,然後再多給前面孩子一個糖。另外,還有繼續一直檢查是否需要多給更前面的孩子一個糖,直到遇到情況1或情況2。

	public int candy(int[] ratings) {
		if (ratings.length == 0)
			return 0;
		int[] d = new int[ratings.length];
		d[0] = 1;
		for (int i = 1; i < ratings.length; i++) {
			if (ratings[i] == ratings[i - 1])
				d[i] = 1;
			else if (ratings[i] > ratings[i - 1])
				d[i] = d[i - 1] + 1;
			else {// should give less candies than the previous child
				d[i] = 1;
				if (d[i - 1] == 1) {
					int j = i;
					while (j > 0 && ratings[j - 1] > ratings[j]
							&& d[j - 1] == d[j]) {
						// only push backwards when the ratings are different
						// but the # of candies are the same
						d[j - 1]++;
						j--;
					}
				}
			}
		}
		int sum = 0;
		for (int i = 0; i < ratings.length; i++) {
			sum += d[i];
		}
		return sum;
	}



發佈了60 篇原創文章 · 獲贊 8 · 訪問量 24萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章