劍指offer刷題2

 

四 重建二叉樹 2/29

參考https://blog.csdn.net/JMasker/article/details/86761566

浙江大學數據結構二叉樹https://www.bilibili.com/video/av55114968?p=35

十四 鏈表倒數第k個節點

【快慢指針】【返回slow->next】

class Solution {
public:
    ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
    //快慢指針
        if(pListHead == NULL || k == 0) return NULL;//注意這裏unsigned,如果寫<0會報錯
        ListNode* dummy = new ListNode(-1);
        dummy -> next = pListHead;//插入假節點
        //這題可以不插入假節點
        ListNode* fast = dummy, *slow = dummy;//快慢指針指向假節點
        for(int i = 0; i < k; i++){//注意這裏終止條件,循環了k次【易錯】第一次寫了<=,就是k+1次
            if(fast -> next) fast = fast -> next;//快指針先走k步
            else return NULL;//k超出了鏈表長度
        }
        while(fast -> next){//快慢指針一起走
            fast = fast -> next;
            slow = slow -> next;
        }
        return slow->next;//
    }
};

十五 翻轉鏈表

浙江大學陳越老師講過

class Solution {
public:
    ListNode* ReverseList(ListNode* pHead) {
        //快速方法有堆棧,vector,爲了避免有空節點,使用常規方法
        if(pHead == NULL || pHead -> next == NULL ) return pHead;//鏈表元素爲0或者1,返回鏈表
        
        ListNode* dummy = new ListNode(-1);
        dummy -> next = pHead;//插入假節點,這題必需品
        
        ListNode* pre = pHead, *cur = pHead;
        while(cur){//鏈表翻轉,知道cur = NULL,此時pre指向最後一個節點(逆序第一個元素)
            ListNode* temp = cur -> next;
            cur -> next = pre;
            pre = cur;
            cur = temp; 
        }
        dummy -> next -> next = NULL;//使第一個元素(逆序的最後一個元素)指向NULL
        dummy -> next = pre;//最後一個節點(逆序第一個節點)
        return dummy -> next;

    }
};

十六 合併兩個排序鏈表3/1

【遞歸】

參考:https://blog.csdn.net/qq_41562704/article/details/89429773

採用遞歸的方法,假設待合併的鏈表1,鏈表2,目前鏈表1頭結點值小於鏈表2,則鏈表1的頭結點爲合併後鏈表的鏈表的頭結點,則合併後的鏈表頭節點指向的next的值爲鏈表1的剩餘節點與鏈表2的合併後的鏈表

思路:建立一個新鏈表頭,

          比較p1 p2val大小,將較小值存入新鏈表(循環此過程)


class Solution {
public:
    ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
    {
        if(pHead1 == NULL) return pHead2;
        if(pHead2 == NULL) return pHead1;
        //if(pHead1 == NULL && pHead2 ==NULL) return NULL;
        
        ListNode* dummy = new ListNode(-1);//新建頭結點
        
        if(pHead1 -> val < pHead2 -> val){//遞歸,將p1 p2中較小val值連接到新頭結點後面
            dummy->next = pHead1;//p1較小,連接到新頭結點
            dummy -> next -> next = Merge(pHead1->next,pHead2);//新鏈表下一個節點爲p1->next和p2中的較小值
        }else{
            dummy ->next = pHead2;
            dummy -> next -> next = Merge(pHead1, pHead2 -> next);
        }
        return dummy->next;
    }
};

常規做法:https://www.cnblogs.com/silentteller/p/11886551.html

可以用一個新的節點,來去比較兩個單調遞增的鏈表當前節點的值,如果p1當前的值小於p2,則新的節點的next=p1,p1移到下一個節點,新的節點p也要移動到下一個節點。

【易錯】這裏要新建一個指針指向新建鏈表,我一開始當作和遞歸一樣,只建了一個新節點。

class Solution {
public:
    ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
    {
        if(pHead1 == nullptr)
            return pHead2;
        if(pHead2 == nullptr)
            return pHead1;
        //ListNode resHead(0);
        ListNode* dummy = new ListNode(-1);//創建新的頭結點
        ListNode* p = dummy;//p指向dummy,p指針向下移動,一直更新【易錯】 
        while(pHead1 && pHead2){
            if(pHead1->val < pHead2->val){
                p->next = pHead1;//當前較小值爲p1,連接到p指針後面
                pHead1 = pHead1->next;//p1指針後移一位
            }
            else{
                p->next = pHead2;
                pHead2 = pHead2->next;
            }
            p = p->next;//p指針後移
        }
        if(pHead1)//如果最後剩下的是p1鏈表,則把p1連接到p上
            p->next = pHead1;
        if(pHead2)//如果最後剩下的是p2鏈表,則把p2連接到p上
            p->next = pHead2;
        return dummy -> next;
    }
};

十七 樹的子結構3/1

思路:(參考:https://blog.csdn.net/BlackLion_zhou/article/details/90706803

1、第一次判斷時,如果B爲空,則返回空
2、先判斷B根節點的值,如果B根節點的與A的根節點值相同,則判斷A的左子樹和B的左子樹及A的右子樹和B的右子樹是爲子結構關係。(遞歸)
3、直到判斷到B的節點值與A的節點值相等,而B沒有子樹,則此時爲其子結構
4、如果判斷過程中,B樹的前面部分都爲A樹從節點root1Now開始的一部分,但是最後出現了值不相等,或者B還有子樹而A沒有子樹的情況,則A應該返回至root1Now的左右節點,B應該返回值最初的B
代碼參考:https://www.cnblogs.com/chenruibin0614/p/11627559.html

【最值得思考的並列if】【isSubTree】寫的太好了

class Solution {
public:
    bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2)
    {

        if(pRoot1 == nullptr || pRoot2 == nullptr) return false;//若p1或p2空或二者都空,則返回false
        bool flag = false;//設置旗幟爲false
        
        //以下三個if語句不是並列
        if(pRoot1 -> val == pRoot2 -> val) flag = isSubTree(pRoot1,pRoot2);//二者根節點val相等,進入判斷
        if(!flag) flag = isSubTree(pRoot1 -> left, pRoot2);//根節點不同,判斷2是否是1的左子樹的子結構
        if(!flag) flag = isSubTree(pRoot1 -> right, pRoot2);//根節點不同,2樹不是1左子樹的子樹,判斷2是不是1右子樹的子樹
        return flag;
    }
    
    bool isSubTree(TreeNode* pRoot1, TreeNode* pRoot2){
        if(pRoot2 == nullptr) return true;//2樹遍歷完成(關鍵語句,結束的標誌,出口A)
//【這裏有點迷糊】返回true之後,還執行下一句嗎,如果兩個樹同時遍歷到最後,那下一句也爲真
        if(pRoot1 == nullptr) return false;//1樹異常(關鍵語句,1樹被遍歷完,出口B)
        
        bool flag = true;//
        if(pRoot1 -> val != pRoot2 -> val) return false;//若比較的值相同,則比較左右子樹
        if(flag) flag = isSubTree(pRoot1 -> left, pRoot2 -> left);//上一節點相同,比較左二子是否相同
        if(flag) flag = isSubTree(pRoot1 -> right, pRoot2 -> right);//上一節點相同,左二子相同,比較右兒子
        return flag;
    }
};

十八 二叉樹的鏡像3/1

操作給定的二叉樹,將其變換爲源二叉樹的鏡像。

【左右翻轉】 【從上到下】

class Solution {
public:
    void Mirror(TreeNode *pRoot) {

        if(pRoot == nullptr) return;//【易錯】這裏不能寫return nullptr 要求 void
        
        TreeNode* temp = pRoot -> left;
        pRoot -> left = pRoot -> right;
        pRoot -> right = temp;//將左右兒子交換
        
        Mirror(pRoot -> left);//對左子樹鏡像
        Mirror(pRoot -> right);//對右子樹鏡像
    }
};

參考:https://www.cnblogs.com/silentteller/p/11910991.html

十九 順時針打印矩陣

二十二 從上到下打印二叉樹3/1

【層序遍歷】【二叉樹】【隊列】

參考:浙江大學數據結構https://www.bilibili.com/video/av55114968?p=37

參考:https://blog.csdn.net/lyl194458/article/details/89790239

思路:

1 建一個數組res保存打印結果,建一個隊列que輔助層序遍歷(隊列數據類型爲TreeNode*,指針類型的啊)

2 將頭結點入隊

3當隊列不空時候循環(隊列爲空,遍歷結束,退出循環)

   a 新建一個TreeNode*指針指向隊列頭結點

   b 將頭結點的val 存入數組res

   c 檢查頭結點是否有左右兒子,有的話入隊列,無的話進入下一次循環

class Solution {
public:
    vector<int> PrintFromTopToBottom(TreeNode* root) {

        vector<int> res;//新建數組用於存放打印結果

        if(root == nullptr) return res;//如果二叉樹爲空,返回空的數組
        
        queue<TreeNode*> que;//建立隊列
        que.push(root);//root入隊列
        
        while(!que.empty()){//當隊列不爲空時循環,隊列爲空,遍歷了全部節點結束
            TreeNode* temp = que.front();//指針指向隊頭節點
            res.push_back(temp -> val);//將頭結點的值壓入數組
            
            if(temp -> left != nullptr)//如果隊頭節點有左兒子,壓入隊列
                que.push(temp -> left);
            if(temp -> right != nullptr)//如果有右兒子,壓入隊列
                que.push(temp -> right);
            
            que.pop();//彈出隊頭節點
        }
        return res;
        
    }
};

 

二十三 二叉搜索樹的後序遍歷序列3/1

參考:https://blog.csdn.net/qq_41901915/article/details/90270150

參考:https://www.cnblogs.com/silentteller/p/11924310.html

【二叉搜索樹】【後序遍歷】

思路:

1 序列最後一個一定是根節點

2 二叉搜索樹左子樹一定小於右子樹,掃描序列,

   a 直到遇到大於根節點的元素,記下左子樹長度;

   b 掃描剩下元素,如果遇到小於根節點的元素,返回false;

    至此,分割出根節點左右子樹,再將左右子樹繼續輸入

    直到只剩下兩個元素,即左右兩個葉子節點,返回true 

 

class Solution {
public:
    bool VerifySquenceOfBST(vector<int> sequence) {
        if(sequence.size() == 0) return false;//數組爲空和數組長度爲0有區別
        return helper(sequence, 0, sequence.size());
    }
    bool helper(vector<int> &v, int index, int length){//index是開始的下標,length是子樹長度
        if(length <= 1)//遞歸到只剩兩個元素時候,只剩左右兩個子節點,序列正確
            return true;//出口A
        /*找到第一個大於根節點的元素偏移量,此元素右邊都是右子樹,包括停止時候的v【index+i】,所以左子樹長度是index + i -i = i*/
        
        int i = 0;
        for(; i < length-1; ++i){
            if(v[index + i] > v[index + length -1])//注意是【index+i】
                break;
        }
        //檢查右子樹元素是否都大於根節點
        for(int j = i; j < length-1; ++j){
            if(v[index + j] < v[index + length -1])//如果右子樹存在小於根節點的情況,則序列不是後序遍歷
                return false;//,出口B
        }
        return helper(v, index, i) && helper(v, index+i, length-i-1);//檢查分割出的左子樹和右子樹
    }
};

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章