四 重建二叉樹 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);//檢查分割出的左子樹和右子樹
}
};