1. 程式人生 > 其它 >【LeetCode】746. Min Cost Climbing Stairs 使用最小花費爬樓梯(Easy)(JAVA)每日一題

【LeetCode】746. Min Cost Climbing Stairs 使用最小花費爬樓梯(Easy)(JAVA)每日一題

1.題目描述

給定一個整數陣列,你需要尋找一個連續的子陣列,如果對這個子陣列進行升序排序,那麼整個陣列都會變為升序排序。
你找到的子陣列應是最短的,請輸出它的長度。
示例 1:

輸入: [2, 6, 4, 8, 10, 9, 15]
輸出: 5
解釋: 你只需要對 [6, 4, 8, 10, 9] 進行升序排序,那麼整個表都會變為升序排序。

2.題解

2.1 暴力

public int findUnsortedSubarray(int[] nums) {
	int res = nums.length;
	for (int i = 0; i < nums.length; i++) {
		for (int j = i; j <= nums.length; j++) {
			int min = Integer.MAX_VALUE, max = Integer.MIN_VALUE, prev = Integer.MIN_VALUE;
			for (int k = i; k < j; k++) {
				min = Math.min(min, nums[k]);
				max = Math.max(max, nums[k]);
			}
			if ((i > 0 && nums[i - 1] > min) || (j < nums.length && nums[j] < max))
				continue;
			int k = 0;
			while (k < i && prev <= nums[k]) {
				prev = nums[k];
				k++;
			}
			if (k != i)
				continue;
			k = j;
			while (k < nums.length && prev <= nums[k]) {
				prev = nums[k];
				k++;
			}
			if (k == nums.length) {
				res = Math.min(res, j - i);

			}
		}
	}
	return res;
}


陣列[2, 6, 4, 8, 10, 9, 15]的最短無序連續子陣列為nums[1:6],即[6, 4, 8, 10, 9]
nums[0]nums[6]是升序的。子陣列nums[1:6]的最小值大於nums[0],最大值小於nums[6]。所以,只要對nums[1:6]這個子陣列進行升序排序,那麼整個陣列都會變為升序排序。
解釋程式碼:
通過兩層迴圈遍歷所有可能的子序列。
遍歷到子序列[2, 6, 4]時,由於4<6,所以需要對[2, 6, 4]排序。
同理,遍歷到子序列[2, 6, 4, 8, 10, 9]時,由於9<10,也需要對[2, 6, 4, 8, 10, 9]排序。

if ((i > 0 && nums[i - 1] > min) || (j < nums.length && nums[j] < max))
      continue;

下面這部分程式碼用於檢查nums[j:n−1]是否是升序的,如果nums[j:n−1]是升序的,那麼k == nums.length,就可以求子陣列nums[i:j]的長度。

int k = 0;
// ...
k = j;
while (k < nums.length && prev <= nums[k]) {
	prev = nums[k];
	k++;
}
if (k == nums.length) {
	res = Math.min(res, j - i);

}

下面這部分程式碼用於檢查nums[0:i−1]nums[j:n−1]是否是升序的。

int k = 0;
while (k < i && prev <= nums[k]) {
	prev = nums[k];
	k++;
}
// ...
k = j;
while (k < nums.length && prev <= nums[k]) {
	prev = nums[k];
	k++;
}
if (k == nums.length) {
	res = Math.min(res, j - i);

}

最後可以求得全域性最短無序連續子陣列。

2.2 更好的暴力

// [2, 6, 4, 8, 10, 9, 15]
public int findUnsortedSubarray(int[] nums) {
	int l = nums.length, r = 0;
	for (int i = 0; i < nums.length - 1; i++) {
		for (int j = i + 1; j < nums.length; j++) {
			if (nums[j] < nums[i]) {
				r = Math.max(r, j);
				l = Math.min(l, i);
			}
		}
	}
	return r - l < 0 ? 0 : r - l + 1;
}

顯然nums[0]在陣列中的正確位置上。
i = 1, j = 2時,由於nums[1]<nums[2](即6<4),所以nums[1]不在陣列中的正確位置上,更新無序子陣列的左邊界為1,右邊界為2
i = 4, j = 5時,同理,由於nums[4]<nums[5],更新無序子陣列的左邊界為1,右邊界為5
實際上,對於陣列[2, 6, 4, 8, 10],對其子陣列[6, 4]進行升序排序即可。而對於[2, 6, 4, 8, 10, 9],需要對其子陣列[6, 4, 8, 10, 9]進行升序排序。
注意到對於陣列[15, 4, 6, 8, 9, 10, 2],即使其子陣列[4, 6, 8, 9, 10]是升序排序的,也需要對整個陣列進行升序排序。

2.3 排序

// [2, 6, 4, 8, 10, 9, 15]
public int findUnsortedSubarray(int[] nums) {
	int[] snums = nums.clone();
	Arrays.sort(snums);
	int start = snums.length, end = 0;
	for (int i = 0; i < snums.length; i++) {
		if (snums[i] != nums[i]) {
			start = Math.min(start, i);
			end = Math.max(end, i);
		}
	}
	return (end - start >= 0 ? end - start + 1 : 0);
}

對原陣列進行升序排序後,對比原陣列和排序後的陣列,如果某個位置上的元素不相等,說明原陣列中該位置的元素不在正確的位置上。

2.4 使用棧

// [2, 6, 4, 8, 10, 9, 15]
public int findUnsortedSubarray(int[] nums) {
	Stack < Integer > stack = new Stack < Integer > ();
	int l = nums.length, r = 0;
	for (int i = 0; i < nums.length; i++) {
		while (!stack.isEmpty() && nums[stack.peek()] > nums[i])
			l = Math.min(l, stack.pop());
		stack.push(i);
	}
	stack.clear();
	for (int i = nums.length - 1; i >= 0; i--) {
		while (!stack.isEmpty() && nums[stack.peek()] < nums[i])
			r = Math.max(r, stack.pop());
		stack.push(i);
	}
	return r - l > 0 ? r - l + 1 : 0;
}

注意到對於陣列[6, 2, 4, 8, 10, 15, 9],需要對整個陣列進行升序排序,因為nums[0]nums[6]都不在正確的位置上。

2.5 不使用額外空間

// [2, 6, 4, 8, 10, 9, 15]
public int findUnsortedSubarray(int[] nums) {
	int min = Integer.MAX_VALUE, max = Integer.MIN_VALUE;
	boolean flag = false;
	for (int i = 1; i < nums.length; i++) {
		if (nums[i] < nums[i - 1])
			flag = true;
		if (flag)
			min = Math.min(min, nums[i]);
	}
	flag = false;
	for (int i = nums.length - 2; i >= 0; i--) {
		if (nums[i] > nums[i + 1])
			flag = true;
		if (flag)
			max = Math.max(max, nums[i]);
	}
	int l, r;
	for (l = 0; l < nums.length; l++) {
		if (min < nums[l])
			break;
	}
	for (r = nums.length - 1; r >= 0; r--) {
		if (max > nums[r])
			break;
	}
	return r - l < 0 ? 0 : r - l + 1;
}

注意到nums[0]nums[6]都在正確的位置上了,於是考慮子陣列[6, 4, 8, 10, 9]
由於6<410<9,所以min4max10
在無序子陣列中,其最小元素應該放到該陣列的第一個位置,其最大元素應該放到該陣列的最後一個位置。
因此,第一個大於4的元素的位置是無序子陣列的第一個位置,第一個小於10的元素的位置是無序子陣列的最後一個位置。

參考: