數(shù)據(jù)結(jié)構(gòu)基礎(chǔ)-遞歸和循環(huán)技巧

為什么要用遞歸

遞歸是從數(shù)學(xué)領(lǐng)域的數(shù)學(xué)歸納法借鑒過來的一種技術(shù)。遞歸代碼通常比迭代代碼更加簡潔易懂腕铸。當(dāng)任務(wù)能夠被相似的子任務(wù)定義時(shí)惜犀,采用遞歸處理十分有效。二分排序和遍歷等問題往往有簡潔的遞歸解決方案汽烦。

遞歸函數(shù)的格式

if (判斷是否是基礎(chǔ)情況) {
    return 該基礎(chǔ)情況下的函數(shù)的值
} else if (判斷是否為另一種基礎(chǔ)情況) {
    return 該基礎(chǔ)情況下的函數(shù)的值
    .
    .
    .
} else return (遞歸調(diào)用)

eg: 階乘為例
n!=1 n = 0
n! = n * (n - 1)! n > 0

int fact(int n) {
    if (n == 1) return 1;
    else if (n == 0) return 1;
    else return n * fact(n-1);
}

遞歸和內(nèi)存

每次遞歸調(diào)用都會(huì)在內(nèi)存中生成一個(gè)新的函數(shù)副本莉御。分析上面的階乘函數(shù):


recurse.png

每一次調(diào)用都會(huì)在程序運(yùn)行的棧內(nèi)存上創(chuàng)建一個(gè)函數(shù)副本,但是棧內(nèi)存是有限的礁叔,當(dāng)遞歸次數(shù)達(dá)到一個(gè)閾值的時(shí)候就會(huì)造成耗盡內(nèi)存,棧溢出錯(cuò)誤煮岁。

  • 遞歸控制
  1. 嚴(yán)格定義遞歸函數(shù)的作用,包括函數(shù)画机、返回值新症,side-effect
  2. 一般步氏,后特殊
  3. 每次調(diào)用必須縮小問題規(guī)模
  4. 每次問題規(guī)耐降縮小程度必須為1
eg:創(chuàng)建鏈表
public Node createLinkedList(List<Integer> data){
    //最后考慮特殊情況
    if(data.isEmpty()){
        retrun null;
    }
    //先考慮一般情況,第一個(gè)成立隆嗅,然后n-1個(gè)成立
    Node firstNode = new Node(data(0));
    Node headOfSublist = createLinkedList(data.subList(1,data.size()));
    firstNode.setNext(headOfSublist);
    return firstNode;
}

eg:反轉(zhuǎn)鏈表

public Node reverseList(Node head){
    //最后思考的特殊情況
    if(head == null || head.getNext() == null){
        return head;
    }
    //一般情況 假如他可以反轉(zhuǎn)鏈表得到出去第一個(gè)的剩下反轉(zhuǎn)
    Node newHead = reverseList(head.getNext());
    head.getNext().setNext(head);
    head.setNext(null);
    return newHead;
}

eg:給定一顆給定的二叉樹榛瓮,輸出所有的從根結(jié)點(diǎn)到葉子結(jié)點(diǎn)的路徑

//time: O(n) space: O(n)
public void printPaths(BinaryTreeNode root){
        int[] path = new int[256];
        printPaths(root, path, 0);
    }

    public void printPaths(BinaryTreeNode root, int[] path, int pathLength){
        if (root == null) {
            return;
        }
        path[pathLength] = root.getData();
        pathLength++;
        if (root.getLeft() == null && root.getRight() == null) {
            printArray(path, pathLength);
        }else {
            printPaths(root.getLeft(), path, pathLength);
            printPaths(root.getRight(), path, pathLength);
        }
    }

    public printArray(int[] path, int pathLength){
        for (int i = 0; i < pathLength; i++) {
            System.out.print(""+path[i]);
        }
        System.out.println();
    }

回溯

回溯是一種采用分治策略進(jìn)行窮舉搜索的方法。(有時(shí)候求解一個(gè)問題最好的方法就是嘗試所有的可能性)

  • 二進(jìn)制串
  • 生成k進(jìn)制串
  • 背包問題
  • 圖的著色問題

eg:從一個(gè)數(shù)組中列出取出n個(gè)元素的所有的組合

首先分解問題,選第一個(gè)元素坝锰,然后再剩下的數(shù)組中選n-1個(gè);不選第一個(gè)元素顷级,然后從剩下的數(shù)組中選n個(gè)确垫。

public void combinations(List<Integer> selected, List<Integer> data, int n){
    if(n == 0){
        for(Integer i : seleted){
            System.out.print(i+"");
        }
        return;
    }
    if(data.isEmpty()){
        return;
    }
    //selected first element
    seleted.add(data.get(0));
    combinations(selected, data.sublist(1,data.size()));
    
    //unselect first element
    selected.remove(selected.size() - 1);
    combinations(selected, data.sublist(1, data.size()), n);
}
  • 循環(huán)控制
  1. 定義循環(huán)不定式,并在循環(huán)體每次結(jié)束后保持循環(huán)不變式
  2. 先一般删掀,后特殊
  3. 每次必須向前推進(jìn)循環(huán)不變式中涉及的變量值
  4. 每次推進(jìn)的規(guī)模必須為1

eg:依然是上面的鏈表反轉(zhuǎn)的例子,用循環(huán)不定式實(shí)現(xiàn)

public Node reverseLinkedList(Node head){
    Node newHead = null;
    Node curHead = head;
    while(curHead != null) {
        Node next = curHead.getNext();
        curHead.setNext(newHead);
        newHead = curHead;
        curHead = next;
    }
    return newHead;
}

eg:鏈表中delete_if

public void deleteIfEquals(Node head, int value){
    while(head != null && head.getValue() == value){
        head = head.getNext();
    }
    if(head == null){
        return null;
    }
    Node prev = head;
    while(prev.getNext() != null){
        if(prev.getNext().getValue() == value){
            prev.getNext().setNext(prev.getNext().getNext());
        }else{
            prev = prev.getNext();
        }
    }
}

保持并推進(jìn)反轉(zhuǎn)關(guān)系

  • 邊界控制
    eg:二分查找
public int binarySearch(int[] arr, int k){
    int a = 0;
    int b = arr.length();
    while(a < b){
        int m = a + (b - a) /2;
        if(k < arr[m]){
            b = m;
        }else if(k > arr[m]){
            a = m + 1;
        }else {
            return m;
        }
        return -1;
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末纤子,一起剝皮案震驚了整個(gè)濱河市款票,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌艾少,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,273評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件幔妨,死亡現(xiàn)場離奇詭異,居然都是意外死亡陶冷,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評論 3 398
  • 文/潘曉璐 我一進(jìn)店門埂伦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來思恐,“玉大人,你說我怎么就攤上這事胀莹。” “怎么了描焰?”我有些...
    開封第一講書人閱讀 167,709評論 0 360
  • 文/不壞的土叔 我叫張陵栅螟,是天一觀的道長篱竭。 經(jīng)常有香客問我,道長掺逼,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,520評論 1 296
  • 正文 為了忘掉前任吕喘,我火速辦了婚禮,結(jié)果婚禮上募舟,老公的妹妹穿的比我還像新娘。我一直安慰自己胃珍,他們只是感情好梁肿,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,515評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著钮热,像睡著了一般。 火紅的嫁衣襯著肌膚如雪烛芬。 梳的紋絲不亂的頭發(fā)上隧期,一...
    開封第一講書人閱讀 52,158評論 1 308
  • 那天,我揣著相機(jī)與錄音赘娄,去河邊找鬼仆潮。 笑死,一個(gè)胖子當(dāng)著我的面吹牛遣臼,可吹牛的內(nèi)容都是我干的性置。 我是一名探鬼主播,決...
    沈念sama閱讀 40,755評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼揍堰,長吁一口氣:“原來是場噩夢啊……” “哼鹏浅!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起屏歹,我...
    開封第一講書人閱讀 39,660評論 0 276
  • 序言:老撾萬榮一對情侶失蹤隐砸,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后蝙眶,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體季希,經(jīng)...
    沈念sama閱讀 46,203評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,287評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了武通。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,427評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡珊搀,死狀恐怖冶忱,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情境析,我是刑警寧澤囚枪,帶...
    沈念sama閱讀 36,122評論 5 349
  • 正文 年R本政府宣布,位于F島的核電站劳淆,受9級(jí)特大地震影響链沼,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜沛鸵,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,801評論 3 333
  • 文/蒙蒙 一括勺、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧曲掰,春花似錦疾捍、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至吊趾,卻和暖如春宛裕,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背论泛。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評論 1 272
  • 我被黑心中介騙來泰國打工揩尸, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人屁奏。 一個(gè)月前我還...
    沈念sama閱讀 48,808評論 3 376
  • 正文 我出身青樓岩榆,卻偏偏與公主長得像,于是被迫代替她去往敵國和親了袁。 傳聞我的和親對象是個(gè)殘疾皇子朗恳,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,440評論 2 359

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