코드랜덤녹화알고리즘 훈련소 첫날 | 704. 이진탐색, 27. 요소제거

오늘 배운 기사 및 비디오 링크

704 기사 링크: 링크
704 비디오 설명 링크: 링크
27 기사 링크: 링크
27 비디오 설명 링크: 링크

704 이진 검색

제목보고 첫생각

주제가 정렬된 배열이고, 배열에 반복되는 요소가 없기 때문에 대상 값을 찾을 때 가장 먼저 떠오르는 방법은 이분법입니다.

코드 변덕을 읽은 후의 생각

이분법은 코드 임의의 생각에도 사용됩니다.

이분법을 사용하기 위한 조건

이 토픽의 전제는 배열이 순서가 있는 배열 이라는 것임 과 동시에 배열에 반복되는 요소가 없다는 점 또한 강조하고 있는데 , 이는 일단 반복되는 요소가 있으면 이진 검색으로 반환된 요소의 첨자가 방법이 고유하지 않을 수 있습니다.이것은 바이너리 방법을 사용하기 위한 전제 조건입니다. 제목 설명이 위의 조건을 충족하는 것을 볼 때 이분법을 사용할 수 있는지 여부를 고려하십시오.

이진 검색의 핵심

  1. 구간 정의
    이분법의 개념이 명확하지 않은 주된 이유는 구간의 정의가 명확하지 않고 구간의 정의가 불변이기 때문 입니다 . 이진 탐색 과정에서 불변성을 유지해야 하는데, 즉 while 탐색에서는 구간의 정의에 따라 각각의 경계 처리가 이루어져야 하는데, 이것이 바로 루프 불변성의 법칙 이다 .

    간격에 대한 두 가지 일반적인 정의가 있습니다.

    • 왼쪽 닫힘 오른쪽 닫힘 [왼쪽, 오른쪽]
    • 왼쪽 닫기 오른쪽 열기 [왼쪽, 오른쪽)
  2. 경계 조건
    이진 검색에는 많은 경계 조건이 포함되며 선택한 간격 정의에 따라 경계 조건을 설정해야 합니다.
    예:
    while(left < right)또는 while(left <= right)
    right = middle또는right = middle - 1

구현 중 발생하는 어려움

1. 첫 번째 작성 방법, 간격 정의 [왼쪽, 오른쪽] 간격을
정의한 후 경계 조건을 설정하는 방법은 무엇입니까?

  • while (left <= right) <=를 사용하려면 왼쪽 == 오른쪽이 의미가 있으므로 <=를 사용하십시오.
  • if (nums[middle] > target) right 에 middle - 1 을 할당해야 한다면, 현재 nums[middle] 은 target 이 아니어야 하기 때문에 다음에 검색할 왼쪽 구간의 끝 첨자 위치는 middle - 1 입니다.

2. 두 번째 작성 방법, 간격 정의 [왼쪽, 오른쪽)
간격을 정의한 후 경계 조건을 설정하는 방법은 무엇입니까?

  • 동안 (left < right), 여기서 <를 사용하십시오. 왜냐하면 left == right는 간격 [left, right)에서 의미가 없기 때문입니다.
  • (nums[middle] > target) 오른쪽이 중간으로 업데이트되면 현재 nums[middle]이 target과 같지 않기 때문에 왼쪽 구간으로 이동하여 검색을 계속하고 검색 구간은 왼쪽 닫힘 오른쪽 열림 간격입니다. , 오른쪽이 중간으로 업데이트됩니다. 즉, 다음 쿼리 간격은 nums[middle]을 비교하지 않습니다.

코드

첫 번째 쓰는 방법 [왼쪽, 오른쪽]

class Solution {
    
    
public:
    int search(vector<int>& nums, int target) {
    
    
        int left = 0;
        int right = nums.size() - 1; // 定义target在左闭右闭的区间里,[left, right]
        while (left <= right) {
    
     // 当left==right,区间[left, right]依然有效,所以用 <=
            int middle = left + ((right - left) / 2);// 防止溢出 等同于(left + right)/2
            if (nums[middle] > target) {
    
    
                right = middle - 1; // target 在左区间,所以[left, middle - 1]
            } else if (nums[middle] < target) {
    
    
                left = middle + 1; // target 在右区间,所以[middle + 1, right]
            } else {
    
     // nums[middle] == target
                return middle; // 数组中找到目标值,直接返回下标
            }
        }
        // 未找到目标值
        return -1;
    }
};

두 번째 쓰는 방법 [왼쪽, 오른쪽)

class Solution {
    
    
public:
    int search(vector<int>& nums, int target) {
    
    
        int left = 0;
        int right = nums.size(); // 定义target在左闭右开的区间里,即:[left, right)
        while (left < right) {
    
     // 因为left == right的时候,在[left, right)是无效的空间,所以使用 <
            int middle = left + ((right - left) >> 1);
            if (nums[middle] > target) {
    
    
                right = middle; // target 在左区间,在[left, middle)中
            } else if (nums[middle] < target) {
    
    
                left = middle + 1; // target 在右区间,在[middle + 1, right)中
            } else {
    
     // nums[middle] == target
                return middle; // 数组中找到目标值,直接返回下标
            }
        }
        // 未找到目标值
        return -1;
    }
};

27 요소 제거

제목보고 첫생각

이 항목에서 값이 val과 동일한 모든 요소를 ​​제거하고 제거된 배열의 새 길이를 반환해야 한다는 것을 알면 빠른 포인터와 느린 포인터를 통해 해당 요소를 삭제하는 이중 포인터 방법이 떠오릅니다. .

코드 변덕을 읽은 후의 생각

Code Caprice는 또한 이중 포인터 방법을 권장합니다.
이중 포인터 방식(빠른 포인터와 느린 포인터 방식): 빠른 포인터와 느린 포인터를 통해 하나의 for 루프 아래 두 개의 for 루프 작업을 완료합니다.

빠르고 느린 포인터 정의

  • 빠른 포인터: 새 배열의 요소를 찾습니다. 새 배열은 대상 요소를 포함하지 않는 배열입니다.
  • 느린 포인터: 새 배열의 첨자가 업데이트되는 위치를 가리킴

삭제 절차는 다음과 같습니다.
여기에 이미지 설명 삽입

구현 중 발생하는 어려움

이중 포인터 방식(빠른 포인터 및 느린 포인터 방식)의 솔루션은 마스터했지만 반대 이중 포인터 방식은 약간 불분명합니다.

/**
* 相向双指针方法,基于元素顺序可以改变的题目描述改变了元素相对位置,确保了移动最少元素
* 时间复杂度:O(n)
* 空间复杂度:O(1)
*/
class Solution {
    
    
public:
    int removeElement(vector<int>& nums, int val) {
    
    
        int leftIndex = 0;
        int rightIndex = nums.size() - 1;
        while (leftIndex <= rightIndex) {
    
    
            // 找左边等于val的元素
            while (leftIndex <= rightIndex && nums[leftIndex] != val){
    
    
                ++leftIndex;
            }
            // 找右边不等于val的元素
            while (leftIndex <= rightIndex && nums[rightIndex] == val) {
    
    
                -- rightIndex;
            }
            // 将右边不等于val的元素覆盖左边等于val的元素
            if (leftIndex < rightIndex) {
    
    
                nums[leftIndex++] = nums[rightIndex--];
            }
        }
        return leftIndex;   // leftIndex一定指向了最终数组末尾的下一个元素
    }
};

코드

이중 포인터 방식(빠른 포인터 및 느린 포인터 방식)

class Solution {
    
    
public:
    int removeElement(vector<int>& nums, int val) {
    
    
        int slowIndex = 0;
        for (int fastIndex = 0; fastIndex < nums.size(); fastIndex++) {
    
    
            if (val != nums[fastIndex]) {
    
    
                nums[slowIndex++] = nums[fastIndex];
            }
        }
        return slowIndex;
    }
};

오늘 수확

1. 이분법을 더 깊이 이해하기 위해서는 루프에서 검색 구간의 정의, 즉 루프 불변 규칙에 따라 경계 처리를 주장하는 것이 핵심입니다.
2. 더블 포인터 방식에서 빠른 포인터와 느린 포인터의 정의에 대한 이해가 깊어졌습니다.
오늘은 3시간씩 자주 공부합니다.

Supongo que te gusta

Origin blog.csdn.net/m0_46555669/article/details/126970834
Recomendado
Clasificación