【計劃執行報告】Day8 LeetCode探索:二叉樹的典型操作實現【遞歸思想】
Day8 04-07 LeetCode探索:二叉樹的典型操作實現【遞歸思想】
今天是:
- 計劃執行的第8天
- 學習二叉樹的一天
- 從計劃實現開始“浪”的時間最多的一天
從今天開始,非特殊情況不會把寫這些報告的時間記錄在atimelogger中(因爲寫報告時時間的記錄已經停止了),並且準備把明日的計劃也貼在最後。
1.今日感想
- 機器學習的VAE聽不進去;
- 通過之前的學習以及今天的編程實踐對二叉樹的性質、典型操作(遞歸實現如DFS遍歷、BFS遍歷等等)有了更深刻的理解,但是目前還沒嘗試過用迭代方法解決這些問題;
- 掌握這些典型數據結構不僅僅是爲了程序競賽,更重要地是它們作爲一些機器學習算法的先修內容,有助於機器學習相關算法的理解,如決策樹、isomap等等
2.計劃執行報告
2.1近期計劃(03-31-04-12)
1.有所改動,貌似組內的大佬已經把所有工作承包了,而且報告日期推遲到了下週三,因此任務重心由“在4月12日之前準備好(推遲了)機器學習的最終報告——《暢想無監督學習》”變爲了補充機器學習所需的數學知識(線性代數與概率統計)
2.完成專業課的作業(流體機械能轉化、生物質能,新能源熱利用可以往後稍稍);
3.備戰藍橋杯,爲此:①1h左右的典型算法補充(優先Leetcode“探索”模塊);②C/C++語法知識;③常見數據結構的構造與基本操作實現;④必要的練習與練題總結(比如時長1:30虛擬競賽與“覆盤”)
2.2今日計劃表
2.3實際時間分配
- 今天玩嗨了,以後要注意;
- 至於洗漱超時,主要是今天洗了澡
3. 二叉樹的結構構造與常用操作實現
今天上午自己寫了個cpp程序把二叉樹實現了一下,爲了寫起來方便沒把結構定義放在頭文件中,途中遇到一個低級失誤:在實現完先序遍歷後,準備寫中序和後序遍歷函數時,忘記把函數體內的遞歸函數的名稱改了,導致結果與預期不符,我找錯誤用了半個小時,真的心態有點小崩。教訓就是:對於遞歸問題,Ctrl+C/V時一定要留意內部調用的函數是否需要變化。
以下是實現的cpp代碼:
#include<iostream>
#include<cstdlib>
#include<queue>
using namespace std;
typedef int elem;
typedef struct TreeNode{
elem val;
struct TreeNode* left,*right;
}BiNode,*BiTree;
void visit(elem val){//訪問操作
cout<<val<<endl;
}
void PreOrderTraverse(BiTree T){//先序遍歷
if(T){
visit(T->val);
PreOrderTraverse(T->left);
PreOrderTraverse(T->right);
}
}
void PostOrderTraverse(BiTree T){//後序遍歷
if(T){
//論複製粘貼的壞處,困擾了我半小時
//PreOrderTraverse(T->left);
//PreOrderTraverse(T->right);
PostOrderTraverse(T->left);
PostOrderTraverse(T->right);
visit(T->val);
}
}
void InOrderTraverse(BiTree T){//中序遍歷
if(T){
//PreOrderTraverse(T->left);
InOrderTraverse(T->left);
visit(T->val);
//PreOrderTraverse(T->right);
InOrderTraverse(T->right);
}
}
//假定元素非負
void CreateBiTree(BiTree *T){//樹的構建
int x;
cin>>x;
if(x<0) return;
else{
*T=(BiNode*)malloc(sizeof(BiNode));
(*T)->val=x;
(*T)->left=NULL;
(*T)->right=NULL;
CreateBiTree(&((*T)->left));//->的優先級比*高
CreateBiTree(&((*T)->right));
}
}
//靜態數組(假定元素不超過6個)
void LOT(BiTree T){//按層次遍歷
if(T){
int front=-1,rear=0;
BiTree queue[6];
BiTree p;
queue[0]=T;
while(front<rear){
p=queue[++front];
visit(p->val);
if(p->left)
queue[++rear]=p->left;
if(p->right)
queue[++rear]=p->right;
}
}
}
//STL模板
void LayerOrderTraverse(BiTree T){//按層次遍歷
if(T){
queue<BiTree> q;
BiTree p;
q.push(T);
while(!q.empty()){
p=q.front();
q.pop();
visit(p->val);
if(p->left)
q.push(p->left);
if(p->right)
q.push(p->right);
}
}
}
int main(){
BiTree T;
CreateBiTree(&T);
cout<<"DLR"<<endl;
PreOrderTraverse(T);
cout<<"LRD"<<endl;
PostOrderTraverse(T);
cout<<"LDR"<<endl;
InOrderTraverse(T);
cout<<"LayerOrderTraverse:"<<endl;
//LayerOrderTraverse(T);
LOT(T);
free(T);
return 0;
}
4. LeetCode探索:二叉樹的典型操作實現
目前Leetcode二叉樹部分完成了2/3,還剩下總結篇的六道算法題。做這些題的目的便是熟悉競賽時的風格,畢竟對於同一個知識點可能在形式上會有所變化。
4.1 實現過程中遇到的ERROR
先擺上在Leetcode上調試時遇到的幾個問題:
- 【ERROR】Vector declaration “expected parameter declarator”
在stack-overflow上看到的一個解答如下:
意思是,對於容器變量的初始化,一定不要放在函數之外(可以聲明,但是不能定義),這個也困擾了我很久(找時間一定要好好看看容器模板)。例如以下寫法就是錯誤的:
- 【ERROR】reference binding to null pointer of type ‘value_type’
這個問題很有可能是數組(容器)越界了,要檢查下相關的數組(容器)下標是否越界。
4.2 Leetcode探索闖關題解記錄
以下是今日闖關的4道題,大多是用遞歸實現的,關於遍歷(上午的代碼已經展示過了)操作就不再列出了。部分題可以用迭代,但是暫時還不會。
Stage 01 二叉樹的層序遍歷
【題目描述】
這個應該令我印象最爲深刻的一道題了,儘管層序遍歷之前寫過了,但就因爲它的返回值形式使得這道題的寫法大變樣,要考慮層次以及容器的擴容。費了不少時間,以下是我的解法:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<vector<int>> v;
vector<vector<int>> levelOrder(TreeNode* root) {
if(root){
queue<pair<TreeNode*,int> >q;
TreeNode* node;
q.push(make_pair(root,0)); //<node,level>
int cur_size=0; //v的當前數組元素個數
while(!q.empty()){
pair<TreeNode*,int>p=q.front();
node=p.first;
q.pop();
if(cur_size<=p.second){
cur_size=p.second+1;
v.resize(cur_size);
}
v[p.second].push_back(p.first->val);
if(node->left)
q.push(make_pair(node->left,p.second+1));
if(node->right)
q.push(make_pair(node->right,p.second+1));
}
}
return v;
}
};
Stage 02 二叉樹的最大深度
【題目描述】
有自頂向下(top-down)以及自底向上(bottom-up)兩種方法,選取方法的原則如下,我採用的是後者。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
int ans=0;
int max(int a,int b){
return a>b?a:b;
}
int maxDepth(TreeNode* root) {
if(!root)
return 0;
return max(maxDepth(root->left),maxDepth(root->right))+1;
}
};
Stage 03 對稱二叉樹
【題目描述】
想了挺久的(主要是遞歸的方向選錯了),不過看了別人的思路後很快就實現了。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
bool isSymmetric(TreeNode* nl,TreeNode* nr){
if(!nl&&!nr)//兩子樹均爲空
return true;
if(nl&&!nr||!nl&&nr)//一個爲空,另一個不爲空
return false;
if(nl->val!=nr->val)//判斷兩子樹的節點是否相同
return false;
return isSymmetric(nl->left,nr->right)&
isSymmetric(nl->right,nr->left);
}
bool isSymmetric(TreeNode* root) {
if(!root)//空樹被認爲是對稱的
return true;
return isSymmetric(root->left,root->right);
}
};
Stage 04 路徑總和
【題目描述】
這個遞歸挺好想的,實現如下:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
bool hasPathSum(TreeNode* root, int sum) {
if(!root)//空樹返回false
return false;
if(!root->left&&!root->right)//葉子節點
return sum==root->val;
return hasPathSum(root->left,sum-root->val)|
hasPathSum(root->right,sum-root->val);
}
};
回想半年前,還在旁聽數據結構的時候,當時是多麼地抵觸”樹”,現在覺得好像也就那樣(當然現在實現的操作都是些皮毛啦),這也是爲何“早接觸一些東西總會是好的”的緣故吧!