347. 前 K 个高频元素
题目链接:https://programmercarl.com/0347.%E5%89%8DK%E4%B8%AA%E9%AB%98%E9%A2%91%E5%85%83%E7%B4%A0.html#%E5%85%B6%E4%BB%96%E8%AF%AD%E8%A8%80%E7%89%88%E6%9C%AC
思路
- 小根堆–用优先队列
PriorityQueue
实现
语法
- 优先队列
PriorityQueue
:- 因为要比较的是频次,所以要在小根堆里保存数组int[];pair2[1]-pair1[1]大根堆,pair1[1]-pair2[1]小根堆:
PriorityQueue<int[]> pq = new PriorityQueue<>((pair1, pair2)->pair2[1]-pair1[1]);
- 添加:
add()
,删除:poll()
- 因为堆里保存的是int[],所以add的是
new int[]{key,value}
- 因为要比较的是频次,所以要在小根堆里保存数组int[];pair2[1]-pair1[1]大根堆,pair1[1]-pair2[1]小根堆:
- hashmap:
- map注意是保存两个值
Map<Integer,Integer> map = new HashMap<>();
- 遍历:
- key和value:
for(Map.Entry<Integer,Integer> en:map.entrySet()):
- key:
for(Integer key : map.keySet()):
- key和value:
- map注意是保存两个值
- 泛型参数:
- 泛型参数必须是引用类型,而原始类型不是引用类型。
- int[]是引用类型
二叉树的递归遍历
题目链接:https://programmercarl.com/%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E9%80%92%E5%BD%92%E9%81%8D%E5%8E%86.html#%E6%80%9D%E8%B7%AF
- 因为要打印出前序遍历节点的数值,所以参数里需要传入数组来放节点的数值,除了这一点就不需要再处理什么数据了也不需要有返回值,所以递归函数返回类型就是void
- 确定终止条件:在递归的过程中,如何算是递归结束了呢,当然是当前遍历的节点是空了,那么本层递归就要结束了,所以如果当前遍历的这个节点是空,就直接return
- 确定单层递归的逻辑
二叉树的非递归遍历
题目链接:https://programmercarl.com/%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E8%BF%AD%E4%BB%A3%E9%81%8D%E5%8E%86.html#%E5%85%B6%E4%BB%96%E8%AF%AD%E8%A8%80%E7%89%88%E6%9C%AC
算法参考:https://blog.csdn.net/Monster_ii/article/details/82115772
前序遍历
思路:从root开始遍历左子树,全部依次压栈,左子树遍历完,开始自下至上遍历右子树。
在前序访问中,栈中元素都是自己和自己的左孩子都访问过了,而右孩子还没有访问到的节点。
伪代码:
TreeNode cur=root;
while(cur!=null||!st.empty()){
while(cur!=null){
访问cur(打印或者添加数组);
st.push(cur);当前节点压栈
cur=cur.left;遍历左孩子
}
cur=st.pop();栈顶是最左的节点
cur=cur.right;左子树遍历完,遍历右子树
}
完整代码:
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> ans=new ArrayList<>();
Stack<TreeNode> st=new Stack<>();
TreeNode cur=root;
while(cur!=null||!st.empty()){
while(cur!=null){
ans.add(cur.val);
st.push(cur);
cur=cur.left;
}
cur=st.pop();
cur=cur.right;
}
return ans;
}
中序遍历
思路:和前序类似,从root开始遍历左子树,全部依次压栈,左子树遍历完,开始自下至上遍历右子树。
在中序访问中,栈中元素都是自己和自己的左孩子,但都还没访问,右孩子也没有访问到的节点。
伪代码:
TreeNode cur=root;
while(cur!=null||!st.empty()){
while(cur!=null){
st.push(cur);当前节点压栈
cur=cur.left;遍历左孩子
}
cur=st.pop();栈顶是最左的节点
访问cur节点
cur=cur.right;左子树遍历完,遍历右子树
}
完整代码:
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> ans=new ArrayList<>();
Stack<TreeNode>st=new Stack<>();
TreeNode cur=root;
while(cur!=null||!st.empty()){
while(cur!=null){
st.push(cur);
cur=cur.left;
}
cur=st.pop();
ans.add(cur.val);
cur=cur.right;
}
return ans;
}
后序遍历
思路:和前序类似,从root开始遍历左子树,全部依次压栈,左子树遍历完,开始自下至上遍历右子树。但多了top指针和last指针。top是栈顶节点,last是上一个存放的节点。
因为后序遍历,所以先左子树,再右子树,再中间节点。
怎么判断top为中间节点?应该判断top的右节点是last,即中间节点的右子树刚刚遍历完毕。
伪代码:
TreeNode cur=root;
while(cur!=null||!st.empty()){
while(cur!=null){
st.push(cur);当前节点压栈
cur=cur.left;遍历左孩子
}
top=st.peek();栈顶是最左的节点
if(top没有右子树||top的右节点遍历过了(top是父节点)){
st.pop();
访问top
last=top;
}
else{
cur=top.right;左子树遍历完,遍历右子树
}
}
完整代码:
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> ans=new ArrayList<>();
Stack<TreeNode>st=new Stack<>();
TreeNode cur=root,last=null,top=null;
while(cur!=null||!st.empty()){
while(cur!=null){
st.push(cur);
cur=cur.left;
}
top=st.peek();
if(top.right==null||top.right==last){
ans.add(top.val);
last=top;
st.pop();
}else{
cur=top.right;
}
}
return ans;
}