LeetCode習(xí)題:隊(duì)列的最大值

題目描述:請(qǐng)定義一個(gè)隊(duì)列并實(shí)現(xiàn)函數(shù) max_value 得到隊(duì)列里的最大值,要求函數(shù)max_value评凝、push_back 和 pop_front 的均攤時(shí)間復(fù)雜度都是O(1)狞膘。若隊(duì)列為空讯蒲,pop_front 和 max_value 需要返回 -1

示例:

例1:
輸入: 
["MaxQueue","push_back","push_back","max_value","pop_front","max_value"]
[[],[1],[2],[],[],[]]
輸出: [null,null,null,2,1,2]

例2:
輸入: 
["MaxQueue","pop_front","max_value"]
[[],[],[]]
輸出: [null,-1,-1]

限制:

  • 1 <= push_back,pop_front,max_value的總操作數(shù) <= 10000
  • 1 <= value <= 10^5

思考:在一個(gè)隊(duì)列中如何在O(1)時(shí)間內(nèi)找到最大值掸鹅?并且在最大值出隊(duì)后,在O(1)時(shí)間內(nèi)找到下一個(gè)的最大值单旁?

解題思路:使用一個(gè)輔助雙端隊(duì)列doubleQueue沪羔,當(dāng)一個(gè)元素value即將入隊(duì)時(shí),如果doubleQueue隊(duì)尾元素小于即將入隊(duì)的value象浑,則將小于value的元素全部出隊(duì)蔫饰,再將value入隊(duì),否則直接入隊(duì)愉豺,基于該思想篓吁,我們維護(hù)一個(gè)單調(diào)的雙端隊(duì)列來(lái)保存隊(duì)列的最大值,輔助隊(duì)列doubleQueue的隊(duì)首元素即為隊(duì)列最大值蚪拦。

Tips: 數(shù)組也支持兩端入隊(duì)和出隊(duì)杖剪,所以直接使用數(shù)組也是OK的,只是需要注意的是對(duì)數(shù)組首元素的操作時(shí)數(shù)組為了數(shù)據(jù)對(duì)齊驰贷,所以會(huì)有數(shù)據(jù)遷移的額外消耗盛嘿,雙端隊(duì)列可以通過(guò)邏輯優(yōu)化這一點(diǎn)

舉個(gè)例子,將[1,3,-1,5,7,3]放入隊(duì)列括袒,求最大值

  • 隊(duì)列中數(shù)組data保存所以入隊(duì)數(shù)據(jù)次兆,輔助雙端隊(duì)列doubleQueue保存最大值
  • 1入隊(duì),data和doubleQueue均為空锹锰,直接入隊(duì)芥炭,data = [1], doubleQueue = [1]
  • 3入隊(duì)狈邑,data = [1,3], doubleQueue中隊(duì)尾元素1小于3,需出隊(duì)蚤认,3入隊(duì),doubleQueue = [3]
  • -1入隊(duì)糕伐,data = [1,3,-1], doubleQueue中隊(duì)尾元素3大于-1砰琢,直接入隊(duì),doubleQueue = [3,-1]
  • 5入隊(duì)良瞧,data = [1,3,-1,5], doubleQueue中隊(duì)尾元素均小于5陪汽,依次從隊(duì)尾出隊(duì),doubleQueue = [5]
  • 3入隊(duì)褥蚯,data = [1,3,-1,5,7], doubleQueue中隊(duì)尾元素5小于7挚冤,需出隊(duì),7入隊(duì), doubleQueue = [7]
  • 6入隊(duì)赞庶,data = [1,3,-1,5,7,3], doubleQueue中隊(duì)尾元素7大于3训挡,直接入隊(duì),doubleQueue = [7,3]
  • 只要data中的7不出隊(duì)歧强,max_value一直是doubleQueue的隊(duì)首元素7,當(dāng)7出隊(duì)后澜薄,隊(duì)列中的最大值是3

解題開發(fā)語(yǔ)言:Swift

// 雙端隊(duì)列
struct DoubleQueue {
    private var data = [Int]()
    private var n = 0, pre = 0, tra = 0
    
    public var trail: Int? {
        return isEmpty ? nil : data[tra - 1]
    }
    
    public var prev: Int? {
        return isEmpty ? nil : data[pre]
    }
    
    public var total: Int {
        return tra - pre
    }
    
    public var isEmpty: Bool {
        return tra - pre == 0
    }
    
    init(num: Int) {
        n = num
        pre = 0
        tra = 0
        data = [Int](repeating: Int.min, count: num)
    }
    
    @discardableResult
    public mutating func enqueue(val: Int) -> Bool {
        if total == n {
            reBuild()
        }
        // 數(shù)組前方有空余空間, 需要做數(shù)據(jù)搬移
        if tra == n && pre > 0 {
            for i in pre..<tra {
                data[i-pre] = data[i]
            }
            tra -= pre
            pre = 0
        }
        data[tra] = val
        tra += 1
        return true
    }
    
    @discardableResult
    public mutating func enqueueFront(val: Int) -> Bool {
        if total == n {
            reBuild()
        }
        pre = pre > 0 ? pre - 1 : 0
        data.insert(val, at: pre)
        return true
    }
    
    @discardableResult
    public mutating func dequeue() -> Int? {
        if total == 0 {
            return nil
        }
        let val = data[pre]
        pre += 1
        return val
    }
    
    @discardableResult
    public mutating func dequeueBack() -> Int? {
        if total == 0 {
            return nil
        }
        let val = data[tra - 1]
        tra -= 1
        return val
    }

    private mutating func reBuild() {
        let count = n * 2
        var newData = [Int](repeating: Int.min, count: count)
        let range = newData.startIndex..<newData.index(newData.startIndex, offsetBy: n)
        newData.replaceSubrange(range, with: data)
        data = newData
        n = n * 2
    }
}

class MaxQueue {
    private var data = [Int]()
    private var queue: DoubleQueue!

    public var isEmpty: Bool {
        return data.count == 0
    }

    init() {
        data = [Int]()
        queue = DoubleQueue(num: 10)
    }
    
    func max_value() -> Int {
        if isEmpty {
            return -1
        }
        return queue.prev!
    }
    
    func push_back(_ value: Int) {
        while (!queue.isEmpty && value > queue.trail!) {
            queue.dequeueBack()
        }
        queue.enqueue(val: value)
        data.append(value)
    }
    
    func pop_front() -> Int {
        if isEmpty {
            return -1
        }
        let val = data.removeFirst()
        if val == queue.prev {
            queue.dequeue()
        }
        return val
    }
}

/**
 * Your MaxQueue object will be instantiated and called as such:
 * let obj = MaxQueue()
 * let ret_1: Int = obj.max_value()
 * obj.push_back(value)
 * let ret_3: Int = obj.pop_front()
 */

復(fù)雜度分析

時(shí)間復(fù)雜度:O(1), (插入,刪除摊册,求最大值)肤京,刪除操作雖然有while循環(huán),但是一次插入操作最多可能有n次出隊(duì)操作茅特,需要注意的是每一個(gè)元素僅會(huì)入隊(duì)和出隊(duì)一次忘分,所以n個(gè)元素的插入操作,對(duì)應(yīng)的出隊(duì)操作最多不超過(guò)n次白修,因此將出隊(duì)的操作均攤到每一次的插入操作上妒峦,時(shí)間復(fù)雜度為O(1)。
空間復(fù)雜度:O(n), n為隊(duì)列存儲(chǔ)數(shù)據(jù)長(zhǎng)度熬荆,運(yùn)算中需要額外的輔助雙端隊(duì)列來(lái)存儲(chǔ)最大值舟山,升序隊(duì)列需要空間為1,降序隊(duì)列需要空間為n卤恳,平均長(zhǎng)度為 n(n+1)/2n = O(n)累盗。
題目來(lái)源:LeetCode
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市突琳,隨后出現(xiàn)的幾起案子若债,更是在濱河造成了極大的恐慌,老刑警劉巖拆融,帶你破解...
    沈念sama閱讀 206,482評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蠢琳,死亡現(xiàn)場(chǎng)離奇詭異啊终,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)傲须,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門蓝牲,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人泰讽,你說(shuō)我怎么就攤上這事例衍。” “怎么了已卸?”我有些...
    開封第一講書人閱讀 152,762評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵佛玄,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我累澡,道長(zhǎng)梦抢,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,273評(píng)論 1 279
  • 正文 為了忘掉前任愧哟,我火速辦了婚禮奥吩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘翅雏。我一直安慰自己圈驼,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評(píng)論 5 373
  • 文/花漫 我一把揭開白布望几。 她就那樣靜靜地躺著绩脆,像睡著了一般。 火紅的嫁衣襯著肌膚如雪橄抹。 梳的紋絲不亂的頭發(fā)上靴迫,一...
    開封第一講書人閱讀 49,046評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音楼誓,去河邊找鬼玉锌。 笑死,一個(gè)胖子當(dāng)著我的面吹牛疟羹,可吹牛的內(nèi)容都是我干的主守。 我是一名探鬼主播,決...
    沈念sama閱讀 38,351評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼榄融,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼参淫!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起愧杯,我...
    開封第一講書人閱讀 36,988評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤涎才,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后力九,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體耍铜,經(jīng)...
    沈念sama閱讀 43,476評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡邑闺,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評(píng)論 2 324
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了棕兼。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片陡舅。...
    茶點(diǎn)故事閱讀 38,064評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖伴挚,靈堂內(nèi)的尸體忽然破棺而出蹭沛,到底是詐尸還是另有隱情,我是刑警寧澤章鲤,帶...
    沈念sama閱讀 33,712評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站咆贬,受9級(jí)特大地震影響败徊,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜掏缎,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評(píng)論 3 307
  • 文/蒙蒙 一皱蹦、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧眷蜈,春花似錦沪哺、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至忌怎,卻和暖如春籍滴,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背榴啸。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工孽惰, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人鸥印。 一個(gè)月前我還...
    沈念sama閱讀 45,511評(píng)論 2 354
  • 正文 我出身青樓勋功,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親库说。 傳聞我的和親對(duì)象是個(gè)殘疾皇子狂鞋,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評(píng)論 2 345