【LeetCode Hot100 二分查找】搜索插入位置、搜索二維矩陣、搜索旋轉排序數組、尋找兩個正序數組的中位數

二分查找

    • 搜索插入位置
    • 搜索二維矩陣
    • 在排序數組中查找元素的第一個和最后一個位置
    • 尋找旋轉排序數組中的最小值
    • 搜索旋轉排序數組
    • 尋找兩個正序數組的中位數(hard)

搜索插入位置

給定一個排序數組和一個目標值,在數組中找到目標值,并返回其索引。如果目標值不存在于數組中,返回它將會被按順序插入的位置。
請必須使用時間復雜度為 O(log n) 的算法。
示例 1:
輸入: nums = [1,3,5,6], target = 5
輸出: 2
示例 2:
輸入: nums = [1,3,5,6], target = 2
輸出: 1

代碼:
閉區間解法

class Solution {public int searchInsert(int[] nums, int target) {int left = 0, right = nums.length - 1;while(left <= right) {int mid = left + (right - left) / 2;if (nums[mid] == target) {return mid;} else if (nums[mid] > target) {right = mid - 1;} else {left = mid + 1;}}return left;}
}

搜索二維矩陣

給你一個滿足下述兩條屬性的 m x n 整數矩陣:

  • 每行中的整數從左到右按非嚴格遞增順序排列。
  • 每行的第一個整數大于前一行的最后一個整數。

給你一個整數 target ,如果 target 在矩陣中,返回 true ;否則,返回 false 。
在這里插入圖片描述
思路
把該二維矩陣設想成一個一維的有序數組,那么在該一維數組的第 i i i 個位置的數可以用二維矩陣 ( m 行 n 列) 表示,即在 i / n i/n i/n 行, i % n i\%n i%n 列.

代碼
在上一題的基礎上修改代碼:

class Solution {public boolean searchMatrix(int[][] matrix, int target) {int m = matrix.length;int n = matrix[0].length;int left = 0, right = m*n-1;while(left <= right) {int mid = left + (right - left) / 2;int val = matrix[mid/n][mid%n];if (val == target) {return true;} else if(val < target) {left = mid + 1;} else {right = mid - 1;}}return false;}
}

在排序數組中查找元素的第一個和最后一個位置

給你一個按照非遞減順序排列的整數數組 nums,和一個目標值 target。請你找出給定目標值在數組中的開始位置和結束位置。
如果數組中不存在目標值 target,返回 [-1, -1]。
你必須設計并實現時間復雜度為 O(log n) 的算法解決此問題。
示例 1:
輸入:nums = [5,7,7,8,8,10], target = 8
輸出:[3,4]
示例 2:
輸入:nums = [5,7,7,8,8,10], target = 6
輸出:[-1,-1]

思路
用兩次二分查找分別找左邊界和有邊界
第一次二分法找左邊界,第二次二分法找右邊界

代碼
先初始化左邊界有邊界為 -1

class Solution {public int[] searchRange(int[] nums, int target) {int left = 0, right = nums.length - 1;int leftBoard = -1, rightBoard = -1;// 尋找左邊界while(left <= right) {int mid = left + (right - left) / 2;if (nums[mid] == target) {leftBoard = mid; right = mid - 1;  // 找到之后 在 mid 的左邊區間繼續找,直到找到最左邊的邊界} else if(nums[mid] < target) {left = mid + 1;} else {right = mid - 1;}}left = 0;right = nums.length - 1;// 尋找右邊界while(left <= right) {int mid = left + (right - left) / 2;if (nums[mid] == target) {rightBoard = mid;left = mid + 1;} else if(nums[mid] < target) {left = mid + 1;} else {right = mid - 1;}}return new int[]{leftBoard, rightBoard};}
}

尋找旋轉排序數組中的最小值

已知一個長度為 n 的數組,預先按照升序排列,經由 1 到 n 次 旋轉 后,得到輸入數組。例如,原數組 nums = [0,1,2,4,5,6,7] 在變化后可能得到:
若旋轉 4 次,則可以得到 [4,5,6,7,0,1,2]
若旋轉 7 次,則可以得到 [0,1,2,4,5,6,7]
注意,數組 [a[0], a[1], a[2], …, a[n-1]] 旋轉一次 的結果為數組 [a[n-1], a[0], a[1], a[2], …, a[n-2]] 。
給你一個元素值 互不相同 的數組 nums ,它原來是一個升序排列的數組,并按上述情形進行了多次旋轉。請你找出并返回數組中的 最小元素 。
你必須設計一個時間復雜度為 O(log n) 的算法解決此問題。
示例 1:
輸入:nums = [3,4,5,1,2]
輸出:1
解釋:原數組為 [1,2,3,4,5] ,旋轉 3 次得到輸入數組。
示例 2:
輸入:nums = [4,5,6,7,0,1,2]
輸出:0
解釋:原數組為 [0,1,2,4,5,6,7] ,旋轉 3 次得到輸入數組。

思路
設 x=nums[mid] 是現在二分取到的數。
我們需要判斷 x 和數組最小值的位置關系,誰在左邊,誰在右邊?
把 x 與最后一個數 nums[n?1] 比大小:

  • 如果 x>nums[n?1],那么可以推出以下結論:
    • nums 一定被分成左右兩個遞增段;
    • 第一段的所有元素均大于第二段的所有元素;
    • x 在第一段。
    • 最小值在第二段。
    • 所以 x 一定在最小值的左邊。
  • 如果 x≤nums[n?1],那么 x 一定在第二段。(或者 nums 就是遞增數組,此時只有一段。)
    • x 要么是最小值,要么在最小值右邊。

所以,只需要比較 x 和 nums[n?1] 的大小關系,就間接地知道了 x 和數組最小值的位置關系,從而不斷地縮小數組最小值所在位置的范圍,二分找到數組最小值。

代碼

class Solution {public int findMin(int[] nums) {int n = nums.length;int left = 0, right = n - 2;while(left <= right) {int mid = left + (right - left) / 2;if (nums[mid] > nums[n - 1]) {left = mid + 1;} else {right = mid - 1;}}return nums[left];}
}

搜索旋轉排序數組

整數數組 nums 按升序排列,數組中的值 互不相同 。
在傳遞給函數之前,nums 在預先未知的某個下標 k(0 <= k < nums.length)上進行了 旋轉,使數組變為 [nums[k], nums[k+1], …, nums[n-1], nums[0], nums[1], …, nums[k-1]](下標 從 0 開始 計數)。例如, [0,1,2,4,5,6,7] 在下標 3 處經旋轉后可能變為 [4,5,6,7,0,1,2] 。
給你 旋轉后 的數組 nums 和一個整數 target ,如果 nums 中存在這個目標值 target ,則返回它的下標,否則返回 -1 。
你必須設計一個時間復雜度為 O(log n) 的算法解決此問題。
示例 1:
輸入:nums = [4,5,6,7,0,1,2], target = 0
輸出:4
示例 2:
輸入:nums = [4,5,6,7,0,1,2], target = 3
輸出:-1

思路
使用上一題的思路,先找到該旋轉排序數組的最小值的位置,然后根據 target 和 旋轉的位置(旋轉排序數組的最后一個數)的大小進行比較,判斷是在左邊查找還是在右邊查找。

代碼

class Solution {public int search(int[] nums, int target) {int min = findMin(nums);  // 先找到最小值的下標int n = nums.length;if (target == nums[n -1]) {return n - 1;   // 相等的話直接返回} else if (target > nums[n-1]) {return searchTarget(nums, target, 0, min - 1);  // 在左邊查找} else { return searchTarget(nums, target, min, n - 2);  // 在右邊查找}}// 查找最小值下標public int findMin(int[] nums) {int n = nums.length;int left = 0, right = n - 2;while( left <= right) {int mid = left + (right - left) / 2;if (nums[mid] > nums[n - 1]) {left = mid + 1;} else {right = mid - 1;}}return left;}// 二分查找public int searchTarget(int[] nums, int target, int left, int right) {int n = nums.length;while(left <= right) {int mid = left + (right - left) / 2;if (nums[mid] == target) {return mid;} else if (nums[mid] > target) {right = mid - 1; } else {left = mid + 1;}}return -1;}
}

尋找兩個正序數組的中位數(hard)

給定兩個大小分別為 m 和 n 的正序(從小到大)數組 nums1 和 nums2。請你找出并返回這兩個正序數組的 中位數 。
算法的時間復雜度應該為 O(log (m+n)) 。
示例 1:
輸入:nums1 = [1,3], nums2 = [2]
輸出:2.00000
解釋:合并數組 = [1,2,3] ,中位數 2
示例 2:
輸入:nums1 = [1,2], nums2 = [3,4]
輸出:2.50000
解釋:合并數組 = [1,2,3,4] ,中位數 (2 + 3) / 2 = 2.5

思路
我們將通過 二分查找 來解決這個問題,具體步驟如下:

  1. 確保 nums1 是較短的數組
  • 由于我們要在較短的數組上進行二分查找,為了減少計算復雜度,我們首先確保 nums1 的長度小于或等于 nums2。如果 nums1 的長度大于 nums2,我們交換這兩個數組。
  • 這樣做的目的是保證二分查找的次數最小化,最大化效率。
  1. 分割數組
  • 我們的目標是將兩個數組分割成左右兩部分,使得合并后的兩個部分的元素個數相同,或者左邊多一個元素(如果總長度是奇數)。具體來說,假設 nums1 的長度是 n,nums2 的長度是 m,則:
    • 左邊部分的元素個數應為 (n + m + 1) / 2,這個值可以保證左邊部分最多比右邊部分多一個元素(當總長度是奇數時)。
    • 右邊部分的元素個數為 n + m - (n + m + 1) / 2。
  1. 二分查找
  • 在 nums1 上執行二分查找,假設當前查找的分割位置是 partition1,那么 nums1 的左邊部分就是 nums1[0]…nums1[partition1-1],右邊部分是 nums1[partition1]…nums1[n-1]。
  • 同樣地,nums2 的分割位置 partition2 可以通過以下公式計算:
    p a r t i t i o n 2 = ( n + m + 1 ) / 2 ? p a r t i t i o n 1 partition2= (n+m+1)/2 ?partition1 partition2=(n+m+1)/2?partition1
    這個公式確保了左邊部分的總元素個數為 (n + m + 1) / 2。
  1. 確保分割符合條件
  • 為了保證左邊部分的所有元素都小于或等于右邊部分的所有元素,我們需要檢查:
    • nums1[partition1 - 1] <= nums2[partition2](nums1 左邊的最大值小于 nums2 右邊的最小值)。
    • nums2[partition2 - 1] <= nums1[partition1](nums2 左邊的最大值小于 nums1 右邊的最小值)。
      如果這些條件成立,說明我們找到了合適的分割位置。
  1. 計算中位數
  • 如果兩個數組的總長度是奇數,中位數就是左邊部分的最大值,max(nums1[partition1 - 1], nums2[partition2 - 1])。
  • 如果兩個數組的總長度是偶數,中位數是左邊部分的最大值和右邊部分的最小值的平均值:
    m e d i a n = ( m a x ( n u m s 1 [ p a r t i t i o n 1 ? 1 ] , n u m s 2 [ p a r t i t i o n 2 ? 1 ] ) + m i n ( n u m s 1 [ p a r t i t i o n 1 ] , n u m s 2 [ p a r t i t i o n 2 ] ) ) / 2 median = (max(nums1[partition1?1],nums2[partition2?1])+min(nums1[partition1],nums2[partition2])) / 2 median=(max(nums1[partition1?1],nums2[partition2?1])+min(nums1[partition1],nums2[partition2]))/2
  1. 二分查找的調整
  • 如果不滿足條件,意味著 partition1 需要調整:
    • 如果 nums1[partition1 - 1] > nums2[partition2],則需要將 partition1 向左移,即減小 partition1。
    • 如果 nums2[partition2 - 1] > nums1[partition1],則需要將 partition1 向右移,即增大 partition1。
  1. 邊界條件
  • 對于數組的邊界,我們使用 Integer.MIN_VALUE 和 Integer.MAX_VALUE 來處理數組分割的邊界情況。例如,如果 partition1 為 0,意味著 nums1 左邊部分沒有元素,我們將 maxLeft1 設置為 Integer.MIN_VALUE;如果 partition1 為 n,意味著 nums1 右邊部分沒有元素,我們將 minRight1 設置為 Integer.MAX_VALUE。

代碼

public class Solution {public double findMedianSortedArrays(int[] nums1, int[] nums2) {// 保證 nums1 是較短的數組if (nums1.length > nums2.length) {int[] temp = nums1;nums1 = nums2;nums2 = temp;}int n = nums1.length;int m = nums2.length;// 在 nums1 上進行二分查找int left = 0, right = n;while (left <= right) {int partition1 = (left + right) / 2;int partition2 = (n + m + 1) / 2 - partition1;// 獲取 nums1 和 nums2 中的元素int maxLeft1 = (partition1 == 0) ? Integer.MIN_VALUE : nums1[partition1 - 1];int minRight1 = (partition1 == n) ? Integer.MAX_VALUE : nums1[partition1];int maxLeft2 = (partition2 == 0) ? Integer.MIN_VALUE : nums2[partition2 - 1];int minRight2 = (partition2 == m) ? Integer.MAX_VALUE : nums2[partition2];// 檢查是否找到合適的分割位置if (maxLeft1 <= minRight2 && maxLeft2 <= minRight1) {// 如果數組長度是奇數if ((n + m) % 2 == 1) {return Math.max(maxLeft1, maxLeft2);} else {// 如果數組長度是偶數return (Math.max(maxLeft1, maxLeft2) + Math.min(minRight1, minRight2)) / 2.0;}} else if (maxLeft1 > minRight2) {// 如果 maxLeft1 太大,調整左邊界right = partition1 - 1;} else {// 如果 maxLeft2 太大,調整右邊界left = partition1 + 1;}}return 0.0;}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/64829.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/64829.shtml
英文地址,請注明出處:http://en.pswp.cn/web/64829.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

24.Java 新特性擴展(重復注解、類型注解)

一、重復注解 1、基本介紹 自從 JDK 5 引入注解以來&#xff0c;注解的使用開始流行&#xff0c;在各個框架中被廣泛使用 不過注解有一個很大的限制&#xff0c;在同一個地方不能多次使用同一個注解 JDK 8 引入了重復注解的概念 2、具體實現 &#xff08;1&#xff09;自…

后端java開發路由接口并部署服務器(四)

一、安裝IntelliJ IDEA&#xff0c;安裝包下載 1、官網下載 2、網盤資源 安裝包下載完成后進行傻瓜式下一步安裝就可以了 打開IntelliJ IDEA&#xff0c;輸入網盤資源文件內容 三、漢化處理 插件搜索chinese&#xff0c;就會找到相應的插件安裝重啟軟件即可 四、新建后端j…

Vue.js 表單驗證實戰:一個簡單的登錄頁面

修改日期備注2025.1.2初版 一、前言 Vue.js 學習第一天——學會一個帶有簡單表單驗證的登錄頁面。通過這個項目&#xff0c;會對 Vue.js 的核心概念有了更深入的理解&#xff0c;加深掌握如何運用 Vue 的一些強大特性來實現動態交互和數據處理。 二、項目的基本結構 首先&a…

MySQL 鎖那些事

Q1 : MySQL有哪些鎖,功能是什么,如何項目中使用?Q2 : 行鎖是如何實現的?什么情況下會使用行鎖?Q3 : 四種事務隔離形式的行鎖有什么不一樣?讀未提交讀提交可重復讀串行 Q4 : MySQL 的讀寫都是怎樣加鎖的?Q5 : 需要注意什么? Q1 : MySQL有哪些鎖,功能是什么,如何項目中使用…

國產文本編輯器EverEdit - 批量轉碼轉換行符

1 批量轉碼&轉換行符 1.1 應用場景 如果用戶批量在Windows編輯文件&#xff0c;要上傳到異構系統&#xff0c;如&#xff1a;Linux&#xff0c;則需要批量轉換編碼和換行符&#xff0c;此時可以使用EverEdit的批量轉碼功能。 1.2 使用方法 選擇主菜單文檔 -> 批量轉碼…

Java實現下載excel模板,并實現自定義下拉框

GetMapping("excel/download")ApiOperation(value "模板下載")public void getUserRecordTemplate(HttpServletResponse response, HttpServletRequest request) throws IOException {OutputStream outputStream response.getOutputStream();InputStream…

成立一家無人機培訓機構需要哪些基礎配置

成立一家無人機培訓機構&#xff0c;需要一系列基礎配置來確保教學質量、學員安全以及機構的正常運營。以下是根據公開發布的信息整理出的關鍵基礎配置&#xff1a; 一、場地配置 1. 飛行場&#xff1a;提供一個安全、寬敞的室外飛行環境&#xff0c;面積最好大于三千平米&…

交換機性能詳解

1. 背板帶寬 只有模塊化交換機&#xff08;擁有可擴展插槽&#xff0c;可靈活改變端口數量&#xff09;才有這個概念&#xff0c;固定端換機是沒有這個概念的。并且固定端換機的背板容量和交換容量大小是相等的。 背板帶寬是交換機的總數據處理能力&#xff0c;由硬件架構設…

讀“將計算性能調高到極致的基點秘訣”的嘗試

看到一篇文章&#xff0c;說最近閱讀LAMMPS源碼&#xff0c;悟出了很多道理。在計算性能優化這塊&#xff0c;源代碼作者很多寫法我最初不以為意&#xff0c;后來發現是作者有意為之&#xff0c;就是為了把計算性能優化到極致。做計算仿真軟件&#xff0c;也特別需要注意這些吧…

Gitea代碼倉服務搭建

特點與優勢 輕量級:Gitea是一個輕量級的Git服務,提供了快速、穩定的代碼托管和協作開發環境。它資源占用低,適合在資源受限的環境中運行。易于安裝和部署:Gitea提供了簡單易用的安裝和部署方式,支持多種安裝方式,包括二進制文件、Docker容器等,并提供了詳細的文檔和配置…

leetcode hot 小偷

class Solution(object):def rob(self, nums):""":type nums: List[int]:rtype: int"""# 使用動態規劃&#xff0c;把之前的給保存起來ans[0,nums[-1]]for i in range(1,len(nums)):ans.append(max(ans[-1],ans[-2]nums[-1*i-1]))return ans[-1]…

端口被占用

端口8080被占用 哈哈哈&#xff0c;我是因為后端項目跑錯了&#xff0c;兩個項目后端名稱太像了&#xff1b; &#xff08;1&#xff09;netstat -aon | findstr 8080&#xff0c;找到占用8080端口的進程號&#xff0c;獲取對應的進程號pid&#xff1b; &#xff08;2&#…

文件本地和OSS上傳

這里寫目錄標題 前端傳出文件后端本地存儲阿里云OSS存儲上傳Demo實現上傳ConfigurationProperties 前端傳出文件 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>上傳文件</title> </head&g…

[人工智能] 結合最新技術:Transformer、CLIP與邊緣計算在提高人臉識別準確率中的應用

隨著人工智能的快速發展&#xff0c;特別是深度學習和自然語言處理領域的革命性技術&#xff0c;越來越多的前沿技術被應用于人臉識別中。Transformer架構、CLIP模型以及邊緣計算的結合&#xff0c;正成為提升人臉識別準確率和應用效能的關鍵技術路徑。特別是在多樣化場景下&am…

Python的*args和**kwargs

參考 總結&#xff1a; &#xff08;1&#xff09;*args用于在函數中處理傳遞的位置參數序列&#xff1b; &#xff08;2&#xff09;**kwargs則用于處理傳遞的關鍵字參數字典。 &#xff08;3&#xff09;示例&#xff1a; def complex_function(first, *args, **kwargs)…

Vue3 + ElementPlus動態合并數據相同的單元格(超級詳細版)

最近的新項目有個需求需要合并單元列表。ElementPlus 的 Table 提供了合并行或列的方法&#xff0c;可以參考一下https://element-plus.org/zh-CN/component/table.html 但項目中&#xff0c;后臺數據返回格式和指定合并是動態且沒有規律的&#xff0c;Element 的示例過于簡單&…

免費又開源:企業級物聯網平臺的新選擇 ThingsPanel

在開源領域&#xff0c;選擇合適的開源協議是開發者和企業能否充分利用平臺的關鍵。ThingsPanel&#xff0c;作為一個專注于物聯網的開源平臺&#xff0c;近日將協議從 AGPLv3 改為更開放的 Apache 2.0。這一改變對開發者和用戶意味著什么&#xff1f; 為什么協議要從 AGPLv3 轉…

C# 設計模式(結構型模式):代理模式

C# 設計模式&#xff08;結構型模式&#xff09;&#xff1a;代理模式 在軟件開發中&#xff0c;有時我們需要通過某種方式間接地訪問一個對象&#xff0c;這時就可以使用代理模式&#xff08;Proxy Pattern&#xff09;。代理模式通過引入一個代理對象來控制對目標對象的訪問…

關于AI面試系統2025年趨勢評估!

在快速發展的科技浪潮中&#xff0c;AI技術正以前所未有的速度滲透到各行各業。企業招聘領域&#xff0c;作為人才選拔的關鍵環節&#xff0c;也不例外地迎來了AI面試系統的廣泛應用和持續創新。2025年&#xff0c;AI面試系統不僅成為企業招聘的主流工具&#xff0c;更在智能化…

MySQL 01 02 章——數據庫概述與MySQL安裝篇

一、數據庫概述 &#xff08;1&#xff09;為什么要使用數據庫 數據庫可以實現持久化&#xff0c;什么是持久化&#xff1a;數據持久化意味著將內存中的數據保存到硬盤上加以“固化”持久化的主要作用是&#xff1a;將內存中的數據存儲在關系型數據庫中&#xff0c;當然也可以…