34 질문에 "전원 버튼"첫 번째와 마지막 위치에 정렬 된 배열의 요소를 찾기
이 질문은 순서 배열에 우리를 필요로 nums
에서 대상 요소를 찾기 위해 target
첫 번째와 마지막 위치의 출현. 타겟 구성 요소가 정렬 된 배열에 나타나지 않는 경우, 각각 시작 및 끝 위치를 고려 -1
하고 -1
.
방법 1 : 폭력을 해결하기 위해 (무력)
한 가지 방법은 해결책을 생각하기 쉽다 폭력, 우리는 배열 한 번 처음부터 끝까지 통과 할 필요는, 우리는 첫 번째 위치와 요소가 표시 대상의 마지막 위치를 찾을 수 있습니다. 문제의 의미에 따르면, 우리는 가장 일반적인 경우는 숫자가 엄격하게 적은 첫 만남보다라고 생각 target
하고 만남의 수는 동일하다 target
발생한 마지막의 수하는 것은보다 확실히 큰 target
.
- 우리가 트래버스 시작할 수있는 경우, 요소가 동일 통과 확인
target
만 발생 동일,target
현재 위치를 기록 할 때; - 그런 요소가 동일하지 여부를 확인하기 위해 탐색, 트래버스 갔다
target
, 단지 만남 같지 않은target
현재의 위치로 이전 위치를 기록 할 때.
이 알고리즘의 시간 복잡도는 , 피사체의 요구 사항을 충족하지 않습니다. 여기에서 우리는 코드를 보면.
자바 코드 :
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;
}
}
설명 :
-
먼저 두 소자 정수 배열 생성
targetRange
초기화 값을[-1, -1]
그 배열의 길이가 변수에 할당되며,len
배열의 길이가 같은지,0
시간 직접 복귀targetRange
; -
그런 다음 레이블에서
0
장소를 통과하는만큼 찾을 수있는 동등target
요소를, 그것은 것targetRange
첨자0
에 할당 된 해당 요소의i
다음 루프를 종료; -
여기서 한가지 유의할 사항은 다음과 같습니다 전체 탐색하는 과정에서, 경우에
targetRange[0]
재 할당되지 않은, 그것은 대상 요소 것을 보여준다target
순서 배열은nums
, 유해가 될 것입니다 존재하지 않습니다targetRange
에 반환; -
그렇다면 우리의 처음 나타나는 위치로부터 요소를 대상으로
targetRange[0]
검출 소자가 동일하지 않은만큼, 다음 위치를 탐색target
, 이전 위치를 기록 할 것이다targetRange[1]
에 할당i - 1
하고, 다음 루프를 종료 할 수있다. -
마지막 위치에서 대상 요소 그냥 배열을 주문한 경우 최종 판단을 할 수 있도록, 사실, 루프의 몸은 단순히 실행하는 방법입니다, 세부 사항에 또한 관심을 지불, 경우
targetRange[1]
아직 할당되지 않은, 넣어 어레이에서 마지막 위치에 할당.
방법 2 : 이진 검색
여기에서 우리는 순서 배열의 대상 요소의 시작과 끝 위치를 찾기 위해 이진 검색을 사용하는 방법을 살펴.
-
중간 위치의 범위의 요소의 값의 범위를 살펴 바이너리 검색의 기본적인 아이디어는
nums[mid]
타겟 소자target
대소 관계, 그리고 목표 값의 일부에 속하는 결정한다. -
이 질문에 대해 이분법의 일반적인 문제의 가장 큰 차이를 찾을 대상 요소가 있다는 것입니다
target
가능성이 더 질서있는 배열에 존재하는 것입니다; -
우리가 볼 수있는 요소 방법의 중간 위치에 이진 검색 값을 사용할 때
nums[mid]
대상 요소 정확하게 일치target
할 필요는 그것을 찾기 위해 계속하면, 이번에는 비교적 쉽게 검색 선형 오류에 해당하는 올바른 방법은 이진 검색을 계속하는 것입니다 .
-
물론, 대상 요소의 위치가 처음보다 엄격하게 작을 수 없습니다 나타나야
target
폭력 전에 분석 솔루션에 따라 요소의 위치, 대상 요소의 위치는 1 일보다 엄격하게 작 나타납니다target
우리 때문에, 요소의 경계 위치 이 이분법의 경계는이 아이디어를 사용하여 찾을 수 있습니다; -
대칭, 1보다 엄격하게 클 수 없습니다 마지막으로 나타나야 대상 요소의 위치
target
요소의 위치는 대상 요소 1의 마지막에 나타나는 위치를보다 확실히 큰target
우리가 이분법으로이 아이디어를 사용할 수 있도록, 요소의 경계 위치 경계를 찾을 수 있습니다.
여기에서 우리는 코드를 보면 :
자바 코드 :
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;
}
}
-
우선 여전히 어레이의 가변 길이 할당이다
len
; -
이어서 공개 판정합니다 때 배열 길이
0
때 직접 복귀[-1 , -1]
; -
다음으로, 우리는 상반기 찾아
target
처음 나타나는 위치를, 그리고 우리는 패키지 private 메소드에 넣어findFirstPosition
:- 왼쪽 경계 초기화
left = 0
및 우측 경계를right = len - 1
조사 대상 범위의 좌우의 경계를 나타내고; - 다음 지속적으로 이진 검색 대상 요소, 우리는주기 조건을 작성 계속할 수 있습니다
while (left < right)
최종 이득에 판단을 만들기 위해 요소의 요구 사항을 충족하기 위해 덜 이것보다, 그 아이디어는 비교적 흔한 표현이다 엄격 여기에 기록 된 메모되어 있습니다 사이클 시간이 종료해야left == right
설정을, 우리는 반환해야합니다 결국 고려하지 않는다left
또는 반환right
의. 그러나 이러한 접근 방식은 노트에 여러 곳이있다, 우리는 즉시 언급 할 것이다. - 그 다음 코드는 피사체의 중간 위치 계산
int mid = left + (right - left) / 2;
- 바로 분석에 따르면
nums[mid] < target
시간,mid
뿐만 아니라mid
모든 왼쪽의 요소가 확실히없는target
위치의 첫 번째 항목, 다음 라운드 따라서 요소를 우리가 검색 될 것이다[mid + 1, right]
, 따라서, 왼쪽 여백이 설정은mid + 1
; - 다음은
nums[mid] < target
그 반대,nums[mid] >= target
구간 탐색이 시간이if
되는 검색 공간의 분기 반대쪽[left, mid]
우리는 확인해야 우리가보다 엄격하게 큰 숫자가 표시되는 경우target
,target
이 번호의 왼쪽에 특정 위치 나타날 때 최초로 검출하는 단계; 우리가 정확히 동일한 번호가 표시되는 경우target
, 그 위치의 첫번째 발생이있을 수있다target
그것의 왼쪽에 첫 번째 위치에 발생하지만, 이것은 적절한 위치에, 탐색 구간의 다음 회전은 그렇게하지 않을[left, mid]
문제는 당신이 바로 그 경계를 설정할 필요가 없다right = mid
; - 이 다음 대상 요소 인 경우 루프를 종료에서
left
(또는이다right
이 시간이 자신의 값과 동일) 제목 말한다 있기 때문에, 대상 요소가 배열에 존재하지 않을 수있다, 보지 못했다, 따라서 필요가 혼자 다시 할 수 있습니다 판정이 단계 후 처리라고한다. - 경우
nums[left] == target
첨자 위치left
는 IStarget
달리 처음 나타나는 위치-1
.
- 왼쪽 경계 초기화
-
대칭, 우리는 쓰기에 보이는
target
위치의 마지막 발생에 대한 코드.-
첫째, 조회의 경우, 것이 확실
target
시간의 처음 나타나는 위치, 우리가 찾을 수 없습니다target
, 찾기에서target
당신은 특별한 판단을 할 수 있도록 마지막 위치, 우리는 확실히,이 번호를 찾을 수 없습니다 ; -
findLastPosition
구조와findFirstPosition
우리가 직접 아래로 복사, 등, 당신은 변화가 필요if
하고else
경우에 분석 이전에 따라, 로직nums[mid] > target
시간,mid
그리고mid
오른쪽에있는 모든 요소는 확실히 아니다target
나타나는 마지막, 검색의 다음 요소에 이렇게 특정에[left, mid - 1]
따라서 오른쪽 경계 설정mid - 1
; -
다음은
nums[mid] > target
반대,nums[mid] <= target
간격을 검색하는이 시간이if
되는 검색 공간,이 지점의 반대[mid, right]
, 우리는 확인해야 : 우리가 확실히 작은 것보다 숫자가 표시되는 경우target
,target
이 숫자의 오른쪽에 마지막으로 특정 위치에 나타납니다 등장; 우리가 정확히 같은 숫자가 표시되는 경우target
, 그것은 마지막 위치가있을 수 있음을 나타날 수 있습니다target
오른쪽 위치의 마지막을 표시하지만,이 위치에 남아해서는 안된다, 따라서 간격의 다음 라운드를 검색하는 것입니다[mid, right]
문제는 왼쪽 여백을 설정할 필요가 없다left = mid
; -
에 특별한주의를 지불하는이 시간은 이것이다 : 당신이보고되면
left = mid
함께right = mid - 1
이진 검색 수축 동작이 경계, 우리가이 괄호 리가 1, 즉, 몇 가지 작은 조정을, 수의 중간에 시간이 걸릴해야합니다 :int mid = left + (right - left + 1) / 2;
-
그 이유는 이것이다 :
/
기본 반올림 동작은 즉, 내림되어, 정수 부문이다(3 + 4) / 2 = 3
. 우리는 원래의 표현을 사용할 때mid
얻을 결코 실패right
하고, 경계가 줄어들고left = mid
로right = mid - 1
검색 범위는 두 가지 요소로 할 때 중간 결코 올바른 번호를 실패 이후 시간, 우리는, 그것의 다이어그램이 방법을 그립니다 이 코드는 중간, 무한 루프에 다음 코드를 고려하지 않을 것이다 왼쪽 테두리 또는 오른쪽 테두리 중 하나에 지점을 실행하면 우리는이 시간에, 그 간격 분리를 발견; -
사실 난 그냥 우리가, 즉 괄호 리가, 반올림에 행동 변화를 반올림 다음 정수 나누기 때 중간 번호를 취할 필요가 말했듯이,이 문제를 해결하려면
1
,이 반올림 동작 변경은 지난만을 필요로하지만, 시간의 나머지 두 가지 요소가 만드는,하지만 우리는 단지 전체에 전체 테이크는 문제가되지 않습니다하자; -
이 사람은 경험을 요약 한 것입니다, 또한 점차 사실, 표현의 사용에 사람들이이 과정을 찾기 어렵지 않다, 우리는 무한 루프시 절차에서 인쇄해야
left
,right
및mid
가치, 그것은이다 쉬운 문제를 확인하고 해결책을 생각합니다; -
루프를 종료 거기에 다음 요소의 주체가 여전히 존재 때
left
(또는이다right
그 값이 같은이 시간)를 참조하지 않았다. 그러나, 우리는 사실, 코드가 여기에 실행 할 수 있습니다, 그것은이지 않는다target
에서nums
어떤 존재, 우리의 이전 판결을 살펴. 따라서, 우리는 판단 할 필요가 없다nums[left]
여부 동일하지target
, 다음left
값이 필요하다target
순서대로 배열에서nums
마지막 1 개 인덱스에 등장.
-
코드를 디버깅 :
자바 코드 :
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;
}
}