Given a circular array (the next element of the last element is the first element of the array), print the Next Greater Number for every element. The Next Greater Number of a number x is the first greater number to its traversing-order next in the array, which means you could search circularly to find its next greater number. If it doesn’t exist, output -1 for this number.
***Example 1:
Input: [1,2,1]
Output: [2,-1,2]
Explanation: The first 1’s next greater number is 2;
The number 2 can’t find next greater number;
The second 1’s next greater number needs to search circularly, which is also 2.*
这是一道Medium的题,但其实我是先做到了它的前一题。
496:
You are given two arrays (without duplicates) nums1 and nums2 where nums1’s elements are subset of nums2. Find all the next greater numbers for nums1’s elements in the corresponding places of nums2.The Next Greater Number of a number x in nums1 is the first greater number to its right in nums2. If it does not exist, output -1 for this number.
Example 1:
Input: nums1 = [4,1,2], nums2 = [1,3,4,2].
Output: [-1,3,-1]
Explanation:
For number 4 in the first array, you cannot find the next greater number for it in the second array, so output -1.
For number 1 in the first array, the next greater number for it in the second array is 3.
For number 2 in the first array, there is no next greater number for it in the second array, so output -1.
Example 2:
Input: nums1 = [2,4], nums2 = [1,2,3,4].
Output: [3,-1]
Explanation:
For number 2 in the first array, the next greater number for it in the second array is 3.
For number 4 in the first array, there is no next greater number for it in the second array, so output -1.
Note:
All elements in nums1 and nums2 are unique.
The length of both nums1 and nums2 would not exceed 1000.
两道题的思路其实差不多,只不过503从496找到右边的更大数,变成了找到循环数组右边的更大数。
496首先我尝试了暴力解法:
- 找到findNums里的数在num数组里的位置;
- 在num里往右边找第一个比他大的数;
写倒是写出来了,但是感觉太NAIVE & STUPID,并且看起来很复杂。所以查看discuss大神的解法。
首先是暴力解法503:
public class Solution {
public int[] nextGreaterElements(int[] nums) {
int max = Integer.MIN_VALUE;
for (int num : nums) {
max = Math.max(max, num);
}
int n = nums.length;
int[] result = new int[n];
int[] temp = new int[n * 2];
for (int i = 0; i < n * 2; i++) {
temp[i] = nums[i % n];
}
for (int i = 0; i < n; i++) {
result[i] = -1;
if (nums[i] == max) continue;
for (int j = i + 1; j < n * 2; j++) {
if (temp[j] > nums[i]) {
result[i] = temp[j];
break;
}
}
}
return result;
}
}
首先制造了一个2倍长度的数组当作循环数组,注意用了i%n(which I can’t think of)来获得相同的元素。
然后就和496的方法一样了,从这个数后面开始找。
首先贴smarter代码:
public int[] nextGreaterElement(int[] findNums, int[] nums) {
Map<Integer, Integer> map = new HashMap<>(); // map from x to next greater element of x
Stack<Integer> stack = new Stack<>();
for (int num : nums) {
while (!stack.isEmpty() && stack.peek() < num)
map.put(stack.pop(), num);
stack.push(num);
}
for (int i = 0; i < findNums.length; i++)
findNums[i] = map.getOrDefault(findNums[i], -1);
return findNums;
}
日常懵B,首先涉及到两个高级的数据结构(对本渣而言),STACK ,MAP。
找来介绍:
stack
序号 方法描述
1 boolean empty()
测试堆栈是否为空。
2 Object peek( )
查看堆栈顶部的对象,但不从堆栈中移除它。
3 Object pop( )
移除堆栈顶部的对象,并作为此函数的值返回该对象。
4 Object push(Object element)
把项压入堆栈顶部。
5 int search(Object element)
返回对象在堆栈中的位置,以 1 为基数。
Map接口中键和值一一映射. 可以通过键来获取值。 给定一个键和一个值,你可以将该值存储在一个Map对象.
之后,你可以通过键来访问对应的值。 当访问的值不存在的时候,方法就会抛出一个NoSuchElementException异常.
当对象的类型和Map里元素类型不兼容的时候,就会抛出一个 ClassCastException异常。
当在不允许使用Null对象的Map中使用Null对象,会抛出一个NullPointerException 异常。
当尝试修改一个只读的Map时,会抛出一个UnsupportedOperationException异常。
1 void clear( ) 从此映射中移除所有映射关系(可选操作)。
2 boolean containsKey(Object k)
如果此映射包含指定键的映射关系,则返回 true。
3 boolean containsValue(Object v)
4 Set entrySet( ) 返回此映射中包含的映射关系的 Set 视图。
5 boolean equals(Object obj) 比较指定的对象与此映射是否相等。
6 Object get(Object k)
7 int hashCode( ) 返回此映射的哈希码值。
8 boolean isEmpty( ) 如果此映射未包含键-值映射关系,则返回 true。
9 Set keySet( )
10 Object put(Object k, Object v)
将指定的值与此映射中的指定键关联(可选操作)。
11 void putAll(Map m)从指定映射中将所有映射关系复制到此映射中(可选操作)。
12 Object remove(Object k)
如果存在一个键的映射关系,则将其从此映射中移除(可选操作)。
13 int size( ) 返回此映射中的键-值映射关系数。
14 Collection values( ) 返回此映射中包含的值的 Collection 视图。
要点就是MAP里存的对象是成对出现的,一个key对应一个value,并且不能重复key;符合题目要求。
代码的逻辑就是,[9, 8, 7, 3, 2, 1, 6],类似这样一个数组,开始循环,先把第i放到栈里面,然后看第i+1是否比栈顶(第i)大,如果是就pop()这个栈当作key,第i+1当作value存到map里。
在这个具体的例子里,栈会在pop以前会是[9, 8, 7, 3, 2, 1],然后到6就把3,2,1pop出去存到map里面。
在503里面,变成循环数组,理所当然的就写要循环两遍。
比较容易的思路就是 第一遍循环当作496来做,第二遍循环就找在左边比他大的数;
public int[] nextGreaterElements(int[] nums) {
int n = nums.length, next[] = new int[n];
Arrays.fill(next, -1);
Stack<Integer> stack = new Stack<>(); // index stack
for (int i = 0; i < n * 2; i++) {
int num = nums[i % n];
while (!stack.isEmpty() && nums[stack.peek()] < num)
next[stack.pop()] = num;
if (i < n) stack.push(i);
}
return next;
}
这里有我非常不理解的地方就是nums[stack.peek()] < num,原来我看了半天以为是stack.peek() < num,心想这么着也不对呀,原来是受到上一题的干扰;唯一的不同就是这一次用stack来存储数组的下表,即循环数组里的数不会进入到栈里,而是下标进栈。
思考这和i++有何不同?:
MD,虽然我说不出来但就是觉得这个方法好厉害啊!!
大神说法:暴力解法的双for循环O(N2),这个解法因为所有元素只会进出一次stack所以是O(N)。