探寻排序的本质-二叉树的思想
快速排序就是二叉树的前序遍历,归并排序就是个二叉树的后序遍历。
快速排序的逻辑是,如果要对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);
}
归并排序的代码框架如下
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);
}
递归永远是我们的好帮手
写递归算法的关键是要明确函数的 定义 是什么,然后相信这个定义,利用这个定义推导出最终的结果,绝不要跳入递归的细节。
//定义:count(root)返回以root为根的树有多少节点
int count(TreeNode root){
//base case
if(root==null){
return 0;
}
return 1+count(root.left)+count(root.right);
}
算法实践
翻转二叉树
只要把二叉树上的每一个节点的左右子节点进行交换,最后的结果就是完全翻转之后的二叉树
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;
}
东哥在博客中写到,二叉树题目的难点其实是如何把题目要求细化成每个节点需要做的事情。
填充二叉树节点的下一个右侧指针
这道题目的要求填充它的每个next指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将next指针设置为NULL。
初始状态下,所有的next指针都被设置为NULL。
很快啊,我们就能想到想上道题一样
Node connect(Node root){
if(root==null || root.left==null){
return root;
}
root.left.next=root.right;
connect(root.left);
connect(root.right);
return root;
}
但是我们遇到的问题节点5和节点6不属于同一个节点,那么按照这段代码的逻辑,他俩就没办法被穿起来。
二叉树的问题难点在于,如何把题目的要求细化为每个节点需要做的事情,如果只依赖一个节点的话,肯定没有办法连接[跨父节点]的两个相邻节点的。
那么,我们的做法就是增加函数参数,一个节点做不到,我们就给他安排两个节点,[将每一层二叉树节点连接起来]
的题目可以细化为将每两个相邻节点都连接起来。
//主函数
Node connect(Node root){
if(root==null){
return null;
}
connectTwo(root.left,root.right);
return root;
}
//辅助函数
void connectTwoNode(Node node1,Node node2){
if(node1==null || node2==null){
return;
}
//前序遍历位置
//将传入的两个节点连接
node1.next=node2;
//连接相同父节点的两个子节点
connectTwoNode(node1.left,node1.right);
connectTwoNode(node2.left,node2.right);
//连接跨越父节点的两个子节点,同时也是修改之后变化最大的节点
connectTwoNode(node1.right,node2.left);
}
将二叉树展开为链表
函数签名如下:
void flattern(TreeNode root);
我们尝试给出这个函数的定义:
给flattern函数输入一个节点root,那么以root为跟的二叉树就会被拉平为一条链表
1.将root的左子树和右子树拉平。
2.将root的右子树接到左子树的下方,然后将整个左子树作为右子树。
//定义:将以root为根的树拉平为链表
void flattern(TreeNode root){
//base case
if(root==null){
return;
}
flattern(root.left);
flattern(root.right);
//后序遍历位置
//1.左右子树已经被拉平成一条链表,这里需要定义两个新的局部变量,方便我们后面修改指向引用
TreeNode left=root.left;
TreeNode right=root.right;
//2.将左子树作为右子树
root.left=null;
root.right=root.left;
//3.将原先的右子树接到当前右子树的末端
TreeNode p=root;
while(p.right!=null){
p=p.right;
}
p.right=right;
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。