"Power button" on 34 questions: Find the elements in the sorted array in the first and last position
This question requires us to an ordered array nums
to find the target element in the target
emergence of the first and last position. If the target element does not appear in an ordered array, considered the start and end positions, respectively -1
and -1
.
Method One: violence to solve (Brute Force)
One way is easy to think of the solution is violence, we only need to traverse from beginning to end once the array, we can find the first position and the last position of the target elements appear. According to the meaning of problems, we think that the most general case, the number is strictly less than the first encounter target
, then the number of encounters are equal to target
the number of the last encountered are strictly greater than target
.
- So if we can traverse start, check to traverse the elements equal
target
, just equal encounteredtarget
when recording the current location; - Then went traversal, traverse to check whether the elements are not equal
target
, not equal to just encountertarget
when recording previous location to the current location.
The time complexity of this algorithm is , does not meet the requirements of the subject. Here we look at the code.
Java code:
public class Solution {
public int[] searchRange(int[] nums, int target) {
int[] targetRange = new int[]{-1, -1};
int len = nums.length;
if (len == 0) {
return targetRange;
}
for (int i = 0; i < len; i++) {
if (nums[i] == target) {
targetRange[0] = i;
break;
}
}
// 连第 1 个位置都没有找到,说明已经遍历完整个数组了
if (targetRange[0] == -1) {
return targetRange;
}
for (int i = targetRange[0] + 1; i < len; i++) {
if (nums[i] != target) {
targetRange[1] = i - 1;
break;
}
}
if (targetRange[1] == -1) {
targetRange[1] = len - 1;
}
return targetRange;
}
}
explain:
-
First create a two-element integer array
targetRange
, the initialization value[-1, -1]
, then the length of the array is assigned to a variablelen
, when the length of the array is equal to0
time, direct returntargetRange
; -
Then from the labeled
0
traversing the place, as long as the equivalent to findtarget
the elements, it will betargetRange
the subscript0
of that element assigned toi
, and then exit the loop; -
One thing to note here is: if in the course of the entire traversal,
targetRange[0]
have not been re-assigned, it shows that the target elementtarget
in an ordered arraynums
does not exist, the remains will betargetRange
returned to; -
Then we target element from the position of the first occurrence of
targetRange[0]
traversing the next position, as long as the detected elements are not equaltarget
, recording its previous location and willtargetRange[1]
assigned toi - 1
, and then you can exit the loop. -
Also pay attention to detail that, if the target element in the last position just ordered array, in fact, the body of the loop is simply no way to be executed, so you can make a final judgment, if
targetRange[1]
not yet been assigned, put it assigned to the last position in the array.
Method Two: binary search
Here we look at how to use binary search to find the start and end positions of the target element in an ordered array.
-
The basic idea of binary search is: a look at the range of values of the elements in the range of intermediate positions of
nums[mid]
the target elementtarget
size relationship, and then decide which part of the target value falls in. -
For this question, find the biggest difference with the common problem of dichotomy is that the target element
target
is likely to exist in a more orderly array; -
And when we use binary search value in an intermediate position of the element method to see
nums[mid]
exactly equal to the target elementtarget
when the need to continue to find it, but this time is relatively easy to fall into the error linear search, the correct approach is to continue binary search .
-
Obviously, the position of the target element must appear the first time can not be strictly less than
target
the position of the element, according to the analysis solution before the violence, the position of the target element appears 1st is strictly less thantarget
the boundary position of the element, so we this dichotomy boundary can be found by using this idea; -
Symmetrically, the position of the target element must appear last 1 can not be strictly greater than
target
the position of elements, the position of the last occurrence of the target element 1 is strictly greater thantarget
the boundary position of the element, so we can use this idea by dichotomy find the boundary.
Here we look at the code:
Java code:
public class Solution {
public int[] searchRange(int[] nums, int target) {
int len = nums.length;
if (len == 0) {
return new int[]{-1, -1};
}
int firstPosition = findFirstPosition(nums, target);
if (firstPosition == -1) {
return new int[]{-1, -1};
}
int lastPosition = findLastPosition(nums, target);
return new int[]{firstPosition, lastPosition};
}
private int findFirstPosition(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while (left < right) {
int mid = (left + right) >>> 1;
// 小于一定不是解
if (nums[mid] < target) {
// 下一轮搜索区间是 [mid + 1, right]
left = mid + 1;
} else {
right = mid;
}
}
if (nums[left] == target) {
return left;
}
return -1;
}
private int findLastPosition(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while (left < right) {
int mid = (left + right + 1) >>> 1;
// 大于一定不是解
if (nums[mid] > target) {
// 下一轮搜索区间是 [left, mid - 1]
right = mid - 1;
} else {
left = mid;
}
}
return left;
}
}
-
First is still assign to a variable length of the array
len
; -
Then make a judgment Laid: when the length of the array
0
when the direct return[-1 , -1]
; -
Next, we find the first half of
target
the position of the first occurrence, and we put it into a package private methodfindFirstPosition
:- Initializes the left border
left = 0
and right borderright = len - 1
, represents the left and right borders of the range to be searched; - Next constantly binary search target element, we can continue the cycle conditions written
while (left < right)
note written here is strictly less than this is a relatively common wording, its idea is to meet the requirements of the elements to make a judgment on the final benefits are there must exit the cycle timeleft == right
set up, we do not consider in the end should returnleft
or returnright
of. However, such an approach has several places to note, we will immediately mention. - Then the code is calculated at an intermediate position of the subject
int mid = left + (right - left) / 2;
- According to the analysis just when
nums[mid] < target
the time,mid
as well asmid
all the elements on the left is definitely nottarget
the first occurrence of a position, and therefore the elements of the next round we will be in search[mid + 1, right]
, and therefore, the left margin is setmid + 1
; - Next is
nums[mid] < target
the opposite,nums[mid] >= target
this time searching interval isif
the opposite of this branch of the search space, that is[left, mid]
, we have to verify: If we see a number strictly greater thantarget
,target
the first occurrence of a certain position appears to the left of this number; If we see a number exactly equaltarget
, it is possible that the first occurrence of a position, there maytarget
occur the first one position to the left of it, but this will not in the right position, so that the next round of the search interval is[left, mid]
There is no problem, then you need to set the right boundaryright = mid
; - In exiting the loop when there is a next target element
left
(or isright
this time equal to their value) did not see, because the title says, the target element there may not exist in the array, therefore, need to do it again alone judgment, this step is called post-processing. - If
nums[left] == target
the subscript positionleft
is thetarget
position of the first occurrence of otherwise-1
.
- Initializes the left border
-
Symmetrically, we look to write
target
code for the last occurrence of the position.-
First, it is certain that, if in the lookup
target
position of the first occurrence of the time, we did not findtarget
, in the Findtarget
position of last occurrence, we certainly will not find this number, so you can do a special judge ; -
findLastPosition
The structure and thefindFirstPosition
like, we directly copy down, you need to change thatif
andelse
logic, based on an analysis earlier, whennums[mid] > target
the time,mid
andmid
all the elements on the right is definitely nottarget
the last one where they appear, so the next element of a search on in certain[left, mid - 1]
, therefore, the right boundary setmid - 1
; -
Next is
nums[mid] > target
the opposite,nums[mid] <= target
this time searching interval isif
the opposite of this branch of the search space, that is[mid, right]
, we have to verify: If we see a number strictly less thantarget
,target
appeared last a certain position appears to the right of this number; If we see a number exactly equaltarget
, it may appear that the last one position, there maytarget
appear the last one in its right position, but must not be left in this position, therefore, is to search for the next round of the interval[mid, right]
There is no problem, then you need to set the left marginleft = mid
; -
This time to pay special attention to is this: Once you see
left = mid
withright = mid - 1
this boundary in a binary search shrinkage behavior, we need to take time in the middle of the number, make some small adjustments, that is, in this parenthesis Riga 1:int mid = left + (right - left + 1) / 2;
-
The reason is this:
/
is an integer division, that the default rounding behavior is rounded down, ie(3 + 4) / 2 = 3
. When we use the original wording,mid
never fail to getright
, and the boundary is shrinkingleft = mid
withright = mid - 1
time, we draw a diagram of it this way, when the search range to be only two elements, since the intermediate never fail to get the right number we found that interval inseparable, at this time, once this code executes a branch to either the left border or right border will not consider the middle, then the code into an infinite loop; -
To solve this problem, in fact I have just said, we need to take the middle number when the next integer division rounding behavior change on rounding, namely in parentheses Riga
1
, although this rounding behavior changes require only last only the remaining two elements of time to make, but we just let the whole take on the whole is not the problem; -
This one is a summary of the experience, but also people in the use of gradually summed up, in fact, this process is not difficult to find, we only need to print out in the proceedings at the time of infinite loop
left
,right
andmid
values, it is easy to identify problems and think of a solution; -
In exiting the loop when there is there is still a subject of the next element
left
(or isright
this time their values are equal) did not see. However, we note that, in fact, the code can be executed here, it is not that,target
innums
a certain presence, take a look at our previous judgment. Therefore, we have no need to judgenums[left]
whether or not equaltarget
, thenleft
the value is necessarilytarget
in an orderly arraynums
appeared in the last 1 index.
-
Debugging code:
Java code:
class Solution {
public int[] searchRange(int[] nums, int target) {
int len = nums.length;
if (len == 0) {
return new int[]{-1, -1};
}
int firstPosition = findFirstPosition(nums, target, len);
if (firstPosition == -1) {
return new int[]{-1, -1};
}
int lastPosition = findLastPosition(nums, target, len);
return new int[]{firstPosition, lastPosition};
}
private int findLastPosition(int[] nums, int target, int len) {
int left = 0;
int right = len - 1;
while (left < right) {
int mid = left + (right - left) / 2;
System.out.println("left = " + left + ", mid = " + mid + ", right = " + right);
// 严格大于 target 的时候不是解
if (nums[mid] > target) {
// 下一轮搜索的区间是 [left, mid - 1]
System.out.println("下一轮搜索的区间是 [left, mid - 1]");
right = mid - 1;
} else {
System.out.println("下一轮搜索的区间是 [mid, right]");
// [mid, right]
left = mid;
}
}
return left;
}
private int findFirstPosition(int[] nums, int target, int len) {
int left = 0;
int right = len - 1;
while (left < right) {
int mid = left + (right - left) / 2;
// 严格小于 target 的时候不是解
if (nums[mid] < target) {
// 下一轮搜索的区间是 [mid + 1, right]
left = mid + 1;
} else {
right = mid;
}
}
if (nums[left] == target) {
return left;
}
return -1;
}
}