回溯篇
前言
本次算法入门训练主要训练以下二个内容:
- 以特殊数据结构为载体的算法结构,比如:数组、链表、栈、二叉树等
- 以考察常见算法思想基础的算法题,比如:动态规划、贪心、回溯等
- 基于某种场景包装下的1和2
提示:以下是本篇文章正文内容
内容
第一题:二叉搜索树的后序遍历序列
解题思路:首先你要了解二叉搜索树的特性和后序遍历,这个序列的最后一个节点就是根节点,再通过BST的特性把除最后一个节点外的序列分为两个序列然后再对这两序列进行递归。
OJ链接:第一题
public class Solution {
private boolean VerifySquenceOfBSTHelper(int[] sequence,int start,int end){
if(start >= end){
//所有序列找完了,证明全部符合条件,返回true
return true;
}
//最后一个为根节点
int root = sequence[end];
int i = start;
while(start < end && sequence[i] < root){
//确定前半段范围
i++;
}
//因为最后一个节点是根节点所以不能等于end
for(int j = i; j < end;j++){
//后半段如果有小于根节点的,则这段序列不合法
if(sequence[j] < root){
return false;
}
}
//再检测左子树右子树是否满足
return VerifySquenceOfBSTHelper(sequence,start,i-1) && VerifySquenceOfBSTHelper(sequence,i,end-1);
}
public boolean VerifySquenceOfBST(int [] sequence) {
//BST特性
if(sequence.length == 0){
return false;
}
return VerifySquenceOfBSTHelper(sequence,0,sequence.length-1);
}
}
第二题:二叉树中和为某一值的路径(二)
解题思路:首先咱们先了解回溯的过程以及和回溯相关的两个专有名词,结果集和待选结果
- 先添加值
- 判断现有结果是否满足条件,判断待选结果是否满足条件
- DFS
- 回溯
这题用回溯就很好解了,对二叉树进行深度优先搜索然后再回溯。
OJ链接:第二题
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 {
private void FindPathHelper(TreeNode root,int expectNumber,ArrayList<ArrayList<Integer>> ret,ArrayList<Integer> list){
if(root == null){
return;
}
//先添加值
list.add(root.val);
expectNumber -= root.val;
//判断待选结果是否满足条件
if(root.left == null && root.right == null && expectNumber == 0){
//注意深浅拷贝
ret.add(new ArrayList<Integer>(list));
}
//DFS 深度优先搜索
FindPathHelper(root.left,expectNumber,ret,list);
FindPathHelper(root.right,expectNumber,ret,list);
//回溯
list.remove(list.size()-1);
}
public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int expectNumber) {
//回溯
//结果集ArrayList<ArrayList<Integer>>
//待选结果ArrayList<Integer>
ArrayList<ArrayList<Integer>> ret = new ArrayList<>();
ArrayList<Integer> list = new ArrayList<>();
FindPathHelper(root,expectNumber,ret,list);
return ret;
}
}
第三题:字符串的排列
解题思路:把字符串以树型结构展示出来,然后用回溯写,不过这题回溯的形式是以交换的方式实现,先添加一个开始值然后循环也就是深度优先搜索然后交换也就是回溯。
OJ链接:第三题
import java.util.*;
public class Solution {
private void swap(char[] str,int start,int end){
char tmp = str[start];
str[start] = str[end];
str[end] = tmp;
}
private boolean IsExit(ArrayList<String> result,char[] str){
return result.contains(String.valueOf(str));
}
private void PermutationHelper(ArrayList<String> result,int start, char[] str){
//最后一个元素不用交换
if(start == str.length-1){
//去重 - 避免字符串全是相同元素
if(!IsExit(result,str)){
result.add(new String(str));
}
return;
}
for(int i = start; i < str.length; i++){
//循环就是以字符串的每个字符作为开始
//以当前start为开始,后序字符递归交换,得出不同排列组合
swap(str,start,i);
PermutationHelper(result,start+1,str);
swap(str,start,i);
}
}
public ArrayList<String> Permutation(String str) {
ArrayList<String> result = new ArrayList<>();
if(str != null && str.length() > 0){
PermutationHelper(result,0,str.toCharArray());
//字典序排序
Collections.sort(result);
}
return result;
}
}
第四题:最小的K个数
解题思路:这题很简单用一个只有k大小的最大堆来筛选元素就行了。当然也可以把数组排好序取前k个(不推荐)
OJ链接:第四题
import java.util.*;
public class Solution {
public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
//topK问题 - 最小堆
ArrayList<Integer> list = new ArrayList<>();
if(input == null || k <= 0 || input.length < k){
return list;
}
PriorityQueue<Integer> que = new PriorityQueue<>(k,Collections.reverseOrder());
for(int i = 0; i < input.length; i++){
if(i < k){
//没放满直接放
que.offer(input[i]);
}else{
//放满了比堆顶元素小的放进来
if(que.peek() > input[i]){
que.poll();
que.offer(input[i]);
}
}
}
for(int i = 0; i < k; i++){
list.add(que.poll());
}
return list;
}
}
总结
以上就是今天练习的内容,今天的题都比较难,最主要是理解回溯,以及回溯的使用场景,大多数情况都是结构决定算法,一般什么结构我们的思维就向其相关的算法延申,所以有时我们可以换一种结构来实现某题的数据解法也就又有新的理解。