算法精講:分享一道值得分享的算法題

分享一道leetcode上的題,當然,居然不是放在刷題貼里來講,意味著分享的這道題不僅僅是教你怎么來解決,更重要的是這道題引發出來的一些解題技巧或許可以用在其他地方,下面我們來看看這道題的描述。

問題描述

給定一個未排序的整數數組,找出其中沒有出現的最小的正整數。

示例 1:輸入: [1,2,0]
輸出: 3
示例 2:輸入: [3,4,-1,1]
輸出: 2
示例 3:輸入: [7,8,9,11,12]
輸出: 1
復制代碼

說明: 你的算法的時間復雜度應為O(n),并且只能使用常數級別的空間。

解答

這道題在 leetcode 的定位是困難級別,或許你可以嘗試自己做一下,然后再來看我的解答,下面面我一步步來分析,秒殺的大佬請忽略.....

對于一道題,如果不能第一時間想到最優方案時,我覺得可以先不用那么嚴格,可以先采用暴力的方法求解出來,然后再來一步步優化。像這道題,我想,如果可以你要先用快速排序先把他們排序,然后在再來求解的話,那是相當容易的,不過 O(nlogn) 的時間復雜度太高,其實我們可以先犧牲下我們的空間復雜度,讓保證我們的時間復雜度為 O(n),之后再來慢慢優化我們的空間復雜度。

方法一:采用集合

我們知道,如果數組的長度為 n,那么我們要找的目標數一定是出于 1~n+1 之間的,我們可以先把我們數組里的所有數映射到集合里,然后我們從 1~n 開始遍歷判斷,看看哪個數是沒有在集合的,如果不存在的話,那么這個數便是我們要找的數了。如果 1~n 都存在,那我們要找的數就是 n+1 了。

不過這里需要注意的是,在把數組里面的數存進集合的時候,對于 小于 1 或者大于 n 的數,我們是不需要存進集合里的,因為他們不會對結果造成影響,這也算是一種優化吧。光說還不行,還得會寫代碼,代碼如下:

    public int firstMissingPositive(int[] nums) {Set<Integer> set = new HashSet<>();int n = nums.length;for (int i = 0; i < n; i++) {if (nums[i] >= 1 && nums[i] <= n) {set.add(nums[i]);}}for (int i = 1; i <= n; i++) {if (!set.contains(i)) {return  i;}}return n + 1;}
復制代碼

采用 bitmap

方法一的空間復雜度在最塊的情況下是 O(n),不知道大家還記不記得位算法,其實我們是可以利用位算法來繼續優化我們的空間的,如果不知道位算法的可以看我直接寫的一篇文章:

1、什么是bitmap算法。

2、自己用代碼實現bitmap算法;

通過采用位算法,我們我們把空間復雜度減少8倍,即從 O(n) -> O(n/32),但其實 O(n/32) 任然還算 O(n),不過,在實際運行的時候,它是確實能夠讓我們運行的更快的,在 Java 中,已經有自帶的支持位算法的類了,即 bitSet,如果你沒學過這個類,我相信你也是能一眼看懂的,代碼如下:

    public int firstMissingPositive2(int[] nums) {BitSet bitSet = new BitSet();int n = nums.length;for (int i = 0; i < n; i++) {if (nums[i] >= 1 && nums[i] <= n) {bitSet.set(nums[i]);}}for (int i = 1; i <= n; i++) {if (!bitSet.get(i)) {return  i;}}return n + 1;}
復制代碼

方法3:最終版本

如果這個數組是有序的,那就好辦了,但是如果我們要把它進行排序的話,又得需要 O(nlogn) 的時間復雜度,那我們有沒有啥辦法把它進行排序,然后時間復雜度又不需要那么高呢?

答是可以,剛才我們說過,對于那些小于 1 或者大于 n 的數,我們是其實是可以不理的,居然我們,我們需要處理的這些數,他們都是處于 1~n 之間的,那要你給這些處于 1~n 之間的數排序,并且重復的元素我們也是可以忽略掉的,記錄一個就可以了,那么你能不能在 O(n) 時間復雜度排序好呢?

不知道大家是否還記得我之間寫過的下標法

一些常用的算法技巧總結。

或者是否還記得計數排序?(計數排序其實就是下標法的一個應用了)

不過學過計數排序的朋友可能都知道,計數排序是需要我們開一個新的數組的,不過我們這里不需要,這道題我們可以這樣做:例如對于 nums[i],我們可以把它放到數組下標位 nums[i] - 1 的位置上,這樣子一處理的話,所有 1<=nums[i]<=n 的數,就都是是處于相對有序的位置了。注意,我指的是相對,也就是說對于 1-n 這些數而言,其他 小于 1 或者大于 n 的我們不理的。例如對于這個數組 nums[] = {4, 1, -1, 3, 7}。

讓 nums[i] 放到數組下標為 nums[i-1]的位置,并且對于那些 nums[i]<=0 或 nums > n的數,我們是可以不用理的,所以過程如下:從下標為 0 開始向右遍歷

1、把 4 放在下標為 3 的位置,為了不讓下標為 3 的數丟失,把下標為 3 的數與 4進行交換。

2、此時我們還不能向右移動來處下標為1的數,因為我們當前位置的3還不是處于有序的位置,還得繼續處理,所以把 3 與下標為 2 的數交換

3、當前位置額數為 -1,不理它,前進到下標為 1 的位置,把 1 與下標為 0的數交換

4、當前位置額數為 -1,不理它,前進到下標為 2 的位置,此時的 3 處于有序的位置,不理它繼續前進,4也是處于有序的位置,繼續前進。

5、此時的 7 > n,不理它,繼續前進。

遍歷完成,此時,那些處于 1~n的數都是處于有序的位置了,對于那些小于1或者大于n的,我們忽略它假裝沒看到就是了

這里還有個要注意的地方,就是 nums[i] 與下標為 nums[i]-1的數如果相等的話也是不需要交換的。

接下來,我們再次從下標 0 到下標 n-1 遍歷這個數組,如果遇到 nums[i] != i + 1 的數,,那么這個時候我們就找到目標數了,即 i + 1。

好吧,我覺得我講的有點啰嗦了,還一步步話題展現過程給你們看,連我自己都感覺有點啰嗦了,大佬勿噴哈。最后代碼如下:

    public int firstMissingPositive(int[] nums) {if(nums == null || nums.length < 1)return 1;int n = nums.length;for(int i = 0; i < n; i++){// 這里還有個要注意的地方,就是 nums[i] 與下標為 nums[i]-1的數如果相等的話// 也是不需要交換的。while(nums[i] >= 1 && nums[i] <= n && nums[i] != i + 1 && nums[i] != nums[nums[i]-1] ){// 和下標為 nums[i] - 1的數進行交換int tmp = nums[i];nums[i] = nums[tmp - 1];nums[tmp - 1] = tmp;}}for(int i = 0; i < n; i++){if(nums[i] != i + 1){return i + 1;}}return n + 1;}
復制代碼

這道題我覺得還是由挺多值得學習的地方的,例如它通過這道原地交換的方法,使指定范圍內的數組有序了。

還有就是這種通過數組下標來解決問題的方法也是一種常用的技巧,例如給你一副牌,讓你打亂順序,之后分發給4個人,也是可以采用這種方法的,詳情可以看這道題:什么是洗牌算法。

最后推廣下我的公眾號:苦逼的碼農:公眾號里面已經有100多篇原創文件,也分享了很多實用工具,海量視頻資源、電子書資源,關注自提。點擊掃碼關注哦。 戳我即可關注,

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

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

相關文章

正幾邊形可以實現無縫拼接?

正n邊形內角為 (n-2)*180/n &#xff0c;要保證可以無縫拼接&#xff0c;就是一個圓可以被整數個n邊形內角拼接&#xff0c;即 360k*(n-2)*180/n > 2nk(n-2)。&#xff08;摘自http://blog.csdn.net/ray58750034/article/details/1365813&#xff09; 以下代碼表明&#xff…

React 18 Beta 來了

大家好&#xff0c;我是若川。最近組織了源碼共讀活動&#xff0c;感興趣的可以加我微信 ruochuan12 參與&#xff0c;目前近3000人參與。經過「React18工作組」幾個月工作&#xff0c;11月16日v18終于從Alpha版本更新到Beta版本。本文會解釋&#xff1a;這次更新帶來的變化對開…

osg著色語言著色_探索數字著色

osg著色語言著色Learn how to colorize icons with your NounPro subscription and Adobe Illustrator.了解如何使用NounPro訂閱和Adobe Illustrator為圖標著色。 For those who want to level up their black and white Noun Project icons with a splash of color, unlockin…

upc組隊賽15 Supreme Number【打表】

Supreme Number題目鏈接 題目描述 A prime number (or a prime) is a natural number greater than 1 that cannot be formed by multiplying two smaller natural numbers. Now lets define a number N as the supreme number if and only if each number made up of an non-e…

CSS3實踐之路(一):CSS3之我觀

CSS 的英文全稱Cascading Style Sheets&#xff0c;中文意思是級聯樣式表,通過設立樣式表&#xff0c;可以統一地控制HMTL中各DOM元素的顯示屬性。級聯樣式表可以使人更能有效地控制網頁外觀。使用級聯樣式表&#xff0c;可以擴充精確指定網頁元素位置&#xff0c;外觀以及創建…

18個項目必備的JavaScript代碼片段——數組篇

大家好&#xff0c;我是若川。最近組織了源碼共讀活動&#xff0c;感興趣的可以加我微信 ruochuan12 參與&#xff0c;目前近3000人參與&#xff0c;0-5年工作經驗的都可以參與學習。1.chunk轉換二維數組將數組&#xff08;array&#xff09;拆分成多個數組&#xff0c;并將這些…

美學評價_卡美學的真正美

美學評價In collectible card games like Hearthstone, Legends of Runeterra, and Magic: The Gathering, the aesthetic of the cards is indubitably one of the greatest highlights for many, if not all players. Although the game loop is reliant on physically build…

好程序員web前端分享CSS Bug、CSS Hack和Filter學習筆記

為什么80%的碼農都做不了架構師&#xff1f;>>> CSS Bug、CSS Hack和Filter學習筆記 1)CSS Bug:CSS樣式在各瀏覽器中解析不一致的情況&#xff0c;或者說CSS樣式在瀏覽器中不能正確顯示的問題稱為CSS bug. 2)CSS Hack: CSS中&#xff0c;Hack是指一種兼容CSS在不同…

as3 淺復制 深復制

基元數據類型&#xff1a;boolean、int、uint、number、string 兩種復雜數據類型&#xff1a;array、object 當數組元素全部是基元數據類型時&#xff0c;即全部是值類型時&#xff0c;是沒有淺復制和深復制的區別。 當數組元素全部是復雜數據類型&#xff0c;即引用類型時&…

碎片化學前端,促進技術提升,我推薦這些

大家好&#xff0c;我是若川。眾所周知&#xff0c;關注公眾號可以了解學習掌握技術方向&#xff0c;學習優質好文&#xff0c;落實到自己項目中。還可以結交圈內好友&#xff0c;讓自己融入到積極上進的技術氛圍&#xff0c;促進自己的技術提升。話不多說&#xff0c;推薦這些…

ux和ui_設計更好的結帳體驗-UX / UI案例研究

ux和uiPlated Cuisine is a food ordering and delivery app for Plated Cuisine Restaurant founded and managed by Rayo Odusanya.Plated Cuisine是由Rayo Odusanya創建和管理的Plated Cuisine Restaurant的食品訂購和交付應用程序。 A short background about Rayo Rayo O…

Django中ajax發送post請求,報403錯誤CSRF驗證失敗解決辦法

今天學習Django框架&#xff0c;用ajax向后臺發送post請求&#xff0c;直接報了403錯誤&#xff0c;說CSRF驗證失敗&#xff1b;先前用模板的話都是在里面加一個 {% csrf_token %} 就直接搞定了CSRF的問題了&#xff1b;很顯然&#xff0c;用ajax發送post請求這樣就白搭了&…

如何在EXCEL中添加下拉框

篩選主要是將已有列的信息以下拉框的形式顯示出來 選中數據欄中的篩選按鈕即可生成 如果是想添加未有信息則如下圖步驟 首先&#xff0c;選擇你要出現下拉的區域&#xff0c;在數據欄中的選擇數據有效性 然后&#xff0c;下面對話框中&#xff0c;有效性條件中按如下設置即可&a…

每次新增頁面復制粘貼?100多行源碼的 element-ui 的新增組件功能教你解愁

1. 前言大家好&#xff0c;我是若川。最近組織了源碼共讀活動&#xff0c;感興趣的可以點此加我微信ruochuan12 參與&#xff0c;每周大家一起學習200行左右的源碼&#xff0c;共同進步。已進行三個月了&#xff0c;很多小伙伴表示收獲頗豐。想學源碼&#xff0c;極力推薦之前我…

原子設計_您需要了解的有關原子設計的4件事

原子設計重點 (Top highlight)Industries such as Architecture or Industrial Design have developed smart modular systems for manufacturing extremely complex objects like airplanes, ships, and skyscrapers. Inspired by this, Atomic Design was proposed as a syst…

深度學習 Caffe 初始化流程理解(數據流建立)

之前在簡書的文章&#xff0c;搬遷過來 ^-^ 本文是作者原創&#xff0c;如有理解錯誤&#xff0c;懇請大家指出&#xff0c;如需引用&#xff0c;請注明出處。 #Caffe FeatureMap數據流的建立 ##用語解釋 FeatureMap: 輸入的圖片信息或者經過多層處理后的圖片信息。weights: 只…

C#中的Clipboard與ContextMenuStrip應用舉例

今天&#xff0c;突然想起了怎樣在一個文本中實現復制、剪切與粘貼的功能&#xff0c;并給這些功能添加右鍵的快捷方式。于是&#xff0c;就用自己的VS2008寫了一個簡單的小應用&#xff0c;以熟悉C#中剪貼板與快捷菜單的使用。 首先&#xff0c;我們不難發現&#xff0c;剪貼板…

控制臺ui_設計下一代控制臺UI

控制臺ui游戲UX (GAMES UX) Yesterday’s Sony presentation showed us the final look of the PlayStation 5, as well as an impressive of next-gen games that will be released with it. What we didn’t get to see, however, is the new operating system and it’s use…

寫給前端新手看的一些模塊化知識

大家好&#xff0c;我是若川。最近組織了源碼共讀活動&#xff0c;感興趣的可以點此加我微信ruochuan12 進群參與&#xff0c;每周大家一起學習200行左右的源碼&#xff0c;共同進步。已進行三個月了&#xff0c;很多小伙伴表示收獲頗豐。一、 為什么需要模塊化以前沒有模塊化時…

重學前端學習筆記(八)--JavaScript中的原型和類

筆記說明 重學前端是程劭非&#xff08;winter&#xff09;【前手機淘寶前端負責人】在極客時間開的一個專欄&#xff0c;每天10分鐘&#xff0c;重構你的前端知識體系&#xff0c;筆者主要整理學習過程的一些要點筆記以及感悟&#xff0c;完整的可以加入winter的專欄學習【原文…