1.問題描述:
給定乙個包含 (n+1)個整數的陣列,陣列中每個元素的值在閉區間[1,n]上,證明陣列至少存在乙個重複的數字,並且找出這個重複的數字
。要求:空間複雜度o(1),時間複雜度小於o(n^2),不能修改陣列(可以認為是唯讀)。
2.問題分析:
問題產生的根本原因在於要要在n個數字上放n+1個整數,則一定會溢位(重複),這道題的意義不在題目問題,而在於題目的要求——時間複雜度小於o(n^2)這就讓我們直接摒棄了迴圈巢狀求解方法,這時候我們可能會想以空間換時間遞迴求解,然而空間複雜度o(1)的要求明確告訴我們此路不通。所以,該問題的目的是豐富我們看待問題的方式,以精簡的**從不同角度來解決問題。
3.動物領地法(自定義):
動物在宣告自己領土主權完整的時候往往會用排洩物為自己的領地標記,而其他有操守的動物要是要是發現這些標記物,就會克制住自己的衝動,去其他地方覓食或者遛彎...... 個人認為以下的這種思維方式與動物領地原則高度契合,故名動物領地法:建立數值(1)與陣列中某一位置的對應關係,對該位置的數值(2)打上「標記」,以此標記判斷數值(1)是否重複,而它的複雜度僅為o(n)!一支穿雲箭,千軍萬碼來相見:
public int findduplicate(int nums) else
}for(int i=0; i< nums.length; i++)
return duplicate;
}
寫這個方法的時候還是很自信,可是注意沒有,我最後使用了乙個迴圈對陣列進行復原,這與要求中的不能修改陣列衝突,其實看一下括號中的說明,這個方法完全可行。那麼為了在荒蕪的大腦上開疆擴土,讓我們領略一下經典演算法:floyd判圈法。
4.龜兔賽跑,平分秋色
floyd判圈法:正常情況下,龜和兔從同一點觸發,兩者的速度不同是無論如何都不會相遇的,如果是兩者相遇則必定是進入了環形場。典型應用:檢測乙個鍊錶是否有環,如果有確定環的起始節點:
public listnode detectcycle(listnode head)
}if(!iscycle)return null;
hare = head;
while(hare != tortoise)
return hare;
}
本文的重點在於如何遷移floyd圈判法解決所遇到的問題,所以對此方法就不做過多介紹,當然有興趣研究**的話有助於你更清晰的理解該演算法。
5.肖申克的救贖——鏈在心中
floyd判圈法的主要物件是鍊錶,而本文中的問題是陣列,如何結合兩者體現經典演算法中的「經典」 ?
回歸問題本身:n個位置,n+1個陣列元素,那麼必有乙個元素無法容身,此時如將元素的值作為鏈連起來,因為有重複值,所以必定會出現兩個鏈指向同乙個節點——環!救贖:重複問題變為判圈問題:
public int findduplicate(int nums)
fast = 0;
while(fast != slow)
duplicate = fast;
return duplicate;
}
寫法上可以看出陣列是反著寫的,但是當做鍊錶操作的原理是沒有區別的。陣列和鍊錶作為兩種基本的資料結構各有千秋,對於floyd法的遷移讓人深刻體會到兩者身上的優越性並非不可移植。**的腦子從不糾結於陣列和鍊錶,就如同武俠世界裡內功深厚的絕世高手從不拘泥於刀劍的使用,拈花飛葉即可傷人。
找重複元素
一道題目 乙個大小為n的整型陣列,值的範圍是1 n 1,其中有一對元素重複,問怎麼在o n 的時間複雜度下 不用o n 的空間複雜度來找出重複的那個值。這個題目最關鍵的應該是條件中的值的範圍,由於是1 n,所以用值作為下標可以遍歷從a 1 到a n 1 而元素值都是正數,那麼又可以在符號上動腦筋,當...
js陣列找重複值
判斷兩組陣列物件中是否有重複值 第一種 let list 1,2,3 1,5,4 let obj let msg list.foreach e,index else 個第 行重複了 return msg 第二種 let list this.data.msglist.reduce acc,val ac...
找陣列中重複元素
題目 乙個大小為n的陣列,裡面的數都屬於範圍 0,n 1 有不確定的重複元素,找到至少乙個重複元素,要求o 1 空間和o n 時間。分析 這個題目要求用 o n 的時間複雜度,這意味著只能遍歷陣列一次。同時還要尋找重複元素,很容易想到建立雜湊表來完成,遍歷陣列時將每個元素對映到雜湊表中,如果雜湊表中...