34 질문에 "전원 버튼"첫 번째와 마지막 위치에 정렬 된 배열의 요소를 찾기 (이진 검색)

34 질문에 "전원 버튼"첫 번째와 마지막 위치에 정렬 된 배열의 요소를 찾기

주제 주소

비디오 주소를 설명

이 질문은 순서 배열에 우리를 필요로 nums에서 대상 요소를 찾기 위해 target첫 번째와 마지막 위치의 출현. 타겟 구성 요소가 정렬 된 배열에 나타나지 않는 경우, 각각 시작 및 끝 위치를 고려 -1하고 -1.

방법 1 : 폭력을 해결하기 위해 (무력)

한 가지 방법은 해결책을 생각하기 쉽다 폭력, 우리는 배열 한 번 처음부터 끝까지 통과 할 필요는, 우리는 첫 번째 위치와 요소가 표시 대상의 마지막 위치를 찾을 수 있습니다. 문제의 의미에 따르면, 우리는 가장 일반적인 경우는 숫자가 엄격하게 적은 첫 만남보다라고 생각 target하고 만남의 수는 동일하다 target발생한 마지막의 수하는 것은보다 확실히 큰 target.

  • 우리가 트래버스 시작할 수있는 경우, 요소가 동일 통과 확인 target만 발생 동일, target현재 위치를 기록 할 때;
  • 그런 요소가 동일하지 여부를 확인하기 위해 탐색, 트래버스 갔다 target, 단지 만남 같지 않은 target현재의 위치로 이전 위치를 기록 할 때.

이 알고리즘의 시간 복잡도는 O ( N ) O (N) , 피사체의 요구 사항을 충족하지 않습니다. 여기에서 우리는 코드를 보면.

자바 코드 :

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는 IS target달리 처음 나타나는 위치 -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 = midright = mid - 1검색 범위는 두 가지 요소로 할 때 중간 결코 올바른 번호를 실패 이후 시간, 우리는, 그것의 다이어그램이 방법을 그립니다 이 코드는 중간, 무한 루프에 다음 코드를 고려하지 않을 것이다 왼쪽 테두리 또는 오른쪽 테두리 중 하나에 지점을 실행하면 우리는이 시간에, 그 간격 분리를 발견;

    • 사실 난 그냥 우리가, 즉 괄호 리가, 반올림에 행동 변화를 반올림 다음 정수 나누기 때 중간 번호를 취할 필요가 말했듯이,이 문제를 해결하려면 1,이 반올림 동작 변경은 지난만을 필요로하지만, 시간의 나머지 두 가지 요소가 만드는,하지만 우리는 단지 전체에 전체 테이크는 문제가되지 않습니다하자;

    • 이 사람은 경험을 요약 한 것입니다, 또한 점차 사실, 표현의 사용에 사람들이이 과정을 찾기 어렵지 않다, 우리는 무한 루프시 절차에서 인쇄해야 left, rightmid가치, 그것은이다 쉬운 문제를 확인하고 해결책을 생각합니다;

    • 루프를 종료 거기에 다음 요소의 주체가 여전히 존재 때 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;
    }
}
发布了442 篇原创文章 · 获赞 330 · 访问量 123万+

추천

출처blog.csdn.net/lw_power/article/details/104066739