1. 程式人生 > >【死磕演算法·棧和佇列】僅用遞迴實現棧的逆序

【死磕演算法·棧和佇列】僅用遞迴實現棧的逆序

題目要求:實現棧的逆序,但只能用遞迴函式和這個棧本身操作來實現,不能自己申請額外的資料結構。

題目思路:

實現兩個遞迴函式:

getBottomItem():移除棧底元素並返回該元素

reverse():實現整個棧逆序

 

如何寫遞迴函式?

遞迴函式在函式體中呼叫自己,對不同的值進行重複操作。

遞迴函式三要素:

1、要有可以退出函式的情況;

2、遞迴過程中將一個問題簡化到更小的規模,遞迴函式的引數規模一定是越來越小的

3、父問題和子問題不能有重疊。

但更重要的是,得知道遞迴半天,寫這個函式最後要返回什麼或者達成什麼效果,進而思考要怎樣一步步

去達到目的。

getBottomItem():

 

這個函式的目的是要得到棧底元素並移除,分三步走:

1、把棧底元素上面的元素一個個的都彈出來;

2、得到並移除棧底元素;

3、將之前在棧底元素上面的元素再壓回棧中去。

遞迴總要有個結尾,如果把遞迴函式比作”俄羅斯套娃“,我們得不斷把外殼拿掉直到得到最小的”實心娃娃“,這個遞迴函式的結尾在於得到並移除棧底元素。那麼我們由函式體前幾行就已經確定了:
 

public int getBottomItem(stack<int> s){

    int nowItem = s.pop();

    if(s.isEmpty()){ //遞迴到底要執行的操作

        return nowItem;

    }
    
    else{

        ...

    }

}

接下來我們填else 括號體的內容:

首先,函式不繼續遞迴無法觸底,把新棧作為引數繼續呼叫遞迴函式,遞迴函式觸底返回到這一層時會把棧底元素也返回,因此int last = getBottomItem(s)來儲存返回的棧底元素值。

看遞迴到底已經獲得棧底元素之後,我們應該做什麼——把原來在棧底元素上面的元素按原來順序壓入棧中

我們已經獲得了這一層裡移除的非棧底元素nowItem,接下來要把這一層的nowItem壓入棧中 s.push(nowItem)

返回到遞迴函式的最頂層,遞迴函式把原來的棧頂元素重新壓入到棧中,

不要忘了我們還在遞迴函式內,而最後的目標是返回棧底元素,因此函式最後返回棧底元素last。

最後getBottomItem()實現如下:


    public int getBottomItem(stack<int> s){
        int nowItem = s.pop();
        if(s.isEmpty())//遞迴到底
            return nowItem;
        else{//
            int last = getBottomItem(s)//沒遞迴到底的時候,我們要用一個變數儲存返回的棧底元素;
            s.push(nowItem)//把這一層的棧元素壓入棧中
            return last;
        }
         
}

我們再來看reverse()函式。

reverse函式的目的是讓棧逆序,棧頂元素要在棧底。同樣的我們首先將這個問題拆解成步驟:

1、呼叫getBottomItem獲取棧底元素並移除,直到我們得到棧頂元素並移除,此時棧為空,遞迴到底

2、把棧頂元素壓入棧底,其他元素按照最後後移除的先入棧的原則入棧,實現棧的逆序。

public void reverse(stack <int> s){
  if(s.isEmpty())
        return;
  int bottom = getBottomItem(s);
  reverse(s);
  s.push(bottom);
}

在向下一層層遞迴中棧底元素被移除,直到棧中只有棧頂元素,棧頂元素被移除後繼續遞迴,棧為空,此時遞迴函式開始向上返回。

返回上一層後,bottom值即為原棧頂值,注意到此時棧為空,要把他壓入棧底,即s.push(bottom)

總結

寫遞迴函式只要考慮到:

1、遞迴函式向下遞迴終結的條件是什麼,要返回什麼,把這段程式碼寫在函式題的前面。

2、向下遞迴結束開始向上返回時,假設只到了觸底反彈的上一層, 考慮我們在遞迴結果的基礎上如何操作,寫在“自己呼叫自己”的下面。

以下是一個遞迴函式體內部的必要步驟和前後邏輯:

Public int  recursiveFunction(s):

1、獲取遞迴函式觸底時需要返回的值(可選)

2、通常以if語句開頭,向下遞迴的終結條件及返回(如果遞迴函式需要返回某值,在這之前要獲取該值)

3、通常以else語句開頭,在呼叫自己之前保證對引數s進行過處理,使其規模簡化。

4、recursiveFunctions

5、向下遞迴到底,向上返回時到上一層,考慮要在向下遞迴到底的基礎上進行什麼操作。

6、返回遞迴函式從最底端開始往上傳遞的值(可選)

棧逆序程式碼實現:

class StackReverse {
public:
        int getBottomItem(vector <int> &s){
        int nowItem = s.back();//返回最後向量尾部最後一個元素
            s.pop_back();//從向量尾部彈出一個元素
        if(s.empty())//遞迴到底
            return nowItem;
        else{//
            int last = getBottomItem(s);//沒遞迴到底的時候,我們要用一個變數儲存返回的棧底元素;
            s.push_back(nowItem);//把這一層的棧元素壓入棧中,用vector的push_back()函式實現,向向量尾部增加一個元素
            return last;
        }
         
    }
    void reverse(vector <int> &s){
        if(s.empty())
            return;
        else{
            int bottom = getBottomItem(s);
            reverse(s);
            s.push_back(bottom);//自下向上把後彈出的值先壓入棧中,實現逆序
        }
    }
    vector<int> reverseStack(vector<int> A, int n) {
        // write code here
        reverse(A);
        return A;
    }
};