C語言之單調(diào)棧

單調(diào)棧

What

單調(diào)棧也是棧的一種窄绒,滿足先進后出的原則,另外,單調(diào)棧中的元素是有序的商乎,分為兩種

  • 單調(diào)遞增棧:單調(diào)遞增棧就是從棧底到棧頂數(shù)據(jù)是從大到小

  • 單調(diào)遞減棧:單調(diào)遞減棧就是從棧底到棧頂數(shù)據(jù)是從小到大

現(xiàn)在有一組數(shù)10距境,3申尼,7,4垫桂,12师幕。從左到右依次入棧,則如果棧為空或入棧元素值小于棧頂元素值诬滩,則入棧霹粥;否則,如果入棧則會破壞棧的單調(diào)性疼鸟,則需要把比入棧元素小的元素全部出棧后控。單調(diào)遞減的棧反之。

  • 10入棧時空镜,棧為空浩淘,直接入棧捌朴,棧內(nèi)元素為10。

  • 3入棧時张抄,棧頂元素10比3大砂蔽,則入棧,棧內(nèi)元素為10署惯,3察皇。

  • 7入棧時,棧頂元素3比7小泽台,則棧頂元素出棧什荣,此時棧頂元素為10,比7大怀酷,則7入棧稻爬,棧內(nèi)元素為10,7蜕依。

  • 4入棧時桅锄,棧頂元素7比4大,則入棧样眠,棧內(nèi)元素為10友瘤,7,4檐束。

  • 12入棧時辫秧,棧頂元素4比12小,4出棧被丧,此時棧頂元素為7盟戏,仍比12小,棧頂元素7繼續(xù)出棧甥桂,此時棧頂元素為10柿究,仍比12小,10出棧黄选,此時棧為空蝇摸,12入棧,棧內(nèi)元素為12办陷。

Why

單調(diào)棧在解決一些問題時貌夕,比暴力循環(huán)方法要簡單的多,比如遇到懂诗,求一個數(shù)組在滿足元素比較條件下的統(tǒng)計數(shù)據(jù)時蜂嗽,可以考慮使用單調(diào)棧的做法苗膝,典型的就是后面第一次出現(xiàn)大于或者小于當(dāng)前元素殃恒,要計算什么什么的時候植旧。

How

適用單調(diào)棧的幾個條件:

  • 求一個數(shù)組在滿足元素比較條件下的統(tǒng)計數(shù)據(jù),比如求和离唐,求面積等

  • 由比較條件推出的單調(diào)棧的類型病附,然后棧中新添加的元素不符合時,可以對當(dāng)前及其前向元素進行單獨統(tǒng)計后出棧亥鬓,且不影響后續(xù)元素統(tǒng)計完沪。

偽代碼如下:

// 此處一般需要給數(shù)組最后添加結(jié)束標(biāo)志符,來滿足全部出棧
for (遍歷這個數(shù)組)
{
    if (椙陡辏空 || 棧頂元素大于等于當(dāng)前比較元素)
    {
        入棧;
    } else {
        while (棧不為空 && 棧頂元素小于當(dāng)前元素) {
            棧頂元素出棧;
            更新結(jié)果;
        }
        當(dāng)前數(shù)據(jù)入棧;
    }
}
// 如果沒有結(jié)束標(biāo)志符覆积,此處要一般要單獨判斷全部出棧

Example

視野總和

先上題目和代碼

/*
描敘:有n個人站隊,所有的人全部向右看熟呛,個子高的可以看到個子低的發(fā)型宽档,給出每個人的身高,問所有人能看到其他人發(fā)現(xiàn)總和是多少庵朝。
輸入:4 3 7 1
輸出:2
解釋:個子為4的可以看到個子為3的發(fā)型吗冤,個子為7可以看到個子為1的身高,所以1+1=2
*/
#define STACK_NUM_MAX 500

int VisionSumGet(int *arr, int arrSize)
{
    int stack[STACK_NUM_MAX] = {0}; /* 記錄元素位置的單調(diào)棧 */
    int top = -1; /* 此單調(diào)棧棧頂?shù)乃饕? 初始位為-1, 棧為空*/
    int sum = 0;

    for (int i = 0; i < arrSize; i++) { /* 遍歷數(shù)組 */
        while ((top >= 0) && (arr[i] >= arr[stack[top]])) { /* 棧不為空, 且遇到數(shù)組元素相等或者大于棧頂對應(yīng)的數(shù)組元素, 則看不到了九府,進行統(tǒng)計 */
            sum += (i - stack[top] - 1); /* 統(tǒng)計棧頂對應(yīng)的數(shù)組元素看到的人數(shù)椎瘟,并累加*/
            top--; /* 棧頂元素出,判斷下一個棧頂元素是否該統(tǒng)計累加 */
        }
        
        top++; /* 把當(dāng)前的數(shù)組元素索引入棧 */
        stack[top] = i;
    }

    /* 最后還要進行一次計算 */
    while (top >= 0) {
        sum += (arrSize - stack[top] - 1);
        top--;
    }

    return sum;
}

首先侄旬,套一下單調(diào)棧的適用條件

  • 這是一個數(shù)組肺蔚,數(shù)組里的元素均向自己看,能看到的是比自己小的儡羔,不能看到的是比自己大的或者相等的婆排,而且是要求每個元素能看到的所有元素的個數(shù),顯然滿足第一個條件笔链。
  • 一個元素右邊能看到的是比自己小的段只,如果右邊有個比自己大的或者相等的,那么是不是可以統(tǒng)計此元素的情況鉴扫,統(tǒng)計后赞枕,把此元素排除,是不是不會影響其右邊元素坪创。因為統(tǒng)計數(shù)據(jù)與該元素的位置相關(guān)炕婶,如果我們記錄元素位置,顯示是符合第二個條件的莱预。

其他可以對照代碼注釋進行理解柠掂。

柱狀圖中的最大矩形

描述:

給定 n 個非負整數(shù),用來表示柱狀圖中各個柱子的高度依沮。每個柱子彼此相鄰涯贞,且寬度為 1 枪狂。

求在該柱狀圖中,能夠勾勒出來的矩形的最大面積宋渔。

解釋:


Snipaste_2022-09-04_16-25-50.png

Snipaste_2022-09-04_16-26-03.png

同理州疾,首先,套一下單調(diào)棧的適用條件

  • 這是一個數(shù)組皇拣,這里的條件比較隱晦严蓖,可以這樣理解,數(shù)組里的元素遇到比自己大的或者相等的氧急,可以向右勾勒颗胡,但是遇到比自己小的,不能向右勾勒吩坝,然后求每個元素的勾勒面積的最大值杭措,是滿足第一個條件的。
  • 如果遇到比自己小的钾恢,不能向右勾勒手素,可以統(tǒng)計此元素的勾勒面積嗎,那就要看左邊了瘩蚪,左邊也都是有序且小于等于此元素的泉懦,寬度只與元素位置有關(guān),高度只與此元素值有關(guān)疹瘦,可以統(tǒng)計崩哩。統(tǒng)計后排除此元素,會對后面元素的統(tǒng)計有影響嗎言沐,這里要注意邓嘹,如果排除對后面元素是可能有影響的,因為后面元素的統(tǒng)計寬度與此元素的位置可能有關(guān)险胰,比如2 1 5 6 2 3汹押,2 > 1, 統(tǒng)計2然后排除,2的位置將丟失起便,在統(tǒng)計1的時候棚贾,1是可以勾勒2的位置的。那怎么辦榆综,只需要保留2的位置就行了妙痹,試想如果2和1之間有多個2,我們應(yīng)該保留哪一個鼻疮?我們應(yīng)該保留距離1最左邊的那個2的位置就行了怯伊,這樣計算1的寬度才是正確的,為了保證有序性判沟,我們把2統(tǒng)計完成后耿芹,我們把1左邊的2全部排出崭篡,然后保留一個最左邊位置的且把它的值修改成1,當(dāng)前的1也不需要進行入棧了猩系,因為它的面積肯迪東比修改成1的那個面積統(tǒng)計的要小。

結(jié)合代碼中燥,可以好好理解一下:

/*
柱狀圖中最大的矩形
給定 n 個非負整數(shù)寇甸,用來表示柱狀圖中各個柱子的高度。每個柱子彼此相鄰疗涉,且寬度為 1 拿霉。
求在該柱狀圖中,能夠勾勒出來的矩形的最大面積咱扣。
*/

#define STACK_NUM_MAX 100000

int largestRectangleArea(int *heights, int heightsSize)
{
    int stack[STACK_NUM_MAX] = {0}; /* 記錄元素位置的單調(diào)棧 */
    int top = -1; /* 此單調(diào)棧棧頂?shù)乃饕? 初始位為-1, 棧為空*/
    int max = 0;
    int area = 0;

    for (int i = 0; i < heightsSize; i++) { /* 遍歷數(shù)組 */
        /* 棧為空, 或者遇到的元素大于等于棧頂對應(yīng)的元素, 還可以繼續(xù)向右勾勒*/
        if ((top < 0) || (heights[i] >= heights[stack[top]])) {
            top++;
            stack[top] = i;
            continue;
        }

        /* 棧不為空, 且遇到的元素小棧頂對應(yīng)的元素, 統(tǒng)計棧頂對應(yīng)的元素的面積*/
        while ((top >= 0) && (heights[i] < heights[stack[top]])) {
            area = heights[stack[top]] * (i - stack[top]);
            if (area > max) {
                max = area;
            }
            top--; /* 統(tǒng)計棧頂后出棧 */
        }
        /* 保留最后一次的棧頂, 并修改對應(yīng)的元素的值為當(dāng)前遇到的元素值, 當(dāng)前的元素值也不必入棧, 因為它統(tǒng)計的面積必然小一些*/
        top++;
        heights[stack[top]] = heights[i];
    }

    /* 最后還要進行一次計算 */
    while (top >= 0) {
        area = heights[stack[top]] * (heightsSize - stack[top]);
        if (area > max) {
            max = area;
        }
        top--;
    }

    return max;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末绽淘,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子闹伪,更是在濱河造成了極大的恐慌沪铭,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,639評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件偏瓤,死亡現(xiàn)場離奇詭異杀怠,居然都是意外死亡,警方通過查閱死者的電腦和手機厅克,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評論 3 385
  • 文/潘曉璐 我一進店門赔退,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人证舟,你說我怎么就攤上這事硕旗。” “怎么了女责?”我有些...
    開封第一講書人閱讀 157,221評論 0 348
  • 文/不壞的土叔 我叫張陵漆枚,是天一觀的道長。 經(jīng)常有香客問我抵知,道長浪读,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,474評論 1 283
  • 正文 為了忘掉前任辛藻,我火速辦了婚禮碘橘,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘吱肌。我一直安慰自己痘拆,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,570評論 6 386
  • 文/花漫 我一把揭開白布氮墨。 她就那樣靜靜地躺著纺蛆,像睡著了一般吐葵。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上桥氏,一...
    開封第一講書人閱讀 49,816評論 1 290
  • 那天温峭,我揣著相機與錄音,去河邊找鬼字支。 笑死凤藏,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的堕伪。 我是一名探鬼主播揖庄,決...
    沈念sama閱讀 38,957評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼欠雌!你這毒婦竟也來了蹄梢?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,718評論 0 266
  • 序言:老撾萬榮一對情侶失蹤富俄,失蹤者是張志新(化名)和其女友劉穎禁炒,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體霍比,經(jīng)...
    沈念sama閱讀 44,176評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡齐苛,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,511評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了桂塞。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片凹蜂。...
    茶點故事閱讀 38,646評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖阁危,靈堂內(nèi)的尸體忽然破棺而出玛痊,到底是詐尸還是另有隱情,我是刑警寧澤狂打,帶...
    沈念sama閱讀 34,322評論 4 330
  • 正文 年R本政府宣布擂煞,位于F島的核電站,受9級特大地震影響趴乡,放射性物質(zhì)發(fā)生泄漏对省。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,934評論 3 313
  • 文/蒙蒙 一晾捏、第九天 我趴在偏房一處隱蔽的房頂上張望蒿涎。 院中可真熱鬧,春花似錦惦辛、人聲如沸劳秋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽玻淑。三九已至嗽冒,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間补履,已是汗流浹背添坊。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留箫锤,地道東北人贬蛙。 一個月前我還...
    沈念sama閱讀 46,358評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像麻汰,于是被迫代替她去往敵國和親速客。 傳聞我的和親對象是個殘疾皇子戚篙,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,514評論 2 348

推薦閱讀更多精彩內(nèi)容