努力经营当下,直至未来明朗!
文章目录
- 1. BST二叉搜索树的后序遍历序列
- 2. 二叉树中和为某一值的路径(二)[回溯法]
- 3. 字符串的排列 [全排列问题]
- 4. 最小的K个数 [topK问题]
普通小孩也要热爱生活!
1. BST二叉搜索树的后序遍历序列
二叉搜索树的后序遍历
1)思路:
① 首先复习二叉搜索树的概念:空树 or (左子树结点值一定小于根结点&&右子树结点值一定大于根结点值)【所有子树都要满足】
② 后序遍历:左右根
③ 后序遍历的特征:最后一个是root,然后前面的部分可以被分为两段,一段小于root(左子树),一段大于root(右子树);而每一段也是类似的,最后的是该段root,其余又可以分为两段。【区间划分】
④ 递归操作,操作左右子树
⑤ 一定要注意true的条件!
2)代码:
public class Solution {
// 前闭后闭
public boolean VerifySquenceOfBSTHelper(int [] sequence,int start,int end) {
// 一定不要忘记正确输出的条件!!
if(start >= end) {
return true;
}
int root = sequence[end]; // 后序遍历最后一个是根结点
int i = start;
// 先找小于根结点的-》属于左子树
while(sequence[i]<root) {
i++;
}
// 来到这儿,说明i的位置已经是>=root
for(int j=i; j<end; j++) {
// 一旦遇到小于root的就说明不是后序遍历
if(sequence[j] <= root) {
return false;
}
}
// 此时的排列[start,i,end/root]
return VerifySquenceOfBSTHelper(sequence,start,i-1)&&
VerifySquenceOfBSTHelper(sequence,i,end-1);
}
public boolean VerifySquenceOfBST(int [] sequence) {
if(sequence==null || sequence.length==0) {
return false;
}
return VerifySquenceOfBSTHelper(sequence,0,sequence.length-1);
}
}
2. 二叉树中和为某一值的路径(二)[回溯法]
二叉树中和为某一值的路径(二)
1)思路:
【回溯法】
① 路径是指从根节点到叶子节点
② 路径可能有多条,那就要输出所有的路径
③ 核心:[回溯法] 先添加值至待选结果,再判定现有结果是否满足条件(剪枝操作),接着DFS深度优先遍历,最后进行回退检测下一个。(如果找到一条完整的满足条件的待选结果就添加到结果集中)
④ 判定结果是否满足条件:减法(预期值-当前值 ==0 ?) or 加法(相加是否已经超过预期值);最终结束标志是已经遍历到叶子节点且达到预期值。
⑤ 注意:深浅拷贝问题!!ret.add(new ArrayList<Integer>(tmp));
⑥ 回溯法:基于二叉、多叉树的穷举过程(深度优先遍历DFS),在穷举的过程中要进行剪枝操作。
2)代码:
import java.util.ArrayList;
/**
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
// 进行DFS
private static void FindPathDFS(TreeNode root,int expectNumber,ArrayList<ArrayList<Integer>> ret,ArrayList<Integer> tmp) {
// 这里注意不要遗漏结束退出的条件
if(root == null) {
return;
}
// 添加值到待选结果中
tmp.add(root.val);
// 剪枝操作
// 判断&条件更新(使用减法,与预期结果进行比较)
expectNumber -= root.val;
// 注意这里不要遗漏正确的条件判断
// 也就是此时待选结果已经合法,需要添加到结果集中
if(root.left==null && root.right==null && expectNumber==0) {
// 一定要注意深浅拷贝!!
ret.add(new ArrayList<Integer>(tmp));
}
// 进行深度优先遍历(左右子树)
FindPathDFS(root.left,expectNumber,ret,tmp);
FindPathDFS(root.right,expectNumber,ret,tmp);
// 进行回退(移除当前的最后一个元素值)
tmp.remove(tmp.size()-1);
}
public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int expectNumber) {
// 声明结果集与待选结果
ArrayList<ArrayList<Integer>> ret = new ArrayList<>();
ArrayList<Integer> tmp = new ArrayList<>();
if(root == null) {
return ret;
}
// 此时进行深度优先遍历DFS
FindPathDFS(root,expectNumber,ret,tmp);
return ret;
}
}
3. 字符串的排列 [全排列问题]
字符串的排列
1)思路:
① “字典序”问题使用TreeSet来解决,是根据key来进行排列的,key不能为空。
② 其实也是“回溯算法”的思路[穷举+剪枝]:多叉树,但是只要出现过的节点后面就不再出现!
③ 每个字母都做一次开头(循环+交换),然后余下的是子问题(子问题采用深度优先遍历DFS+回退)
④ “剪枝”其实就是去重操作
⑤ String类型不能直接操作,所以要么转为可变字符串操作,要么转为字符数组
⑥ 引用类型注意深浅拷贝问题!!
2)代码:
import java.util.ArrayList;
import java.util.TreeSet;
public class Solution {
// 交换
private static void swap(char[] ch,int start,int i) {
char tmp = ch[start];
ch[start] = ch[i];
ch[i] = tmp;
}
// 进行排列
private static void PermutationHelper(char[] ch,int start,TreeSet<String> tmp) {
// 同样先判断
if(ch.length==0 || ch==null || start<0 || start>ch.length-1) {
return ;
}
// 此时就要进行排列
// 出口:需要将一条记录插入到tmp中
if(start == ch.length-1) {
// 此时说明已经走到了最后
tmp.add(String.valueOf(ch));
} else {
// 此时就说明排列还没有结束,需要继续排列
// 排列:交换+排列+交换回来
// start永远指向的是还未进行交换的开头位置!!
// 需要进行循环,使得每一个字母都有机会当开头
// 注意i的开始是start,不是0,start是会变化的!
for(int i=start; i<ch.length; i++) {
swap(ch,start,i);
PermutationHelper(ch,start+1,tmp);
swap(ch,start,i);
}
}
}
public ArrayList<String> Permutation(String str) {
// 存储最终结果集
ArrayList<String> ret = new ArrayList<>();
// 判断是否是空串
if(str==null || str.length()==0) {
return ret;
}
// 为了得到字典序,先将结果存储在TreeSet中(直接按字典序排列)
TreeSet<String> tmp = new TreeSet<>();
// 为了便于后续操作字符串,先转为字符数组
char[] ch = str.toCharArray();
// 开始进行排列:传入字符串,从0开始,先暂存到tmp中
PermutationHelper(ch,0,tmp);
// 然后将结果存到ret结果集中
// addAll就是将tmp里面的每一条记录(String)都存入ret中
// 类型也是符合的!
ret.addAll(tmp);
return ret;
}
}
4. 最小的K个数 [topK问题]
【面试常问!!】
最小的K个数
1)思路:
① 直接升序排列(但是不考虑)
② 采用大根堆的方式【重点】:先拿K个元素,因为要的是最小的K个元素,所以就将余下的元素依次与目前K个元素中的最大(堆顶)比较,大的出去小的留下。
③ 大根堆、小根堆可以使用优先级队列,注意参数的使用。
④ 补充:java官方文档:java官方文档
⑤ 优先级队列参数:
⑥ 优先级队列默认是升序排列,但是大根堆是降序排列的
⑦ 注意:最小K个则建立大根堆,因为要将堆顶最大的弹出去!
2)代码:
import java.util.ArrayList;
import java.util.PriorityQueue;
import java.util.Collections;
// 最小k个,那就建立大根堆(使用优先级队列)
public class Solution {
public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
// 存储结果的链表
ArrayList<Integer> ret = new ArrayList<>();
// 进行判断
if(input==null || input.length<k || k<=0) {
return ret;
}
// 满足条件的话就开始建立大根堆并比较
// 参数是容量+排序方式
// 优先级队列默认是升序
PriorityQueue<Integer> queue = new PriorityQueue<>(k,Collections.reverseOrder());
// 插入元素+判断
for(int i=0; i<input.length; i++) {
if(i < k) {
// 首先随便插入k个元素
queue.offer(input[i]);
} else {
// 这里开始就要开始与堆顶元素比较了
if(input[i] < queue.peek()) {
queue.poll();
queue.offer(input[i]);
}
}
}
// 最后将满足条件的k个数存入到ret中
for(int i=0; i<k; i++) {
ret.add(queue.poll());
}
return ret;
}
}