二叉树

先序非递归

  递归算法使用栈的特性——先进后出,因此非递归遍历二叉树时也需要用栈来模拟递归调用。递归处理内容如下:

  1. 当前节点的右子树若存在压栈
  2. 当前节点的左子树若存在压栈
  3. 当前节点再次压栈(以便下次非递归时访问)

  非递归处理内容

  1. 访问当前节点
  2. 弹出当前节点
typedef struct TreeNode {
    int val;
    TreeNode *left, *right;
}TreeNode;

vector<int> preorderTraversal(TreeNode *root) {
    if(root == nullptr) {
        return {};
    }
    
    vector<int> res;
    stack<TreeNode *> call;
    call.push(root)
    while(!call.empty()) {
        TreeNode *tmp = call.top();
        call.pop();
        if(tmp != nullptr) { //当前节点没有被访问过 
            if(tmp->right) {
                call.push(tmp->right); //1.栈的最底层,最后处理右子树 
            }
            if(tmp->left) { //2.左子树在右子树上,因为先序遍历先访问左子树 
                call.push(tmp->left);
            }
            call.push(tmp); //3.当前访问的结点(可以看做根节点)在左子树上方,先序遍历根节点在左子树前被访问 
            call.push(nullptr); //4.标记当前结点被访问过 
        }
        else { //当前节点被访问过 
            res.push_back(call.top()->val); //5.访问当前节点,也就是加入结果集中 
            call.pop(); //6.彻底删除当前节点 
        }
    }
    return res;
}

中序非递归

  栈低到栈顶的顺序为:右子树-根节点-左子树,下次递归时左子树要弹出标记为被访问的节点

vector<int> inorderTraversal(TreeNode *root) {
    if(root == nullptr) {
        return {};
    }
    
    vector<int> res;
    stack<TreeNode *> call;
    call.push(root);
    while(!call.empty()) {
        TreeNode *tmp = call.top();
        call.pop();
        if(tmp != nullptr) {
            if(tmp->right) {
                call.push(tmp->right);
            }
            call.push(tmp);
            call.push(nullptr);//标记为要访问的结点,待处理;当左右子树都不存在时当前节点就是最后一个访问的结点 
            if(tmp->left) {
                call.push(tmp->left); //沿着左子树一直往左下方走 
            }
        }
        else {
            res.push_back(call.top()->val);
            call.pop(); 
        }
    }
    return res;
}

后序非递归

vector<int> postorderTraversal(TreeNode *root) {
    if(root == nullptr) {
        return {};
    }
    
    vector<int> res;
    stack<TreeNode *> call;
    call.push(root);
    while(!call.empty()) {
        TreeNode *tmp = call.top();
        call.pop();
        if(tmp != nullptr) {
            call.push(tmp);
            call.push(nullptr);
            if(tmp->right) {
                call.push(tmp->right);
            }
            if(tmp->left) {
                call.push(tmp->left);
            }
        }
        else {
            res.push_back(call.top()->val);
            call.pop();
        }
    }
    return res;
} 

分治法

  递归(先序递归遍历,从上到下)和分治遍历(先序遍历,从下向上合并子集)区别:前者一般将结果通过参数传递,后者递归返回结果合并。

  先分别处理局部,再合并结果。

//先序 
vector<int> inorderTraversal(TreeNode *root) {
    if(root == nullptr) {
        return {};
    }
    
    vector<int> res;
    vector<int> left = inorderTraversal(root->left);
    vector<int> right = inorderTraversal(root->right);
    
    res.push_back(root->val);
    res.insert(res.back(), left.begin(), left.end());
    res.insert(res.back(), right.begin(), right.end());
    return res;
}

BFS按层遍历

vector<vector<int>> leverOrder(TreeNode *root) {
    if(root == nullptr) {
        return {};
    }
    
    vector<vector<int>> res;
    vector<int> tmp;
    queue<TreeNode *> q;
    TreeNode *last = root, *nlast = nullptr;//last代表当前行最右结点;nlast代表下一行最右结点 
    q.push(root);
    while(!q.empty()) {
        TreeNode *cur = q.front();
        q.pop();
        tmp.push_back(cur->val);//访问操作 
        if(cur->left) {
            q.push(cur->left);
            nlast = cur->left;
        }
        if(cur->right) {
            q.push(cur->right);
            nlast = cur->right;
        }
        
        if(cur == last) { //当前节点 == 当前行最右节点证明当前行已经访问完毕 
            res.push_back(tmp);
            tmp.clear();
            last = nlast;
        }
    }
    return res;
}

DFS按层遍历

vector<vector<int>> res;
void levelOrder(TreeNode* root, int level) {
    if(root == nullptr) {
        return ;
    }
    
    if(res.size() == level) {
        res.resize(level+1);
    }
    res[level].push_back(root->val);
    levelOrder(root->left, level+1);
    levelOrder(root->right, level+1);
}

序列化与反序列化

void serialize(TreeNode* head,vector<char> &B)//先序序列化:B存放序列化结果
{
    if(!head)//1.遍历到空节点时做什么事
    {
        B.push_back('#');
        B.push_back('!');
        return;
    }
    //2.结点不为空
    int value=head->val;
    if(!value)//2.1值是否为真
    {
        B.push_back('0');
        B.push_back('!');
        serialize(head->left,B);
        serialize(head->right,B);
        return;
    }
    //数字数值可能超过一位
    int a=0;
    vector<int> A;
    while(value)
    {
        a=value%10;
        A.push_back(a);
        value/=10;
    }
    while(!A.empty())
    {
        a=A.back();
        A.pop_back();
        B.push_back('0'+a);
    }
    B.push_back('!');

    serialize(head->left,B);
    serialize(head->right,B);
    return;
}

void deserialize(vector<char> A,TreeNode* & head)//先序反序列化:#空结点,!当前结点结束,n表示当前读的字符位置,A为输入原序列
{
    int i,j,value=0;
    if((n>A.size()-1)||A[n]=='#')
        return;

    i=j=n;
    while(A[j]!='!')
        j++;
    while(j>i)
    {
        value=(A[i]-'0')+value*10;
        i++;
    }

    head=(TreeNode*)malloc(sizeof(TreeNode));
    (*head).val=value;
    (*head).right=(*head).left=NULL;
    n=i+1;
    deserialize(A,(*head).left);
    deserialize(A,(*head).right);
}

分治应用

  适用场景

  1. 快速排序
  2. 归并排序
  3. 二叉树相关问题

  分治法模板

  1. 递归返回条件
  2. 分段处理
  3. 合并结果

  递归结束需要对返回结果进行合并

void _merge(vector<int>& num, int start, int mid, int end) {
    int *tmp = new int[start-end+1];//合并两个临时区
    int i = start;
    int j = mid+1;
    int k = 0;
    
    while(i <= mid && j <= end) {
        tmp[k++] = num[i] < num[j] ? num[i++] : num[j++];
    } 
    
    while(i <= mid) {
        tmp[k++] = num[i++];
    }
    while(j <= end) {
        tmp[k++] = num[j++];
    }
    
    for(i = 0; i < k; ++i) {
        num[start+i] = tmp[i];
    }
    delete []tmp;
}
void mergeSortUpToDown(vector<int>& num, int start, int end) {
    if(num.empty() || start >= end) {
        return ;
    }
    
    int mid = start+(end-start)/2;
    mergeSortUpToDown(num, start, mid);
    mergeSortUpToDown(num, mid+1, end);
    
    _merge(num, start, mid, end);
}

快排

  快排由于是原地交换所以没有合并过程 传入的索引是存在的索引(如:0、length-1 等),越界可能导致崩溃

void quickSort(vector<int>& num, int start, int end) {
    if(start >= end || num.empty()) {
        return;
    }
    
    int left = start;
    int right = end;
    int key = num[start];
    while(left < right) {
        while(left < right && num[right] >= key) {
            --right;
        }
        num[left] = num[right];
        
        while(left < right && num[left] <= key) {
            ++left;
        }
        num[right] = num[left];
    }
    
    num[left] = key;
    quickSort(num, start, left);
    quickSort(num, left+1, end);
}

题目

求给定二叉树的最大深度

  按层求高度

int maxDepth(TreeNode* root) {
    if(!root)
        return 0;
    
    queue<TreeNode*> q;
    q.push(root);
    int depth=0;
    while(!q.empty())
    {
        ++depth;
        int layer=q.size();
        while(layer>0)//按层处理,把每层的结点处理完
        {
            auto tmp=q.front();
            q.pop();
            if(tmp->left)
                q.push(tmp->left);
            if(tmp->right)
                q.push(tmp->right);
            --layer;
        }
    }
    return depth;
}

  分治求,先递归求左子树高度,再递归求右子树高度,合并左右子树高度返回

int maxDepth(TreeNode* root) {
    if(root == NULL)
        return 0;
    
    int leftHeight = maxDepth(root->left);
    int rightHeight = maxDepth(root->right);

    return max(leftHeight, rightHeight)+1;
}
原文地址:https://www.cnblogs.com/tianzeng/p/13735416.html