数据结构&算法——二叉树
Posted vector6_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构&算法——二叉树相关的知识,希望对你有一定的参考价值。
数据结构&算法——二叉树
很多经典算法都是二叉树的变形。比如说快速排序就是个二叉树的前序遍历,归并排序就是个二叉树的后序遍历。
快速排序的逻辑是,若要对 nums[lo..hi]
进行排序,我们先找一个分界点 p
,通过交换元素使得 nums[lo..p-1]
都小于等于 nums[p]
,且 nums[p+1..hi]
都大于 nums[p]
,然后递归地去 nums[lo..p-1]
和 nums[p+1..hi]
中寻找新的分界点,最后整个数组就被排序了。
快速排序的算法框架:
void sort(int[] nums, int lo, int hi)
/****** 前序遍历位置 ******/
// 通过交换元素构建分界点 p
int p = partition(nums, lo, hi);
/************************/
sort(nums, lo, p - 1);
sort(nums, p + 1, hi);
对于归并排序,若要对 nums[lo..hi]
进行排序,我们先对 nums[lo..mid]
排序,再对 nums[mid+1..hi]
排序,最后把这两个有序的子数组合并,整个数组就排好序了。归并排序的代码框架如下:
void sort(int[] nums, int lo, int hi)
int mid = (lo + hi) / 2;
sort(nums, lo, mid);
sort(nums, mid + 1, hi);
/****** 后序遍历位置 ******/
// 合并两个排好序的子数组
merge(nums, lo, mid, hi);
/************************/
二叉树问题的重点是搞清楚当前 root 节点“该做什么”以及“什么时候做”,然后根据函数定义递归调用子节点,递归调用会让孩子节点做相同的事情。
其中“该做什么”指在当前节点需要的处理行为,“什么时候做”指考虑这段代码到底应该写在前序、中序还是后序遍历的代码位置上。
翻转二叉树
// 将整棵树的节点翻转
TreeNode invertTree(TreeNode root)
// base case
if (root == null)
return null;
/**** 前序遍历位置 ****/
// root 节点需要交换它的左右子节点
TreeNode tmp = root.left;
root.left = root.right;
root.right = tmp;
// 让左右子节点继续翻转它们的子节点
invertTree(root.left);
invertTree(root.right);
return root;
填充二叉树节点的右侧指针
给定一个 完美二叉树 ,其所有叶子节点都在同一层,每个父节点都有两个子节点。二叉树定义如下:
struct Node int val; Node *left; Node *right; Node *next;
填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。
初始状态下,所有 next 指针都被设置为 NULL。
class Solution
public:
void connectHelper(Node* node1, Node* node2)
if(node1==nullptr)
return;
/**** 前序遍历位置 ****/
// 将传入的两个节点连接
node1->next=node2;
// 连接相同父节点的两个子节点
connectHelper(node1->left, node1->right);
connectHelper(node2->left, node2->right);
// 连接跨越父节点的两个子节点
connectHelper(node1->right,node2->left);
Node* connect(Node* root)
if(root==nullptr)
return nullptr;
connectHelper(root->left, root->right);
return root;
;
如果我们在当前根节点只是将其左右子节点连接,可能出现的问题是,对于不是同一个父节点的两个相邻节点(跨越父节点的两个子节点)无法被连接。如果只依赖一个节点的话,肯定是没办法连接跨父节点的两个相邻节点的那么,我们的做法就是增加函数参数,一个节点做不到,我们就给他安排两个节点,「将每一层二叉树节点连接起来」可以细化成「将每两个相邻节点都连接起来」。
将二叉树展开为链表
给你二叉树的根结点 root ,请你将它展开为一个单链表:
展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左子指针始终为 null 。
展开后的单链表应该与二叉树 先序遍历 顺序相同。
class Solution
public:
void flatten(TreeNode* root)
if(root == nullptr)
return;
flatten(root->left); //先将左右子树都展开
flatten(root->right);
TreeNode* temp = root->right;//再将左子树放在右指针位置,左子树的最后一个节点指向右子树
root->right = root->left;
root->left = nullptr;
while(root->right!=nullptr)
root = root->right;
root->right = temp;
;
最大二叉树
给定一个不含重复元素的整数数组 nums 。一个以此数组直接递归构建的 最大二叉树 定义如下:
二叉树的根是数组 nums 中的最大元素。
左子树是通过数组中 最大值左边部分 递归构造出的最大二叉树。
右子树是通过数组中 最大值右边部分 递归构造出的最大二叉树。
返回有给定数组 nums 构建的 最大二叉树 。
/**
* Definition for a binary tree node.
* struct TreeNode
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr)
* TreeNode(int x) : val(x), left(nullptr), right(nullptr)
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right)
* ;
*/
class Solution
public:
int getMax(vector<int> nums, int low, int high)
if(low==high)
return low;
int maxIndex = low, maxNum = nums[low];
for(int i = low+1; i <= high; ++i)
if(maxNum<nums[i])
maxNum = nums[i];
maxIndex = i;
return maxIndex;
TreeNode* buildNode(vector<int> nums, int low, int high)
if(nums.size() == 0 || low > high)
return nullptr;
if(low==high)
TreeNode* node = new TreeNode(nums[low]);
return node;
int index = getMax(nums, low, high);
TreeNode* root = new TreeNode(nums[index]);
root->left = buildNode(nums, low, index-1);
root->right = buildNode(nums, index+1, high);
return root;
TreeNode* constructMaximumBinaryTree(vector<int>& nums)
if(nums.size()==0)
return nullptr;
TreeNode* root = buildNode(nums, 0, nums.size()-1);
return root;
;
对于每个根节点,只需要找到当前 nums
中的最大值和对应的索引,然后递归调用左右数组构造左右子树即可。对于这种通过索引去递归的,最好使用辅助函数显式地传入(维护)索引,更清晰不易出错。
寻找重复的子树
给定一棵二叉树,返回所有重复的子树。对于同一类的重复子树,你只需要返回其中任意一棵的根结点即可。
两棵树重复是指它们具有相同的结构以及相同的结点值。
/**
* Definition for a binary tree node.
* struct TreeNode
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr)
* TreeNode(int x) : val(x), left(nullptr), right(nullptr)
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right)
* ;
*/
class Solution
public:
unordered_map<std::string, int> subTreeMap; //如果有2个以上重复的子树,结果中只能返回一次,所以使用unordered_map存储,键为序列化的子树,值 //为该子树出现的次数。
vector<TreeNode*> result;
vector<TreeNode*> findDuplicateSubtrees(TreeNode* root)
traverse(root);
return result;
std::string traverse(TreeNode* root)
if(root == nullptr)
return "#";
std::string left = traverse(root->left);
std::string right = traverse(root->right);
string sTree = left + "," + right + "," + to_string(root->val);//序列化当前子树
if(subTreeMap.find(sTree) != subTreeMap.end()) //判断是否有重复子树
if(subTreeMap[sTree]==1) //若重复子树添加过,则不需要再添加
result.push_back(root);
subTreeMap[sTree]++;
else
subTreeMap.insert(make_pair(sTree, 1));
return sTree;
;
要判断以当前节点为根的子树是否有与之相同的子树,首先要清楚自己所在子树的具体内容(后序遍历求得),其次还要知道其他子树的情况(使用map存储其他子树的),当然这里需要把子树序列化来存储,常用的形式:left + “,” + right + “,” + to_string(root->val) (后序)。要注意的是如果有2个以上重复的子树,结果中只能返回一次,所以使用unordered_map存储,键为序列化的子树,值为该子树出现的次数。
以上是关于数据结构&算法——二叉树的主要内容,如果未能解决你的问题,请参考以下文章