查找效率滿分的算法—— “二分查找” 算法 (Java版)

本篇會加入個人的所謂魚式瘋言

??????魚式瘋言:??????此瘋言非彼瘋言

而是理解過并總結出來通俗易懂的大白話,

小編會盡可能的在每個概念后插入魚式瘋言,幫助大家理解的.

🤭🤭🤭可能說的不是那么嚴謹.但小編初心是能讓更多人能接受我們這個概念 !!!

在這里插入圖片描述

前言

在前面的章節中,我們學習了 "雙指針"算法“滑動窗口"算法

而在本篇章節 , 我們期待已久的 “二分查找”算法 即將登場, 透露下本次算法主要的規劃 💖 💖 💖

目錄

  1. 二分算法的初識

  2. 二分算法的應用

  3. 二分算法的總結

一. 二分算法的初識

1. 二分算法的簡介

二分算法,也被稱為 二分查找 算法,是一種常用的查找 算法。

它的基本思想是將已排序的數據序列分成 兩部分取中間 的元素與 目標值 進行比較,然后根據比較結果,確定目標值 在左半部分還是 右半部分,再繼續在相應的部分進行 查找,直到找到目標值或者確定目標值 不存在 為止。

2. 二分算法的使用步驟

因為二分查找算法分為兩種:

“一種是 樸素二分查找” 算法, 另外一種是 "左右邊界二分查找 " 算法

因為樸素二分查找比較基礎,我們先學習基礎版本的,再調整有難度的 💖 💖 💖

小編在這里說明 “樸素二分查找” 的具體步驟哦

先拿個題目來瞧瞧唄

二分查找

704.二分查找題目鏈接

<1>. 題目描述

在這里插入圖片描述

給定一個 n 個元素有序的(升序)整型數組 nums 和一個目標值 target ,寫一個函數搜索 nums 中的 target,如果目標值存在返回下標,否則返回 -1。

示例 1:

輸入: nums = [-1,0,3,5,9,12], target = 9
輸出: 4
解釋: 9 出現在 nums 中并且下標為 4

示例 2:

輸入: nums = [-1,0,3,5,9,12], target = 2
輸出: -1
解釋: 2 不存在 nums 中因此返回 -1

題目含義

在給定的數組中查找一個數 ,返回其 下標 ,如果沒有就返回 -1

<2>. 講解算法思想

題目分析 : 我們要查找一個數最簡單的方式

解法一

暴力求解

用一個 for -遍歷 數組 ,然后中間用一個 if 來判斷 ,一定成立就返回其下標

在解法一的基礎上,我們為了減少一半的數據,特別提煉出 二分查找 算法的思想

解法二

二分算法 :

請添加圖片描述

  • 首先,確定要查找的目標值在序列中的可能范圍,通常是通過比較目標值和序列的 第一個元素最后一個元素 來確定;

我們定義 一個數組的第一個元素的為 left , 再定義 最后一個元素 的下標為 right

  • 然后,在可能范圍內,取中間的元素與目標值進行比較;

  • 如果中間的元素等于目標值,則查找成功,返回對應的位置;

  • 如果中間的元素 大于 目標值,則說明目標值可能在 左半部分 ,縮小范圍到 左半部分 繼續查找,重復步驟2;

  • 如果中間的元素 小于 目標值,則說明目標值可能在 右半部分 ,縮小范圍到 右半部分 繼續查找,重復步驟2;

  • 如果范圍縮小到只有一個元素,但該元素不等于目標值,則查找失敗,目標值不存在。

  • 二分算法的時間復雜度為 O(log n) ,其中 n是序列的長度 。``二分算法 通常適用于 已排序的數據序列,能夠 快速查找目標值 的位置。

<3>. 編寫代碼

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

在這里插入圖片描述

魚式瘋言

樸素二分的算法體會就是

小了 整體新的區間移動到 中間值的右邊

大了 整體新的區間移動到 中間值的左邊

并且 樸素二分 必須是保證數據是有序的

 while(left <= right) {};

細節注意

我們需要這里要取等的, 當 leftright 相等 的時候, 也有可能是 目標值

二. 二分算法的應用

講解完了 樸素的二分算法, 接下來講解了 左右邊界二分查找 算法

小編在這里說一句哦,這個 左右邊界的 二分算法思想

可以這么說,是 基礎算法 細節最多最惡心 ,也是 最容易造成死循環 的一種算法

1. 尋找峰值

162.尋找峰值題目鏈接

<1>. 題目描述

在這里插入圖片描述

峰值元素是指其值嚴格大于左右相鄰值的元素。

給你一個整數數組 nums,找到峰值元素并返回其索引。數組可能包含多個峰值,在這種情況下,返回 任何一個峰值 所在位置即可。

你可以假設 nums[-1] = nums[n] = -∞ 。

你必須實現時間復雜度為 O(log n) 的算法來解決此問題。

示例 1:

輸入:nums = [1,2,3,1]
輸出:2
解釋:3 是峰值元素,你的函數應該返回其索引 2。

示例 2:

輸入:nums = [1,2,1,3,5,6,4]
輸出:1 或 5
解釋:你的函數可以返回索引 1,其峰值元素為 2;
或者返回索引 5, 其峰值元素為 6。

題目含義 :

在一個數組尋找一個既 小于左邊小于右邊 的 一個數字的 下標

<2>. 講解算法思想

其實本質上我們可以認為是尋找一個 極大值(也可以是最大值)

解法一 :

暴力遍歷 :

我們只需要遍歷數組即可查找到我們 極大值

解法二 :

二分算法

當我們需要尋找一個 最大值 的 時候, 我們可以通過 單調性 來尋找 二段性

什么是 二段性 , 就是在數組中可以區分為 兩個區域 ,我們可以把這個區域劃分為 兩段 ,而這個 兩端 我們可以進行分為

第一個區域的 右邊界 ,以及相鄰的第二個區域的 左邊界

  1. 首先我們定義 left第一個元素 下標 , right最后 一個元素下標
  1. 如果 mid遞增 區間, 就讓 left = mid
  1. 如果 mid遞減 區間, 就讓 right = mid - 1
  1. 最后就是我們需要注意的就是 終止條件 怎么設置
  1. 遞減區間 邊界的 right 剛好跳到 左邊 ,而 left 也剛剛好處于 遞增區間 的時我們就可以確定右邊界了

所以我們 只需要讓 left 等于 right

請添加圖片描述

<3>. 編寫代碼

class Solution {public int findPeakElement(int[] nums) {// 進行 左二分查找算法int left=0,right=nums.length-1;while(left < right) {int mid= left +  ( (right - left + 1) >>> 1 );if(nums[mid] > nums[mid-1]) {left = mid;} else {right = mid -1;}}return right;        }
}

在這里插入圖片描述

魚式瘋言

小編的體會

  1. 對于二分算法來說,二段性 才是 核心 , 如何尋找到在 一個區間 中劃分為 兩個 不同含義 的區間,

從而利用二分法尋找左右邊界來得到目標值

本題尋找二段性 常見的方式: 遞增性

細節處理:

 int mid= left +  ( (right - left + 1) >>> 1 );

這里我們需要用到 當我們用到 right = mid - 1 ;

自然我們就需要 在這里進行 +1 操作

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

在排序數組中查找元素中 第一個位置和最后一個位置題目鏈接

<1>. 題目描述

在這里插入圖片描述

給你一個按照非遞減順序排列的整數數組 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]
示例 3:

輸入:nums = [], target = 0
輸出:[-1,-1]

題目含義 :

尋找一段相同目標值的 第一個位置最后一個位置下標

<2>. 講解算法思想

題目分析:

用的還是二分思想,就是根據數據的性質,在某種判斷條件下將區間 一分為二 ,然后舍去其中一個
區間,然后再另一個區間內查找;方便敘述,用 target 表示該元素, left 表示 左邊界right 表示右邊界。

當我們求左邊界時, 就可以按照

尋找左邊界:

  • 我們注意到以左邊界劃分的兩個區間的特點:

  • 左邊區間 [left, left - 1] 都是小于 target 的;

  • 右邊區間(包括左邊界) =[left, right] 都是 大于等于 x 的;

  • 因此,關于 mid 的落點,我們可以分為下面兩種情況:

  • 當我們的 mid 落在 [left, left - 1] 區間的時候,也就是 arr[mid] <
    target

  • 說明 [left, mid] 都是可以舍去的,此時更新 leftmid + 1 的位置,
    繼續在 [mid + 1, right] 上尋找左邊界;

  • mid 落在 [left, right] 的區間的時候,也就是 arr[mid] >= target
    說明 [mid + 1, right] (因為 mid 可能是最終結果,不能舍去)是可以舍去的,此時
    更新 rightmid 的位置,繼續在 [left, mid] 上尋找 左邊界

請添加圖片描述

當我們需要右邊界時

按照上面我們尋找 左邊界 的思維來尋找 右邊界

我們就以 右邊界 的來劃分區域

mid 處于 <= target 時 ,right = mid

mid 處于 > target 時 , left = mid -1

請添加圖片描述

<3>. 編寫代碼

class Solution {public  int[] searchRange(int[] nums, int target) {// 定義一個存放起始和終止位置的數組int[] ret=new int[]{-1,-1};// 得到長度int len=nums.length;// 數組為 空 時 就直接返回if(len == 0) return ret;// 進行二分操作// 定義左右指針int left=0,right= len-1;/*** 尋找左邊界* 當 right = mid = left 就會陷入死循環* 細節一 : 終止條件 不能寫等號** 當 中點值  <= target 時* 細節二 : 我們更新 left = mid** 當 left  mid  right  相鄰 時*  細節三 : 更新 mid = left + ( (right - left + 1) >>> 1 )**///while(left < right) {int mid=left + ( (right-left) >>> 1);if(nums[mid] < target) {left = mid+1;}  else if(nums[mid] >= target) {right = mid ;}}if(nums[right] == target) ret[0] = right;left=0;right=len-1;/*** 尋找右邊界**  當 right = mid = left 就會陷入死循環*  細節一 : 循環終止條件 : 這里不可以進行 取等**  當 中點值 >= target  時 進行 right 移動*  細節二 : 右指針 right 移動的位置 是 到達  中點下標 mid***  left  mid  right*  當 中點 和 left  right 相鄰 時就需要*  細節三 : 確立 mid = left +(  (right - left ) >>> 1 )*/while(left < right) {int flg=left + ( (right - left + 1) >>> 1);if(nums[flg] <= target) {left = flg;}  else if(nums[flg] > target) {right = flg -1;}}if (nums[left]== target) ret[1]= left;return ret;}
}

在這里插入圖片描述

魚式瘋言

說下對于本題小編個人的體會吧

二段性 我們是找到了,但這里的是如何去 劃分我們的區域

所以然后選擇 等號 也是很關鍵的一條

當我們尋找 左邊界 時,我們就需要 讓 mid >= target 的情況進行劃分

當我們尋找 右邊界 時, 我們就需要 讓 mid < target 的情況進行劃分\

細節處理

處理 右邊界 的時

 while(left < right) {}

right = mid = left 就會陷入 死循環

細節一 :

終止條件 不能寫等號

   if(nums[flg] <= target) {left = flg; }

中點值 <= target

細節二 :

我們更新 left = mid

left mid right 相鄰 時

細節三 :

更新 mid = left + ( (right - left + 1) >>> 1 )

處理 左邊界

 while(left < right) {}

right = mid = left 就會陷入 死循環

細節一 :

終止條件 不能寫等號

   if(nums[flg] >= target) {right = flg; }

中點值 <= target

細節二 :

我們更新 right = mid

left mid right 相鄰 時

細節三 :

更新 mid = left + ( (right - left ) >>> 1 )

3. 尋找排序數組中的最小值

尋找排序數組中的最小值題目鏈接

<1>. 題目描述

在這里插入圖片描述

已知一個長度為 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 次得到輸入數組。

示例 3:

輸入:nums = [11,13,15,17]
輸出:11
解釋:原數組為 [11,13,15,17] ,旋轉 4 次得到輸入數組。

** 題目含義** :

在一個由原先 有序 進行 旋轉 后的數組中,尋找 最小值

<2>. 講解算法思想

題目分析

因為題目要求時間 復雜度只能是 O(log n)

所以這樣我們就需要用到 二分算法

那我們就必須尋找到 二段性

首先還是利用的大小關系來尋找我們的 基準值

因為是 反轉后的數據,所以從最小值到最后一個元素必然是 遞增 的,

那么這段區間肯定都是 小于等于 最后一個元素的

在這里插入圖片描述

翻轉過去的元素 必然是 大于 我們最后一個元素的,我們又劃分出了 這段區間

** 二分算法**

所以我們操作數字定義 left= 0 ,right 指向最后一個元素 , 并且取一個 基準值 target 也為最后一個元素

然后進行 mid 的判斷,以及 rightleft 的移動,

最終 leftright 相遇的位置就是我們要找的 最小值

請添加圖片描述

<2>.編寫代碼

class Solution {public int findMin(int[] nums) {// 進行二分左查找 int left= 0, right=nums.length-1;// 以 最右邊為基準值 進行 原數組的劃分int flg=nums[right];while(left < right) {// 得到中間 下標int mid= left + (  (right - left ) >>> 1 );// 如果 小于 基準   說明 是 左邊的數 , 不存在最小值if( nums[mid]  >  flg) {left = mid +1;} else {// 存在 右邊 小于或等于 基準值    right =mid;}}  return nums[left];}}

在這里插入圖片描述

三. 二分算法的總結

我們先初步認識了什么是 二分算法以及并明白了 樸素二分查找算法 的使用,

并且 樸素 是在 有序 的條件下進行

之后我們又通過 “尋找峰值”“在排序數組中查找元素中 第一個位置和最后一個位置”"尋找排序數組中的最小值" , 我們更明白 尋找 二段性 比如通過 比大小, 單調性端點值, 不等性

下面有 小彩蛋

總結樸素二分算法模板

在這里插入圖片描述

總結左右邊界二分算法模板

記住一點:

求左邊界時: right = mid

求右邊界時: left = mid

在這里插入圖片描述

如果覺得小編寫的還不錯的咱可支持 三連 下 (定有回訪哦) , 不妥當的咱請評論區 指正

希望我的文章能給各位寶子們帶來哪怕一點點的收獲就是 小編創作 的最大 動力 💖 💖 💖

在這里插入圖片描述

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

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

相關文章

removeAttribute和removeAttributeNode有什么區別(代碼舉例說明)

removeAttribute 和 removeAttributeNode 都是用于從 HTML 元素中移除屬性的 DOM 方法&#xff0c;但它們在用法和接受的參數上有一些區別。 removeAttribute removeAttribute 是一個元素&#xff08;Element&#xff09;對象的方法&#xff0c;它接受一個字符串參數&#xf…

深入了解Nginx(一):Nginx核心原理

一、Nginx核心原理 本節為大家介紹Nginx的核心原理,包含Reactor模型、Nginx的模塊化設計、Nginx的請求處理階段. &#xff08;本文源自微博客,且已獲得授權&#xff09; 1.1、Reactor模型 Nginx對高并發IO的處理使用了Reactor事件驅動模型。Reactor模型的基本組件包含時間收集…

華為OBS命令行簡單使用

華為OBS&#xff08;Object Storage Service&#xff09;是一種云存儲服務&#xff0c;提供了高可靠、高性能、安全的數據存儲能力。通過使用OBS的命令行工具obsutil&#xff0c;用戶可以方便地進行文件上傳、下載、刪除等操作&#xff0c;而無需依賴圖形界面。下面&#xff0c…

使用xsd驗證xml格式的正確性

1.1 基礎知識介紹 XML簡介&#xff1a;XML是可擴展標記語言&#xff08;eXtensible Markup Language&#xff09;的縮寫&#xff0c;它是一種數據表示格式&#xff0c;可以描述非常復雜的數據結構&#xff0c;常用于傳輸和存儲數據。xml文件、xml消息。XSD簡介&#xff1a;是X…

oracle 表同一列只取最新一條數據寫法

select * from (select t.*,row_number() over(partition by 去重列名 order by 排序列名 desc) as rnfrom 表名)where rn1 1.row_number() over(....): 為每條數據分配一個行號,1.2.3....這樣的 2.partition by : 以某列作為分組&#xff0c;每個分組行號從1開始&#xf…

ComputerLab實例2.0(繼承)

要求&#xff1a; Write a computer program that could be used to track users activities. Lab NumberComputer Station Numbers11-321-431-541-6 ? You run four computer labs. Each lab contains computer stations that are numbered as the above table. ? There…

LabVIEW和ZigBee無線溫濕度監測

LabVIEW和ZigBee無線溫濕度監測 隨著物聯網技術的迅速發展&#xff0c;溫濕度數據的遠程無線監測在農業大棚、倉庫和其他需環境控制的場所變得日益重要。開發了一種基于LabVIEW和ZigBee技術的多區域無線溫濕度監測系統。系統通過DHT11傳感器收集溫濕度數據&#xff0c;利用Zig…

uniapp-自定義navigationBar

封裝導航欄自定義組件 創建 nav-bar.vue <script setup>import {onReady} from dcloudio/uni-appimport {ref} from vue;const propsdefineProps([navBackgroundColor])const statusBarHeight ref()const navHeight ref()onReady(() > {uni.getSystemInfo({success…

圖生代碼,從Hello Onion 代碼開始

從Hello Onion 代碼開始 1&#xff0c;從代碼開始 原生語言采用java 作為載體。通過注解方式實現“UI可視化元素"與代碼bean之間的映射. 轉換示例 2&#xff0c;運行解析原理 在執行JAVA代碼期間&#xff0c;通過讀取注解信息&#xff0c;轉換為前端的JSON交由前端JS框…

NB49 牛群的秘密通信

描述 在一個遠離人類的世界中&#xff0c;有一群牛正在進行秘密通信。它們使用一種特殊的括號組合作為加密通信的形式。每一組加密信息均包括以下字符&#xff1a;(,{,[,),},]。 加密信息需要滿足以下有效性規則&#xff1a; 每個左括號必須使用相同類型的右括號閉合。左括號…

c++設計模式-->訪問者模式

#include <iostream> #include <string> #include <memory> using namespace std;class AbstractMember; // 前向聲明// 行為基類 class AbstractAction { public:virtual void maleDoing(AbstractMember* member) 0;virtual void femaleDoing(AbstractMemb…

OrangePiKunPengPro | 開發板學習與使用

OrangePi KunPengPro | 開發板學習與使用 時間:2024年5月23日20:51:12 文章目錄 `OrangePi KunPengPro` | 開發板學習與使用1.參考2.資料2.使用2-1.通過串口登錄系統2-2.通過SSH登錄系統2-3.安裝交叉編譯工具鏈2-4.復制文件到設備1.參考 1.OrangePi Kunpeng Pro Orange Pi官網…

c語言指針入門(二)

今天學習了指針的兩個常用場景&#xff0c;在此記錄&#xff0c;以便后續查看。 場景1&#xff1a;傳數組 在c語言中&#xff0c;我們在定義函數的時候是沒有辦法直接傳一個數組進去的&#xff0c;為了解決這個問題&#xff0c;我們一般將數組的名稱當作一個指針參數傳入到函數…

mysql主從復制的步驟和使用到的操作命令有哪些?

步驟&#xff1a; 配置主服務器&#xff08;Master&#xff09;&#xff1a; 啟用二進制日志記錄&#xff08;binary logging&#xff09;。配置主服務器的唯一標識&#xff08;server-id&#xff09;。創建用于復制的專用復制賬戶。 配置從服務器&#xff08;Slave&#xff0…

安裝Pnetcdf順便升級autoconf與automake

Netcdf NetCDF&#xff08;Network Common Data Form&#xff09;是一種用于存儲科學數據的文件格式和軟件庫。它是一種自描述、可移植且可擴展的數據格式&#xff0c;廣泛應用于氣象學、海洋學、地球科學和其他領域的科學研究。 NetCDF文件以二進制形式存儲&#xff0c;結構…

Qt | QGridLayout 類(網格布局)

01、上節回顧 Qt | QBoxLayout 及其子類(盒式布局)02、QGridLayout 簡介 1、網格布局原理(見下圖): 基本原理是把窗口劃分為若干個單元格,每個子部件被放置于一個或多個單元格之中,各 單元格的大小可由拉伸因子和一行或列中單元格的數量來確定,若子部件的大小(由 sizeH…

Vue從入門到實戰 Day08~Day10

智慧商城項目 1. 項目演示 目標&#xff1a;查看項目效果&#xff0c;明確功能模塊 -> 完整的電商購物流程 2. 項目收獲 目標&#xff1a;明確做完本項目&#xff0c;能夠收獲哪些內容 3. 創建項目 目標&#xff1a;基于VueCli自定義創建項目架子 4. 調整初始化目錄 目…

網絡安全之BGP詳解

BGP&#xff1b;邊界網關協議 使用范圍&#xff1b;BGP范圍&#xff0c;在AS之間使用的協議。 協議的特點&#xff08;算法&#xff09;&#xff1a;路徑矢量型&#xff0c;沒有算法。 協議是否傳遞網絡掩碼&#xff1a;傳遞網絡掩碼&#xff0c;支持VLSM&#xff0c;CIDR …

【15】編寫shell-安裝mysql

說明: 1、請注意mysql版本的壓縮包格式 2、請注意掛載data盤 3、請注意部署包和shell腳本放在同一個文件夾 4、實現shell腳本自動化部署mysql5.7.40版本 # !/bin/bash#****************************************************** # Author : 秋天楓葉35 # Last modified …

Spring Boot 中 對話 Redis

Redis 是一款開源的&#xff0c;使用 C 開發的高性能內存 Key/Value 數據庫&#xff0c;支持 String、Set、Hash、List、Stream 等等數據類型。它被廣泛用于緩存、消息隊列、實時分析、計數器和排行榜等場景。基本上是當代應用中必不可少的軟件&#xff01; Spring Boot 對 Re…