LeetCode - 34 在排序数组中查找元素的第一个和最后一个位置

目录

题目来源

题目描述

示例

提示

题目解析

算法源码


题目来源

34. 在排序数组中查找元素的第一个和最后一个位置 - 力扣(LeetCode)

题目描述

给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。

如果数组中不存在目标值 target,返回 [-1, -1]。

你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。

示例

输入 nums = [5,7,7,8,8,10], target = 8
输出 [3,4]
说明
输入 nums = [5,7,7,8,8,10], target = 6
输出 [-1,-1]
说明
输入 nums = [], target = 0
输出 [-1,-1]
说明

提示

  • 0 <= nums.length <= 105
  • -109 <= nums[i] <= 109
  • nums 是一个非递减数组
  • -109 <= target <= 109

题目解析

本题的nums是一个非递减顺序排列的整数数组 nums,则说明nums中是存在重复数值的,因此本题要查找的元素有第一个和最后一个位置的概念。

本题要我们在O(logN)的时间复杂度内完成元素的第一个和最后一个位置的查找,这很容易联想到二分查找,因为本题nums是一个单调的,而二分的时间复杂度也是O(logN)

但是常用的二分查找,比如Java语言的Arrays.binarySearch,并不适用于找有重复值的元素的位置,比如下面代码最终返回的要查找元素1的位置是索引2。

import java.util.Arrays;

public class Main {
  public static void main(String[] args) {
    int[] arr = {1, 1, 1, 1, 1};
    System.out.println(Arrays.binarySearch(arr, 1));
  }
}

这和底层二分查找的策略有关,关于二分查找,可以看下这篇博客中关于二分查找的实现:

算法设计 - 二分法和三分法,洛谷P3382_伏城之外的博客-CSDN博客

下面给出三种语言的标准二分查找实现

Java 标准二分查找实现

import java.util.*;
 
public class Main {
  public static void main(String[] args) {
	  int[] arr = {1,1,1,1,1};
	  System.out.println(binarySearch(arr, 1));
  }
  
  public static int binarySearch(int[] arr, int target) {
    int low = 0;
    int high = arr.length - 1;

    while (low <= high) {
      int mid = (low + high) >> 1;
      int midVal = arr[mid];

      if (midVal > target) {
        high = mid - 1;
      } else if (midVal < target) {
        low = mid + 1;
      } else {
        return mid; // midVal == target,则直接返回mid作为target的位置
      }
    }

    return -low - 1; // 找不到则返回插入位置
  }
}

JavaScript标准二分查找实现

function binarySearch(arr, target) {
  let low = 0;
  let high = arr.length - 1;

  while (low <= high) {
    const mid = (low + high) >> 1;
    const midVal = arr[mid];

    if (midVal > target) {
      high = mid - 1;
    } else if (midVal < target) {
      low = mid + 1;
    } else {
      return mid; // midVal == target,则直接返回mid作为target的位置
    }
  }

  return -low - 1; // 找不到则返回插入位置
}

Python 二分查找标准实现

def binarySearch(arr, target):
    low = 0
    high = len(arr) - 1

    while low <= high:
        mid = (low + high) >> 1
        midVal = arr[mid]

        if midVal > target:
            high = mid - 1
        elif midVal < target:
            low = mid + 1
        else:
            return mid

    return -low - 1

标准的二分查找框架中,如果midVal == target时,是直接返回了此时的mid值作为了target的位置。

但是如果target有多个重复值元素的话,那么在midVal == target时,不应该武断地判定mid就是target的位置,而是应该尝试向mid位置的左右方向进行延伸判断:

向mid位置的左边进行延伸判断:

  • 如果 mid == 0 或 arr[mid] != arr[mid-1],则说明当前mid位置已经是 target数域 的左边界了,即target第一次出现的位置
  • 如果 mid > 0 且 arr[mid] == arr[mid - 1],则说明target数域的左边界还在mid位置的左边,此时为了找到左边界,我们应该让 high = mid - 1

向mid位置右边进行延伸判断:

  • 如果 mid == arr.length - 1 或者 arr[mid] != arr[mid + 1],则说明按当前mid位置已经是 target数域 的右边界了,即target最后一次出现的位置
  • 如果 mid < nums.length -1 且 arr[mid] == arr[mid+1],则说明target数域的右边界还在mid位置的右边,此时为了找到右边界,我们应该让 low = mid + 1

实现代码如下:

Java实现代码

import java.util.*;
 
public class Main {
  public static void main(String[] args) {
	  int[] arr = {1,1,1,1,1};
	  System.out.println(binarySearch(arr, 1));
  }
  
  public static int searchFirst(int[] arr, int target) {
    int low = 0;
    int high = arr.length - 1;

    while (low <= high) {
      int mid = (low + high) >> 1;
      int midVal = arr[mid];

      if (midVal > target) {
        high = mid - 1;
      } else if (midVal < target) {
        low = mid + 1;
      } else {
		// 向左延伸判断,mid是否为target数域的左边界,即第一次出现的位置
		if(mid == 0 || arr[mid] != arr[mid - 1]) {
			return mid;
		} else {
			high = mid - 1;
		}
      }
    }

    return -low - 1; // 找不到则返回插入位置
  }
  
  public static int searchLast(int[] arr, int target) {
    int low = 0;
    int high = arr.length - 1;

    while (low <= high) {
      int mid = (low + high) >> 1;
      int midVal = arr[mid];

      if (midVal > target) {
        high = mid - 1;
      } else if (midVal < target) {
        low = mid + 1;
      } else {
		// 向右延伸判断,mid是否为target数域的右边界,即最后一次出现的位置
		if(mid == nums.length - 1 || arr[mid] != arr[mid + 1]) {
			return mid;
		} else {
			low = mid + 1;
		}
      }
    }

    return -low - 1; // 找不到则返回插入位置
  }
}

JavaScript实现代码

function searchFirst(arr, target) {
  let low = 0;
  let high = arr.length - 1;

  while (low <= high) {
    const mid = (low + high) >> 1;
    const midVal = arr[mid];

    if (midVal > target) {
      high = mid - 1;
    } else if (midVal < target) {
      low = mid + 1;
    } else {
      // 向左延伸判断,mid是否为target数域的左边界,即第一次出现的位置
      if (mid == 0 || arr[mid] != arr[mid - 1]) {
        return mid;
      } else {
        high = mid - 1;
      }
    }
  }

  return -low - 1; // 找不到则返回插入位置
}

function searchLast(arr, target) {
  let low = 0;
  let high = arr.length - 1;

  while (low <= high) {
    const mid = (low + high) >> 1;
    const midVal = arr[mid];

    if (midVal > target) {
      high = mid - 1;
    } else if (midVal < target) {
      low = mid + 1;
    } else {
      // 向右延伸判断,mid是否为target数域的右边界,即最后一次出现的位置
      if (mid == arr.length - 1 || arr[mid] != arr[mid + 1]) {
        return mid;
      } else {
        low = mid + 1;
      }
    }
  }

  return -low - 1; // 找不到则返回插入位置
}

Python实现代码

def searchFirst(arr, target):
    low = 0
    high = len(arr) - 1

    while low <= high:
        mid = (low + high) >> 1
        midVal = arr[mid]

        if midVal > target:
            high = mid - 1
        elif midVal < target:
            low = mid + 1
        else:
            if mid == 0 or arr[mid] != arr[mid - 1]:
                return mid
            else:
                high = mid - 1

    return -low - 1


def searchLast(arr, target):
    low = 0
    high = len(arr) - 1

    while low <= high:
        mid = (low + high) >> 1
        midVal = arr[mid]

        if midVal > target:
            high = mid - 1
        elif midVal < target:
            low = mid + 1
        else:
            if mid == len(arr) - 1 or arr[mid] != arr[mid + 1]:
                return mid
            else:
                low = mid + 1

    return -low - 1

Java算法源码

class Solution {
    public int[] searchRange(int[] nums, int target) {
        return new int[]{search(nums, target, true), search(nums, target, false)};
    }

    public int search(int[] nums, int target, boolean isFirst) {
        int low = 0;
        int high = nums.length - 1;

        while(low <= high) {
            int mid = (low + high) >> 1;
            int midVal = nums[mid];

            if(midVal > target) {
                high = mid - 1;
            } else if(midVal < target) {
                low = mid + 1;
            } else {
                if(isFirst) {
                    // 查找元素的第一个位置
                    if(mid == 0 || nums[mid] != nums[mid-1]){
                        return mid;
                    } else {
                        high = mid - 1;
                    }
                } else {
                    // 查找元素的最后一个位置
                    if(mid == nums.length - 1 || nums[mid] != nums[mid + 1]) {
                        return mid;
                    } else {
                        low = mid + 1;
                    }
                }
                
            }
        }

        return -1;
    }
}

 

JavaScript算法源码

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var searchRange = function(nums, target) {
    function search(nums, target, isFirst) {
        let low = 0;
        let high = nums.length - 1;

        while(low <= high) {
            const mid = (low + high) >> 1;
            const midVal = nums[mid];

            if(midVal > target) {
                high = mid - 1;
            } else if(midVal < target) {
                low = mid + 1;
            } else {
                if(isFirst) {
                    if(mid == 0 || nums[mid] != nums[mid-1]) {
                        return mid;
                    } else {
                        high = mid - 1;
                    }
                } else {
                    if(mid == nums.length - 1 || nums[mid] != nums[mid + 1]) {
                        return mid;
                    } else {
                        low = mid + 1;
                    }
                }
            }
        }

        return -1;
    }

    return [search(nums, target, true), search(nums, target, false)];
};

Python算法源码

class Solution(object):
    def searchRange(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: List[int]
        """
        return self.search(nums, target, True), self.search(nums, target, False)
    
    def search(self, nums, target, isFirst):
        low = 0
        high = len(nums) - 1

        while low <= high:
            mid = (low + high) >> 1
            midVal = nums[mid]

            if midVal > target:
                high = mid - 1
            elif midVal < target:
                low = mid + 1
            else:
                if isFirst:
                    if mid == 0 or nums[mid] != nums[mid-1]:
                        return mid
                    else:
                        high = mid - 1
                else:
                    if mid == len(nums) - 1 or nums[mid] != nums[mid + 1]:
                        return mid
                    else:
                        low = mid + 1
        
        return -1

猜你喜欢

转载自blog.csdn.net/qfc_128220/article/details/130669841
今日推荐