【算法】Day06

努力经营当下,直至未来明朗!


普通小孩也要热爱生活!

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官方文档
⑤ 优先级队列参数:
0
⑥ 优先级队列默认是升序排列,但是大根堆是降序排列
⑦ 注意:最小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;
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

'Dream是普通小孩耶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值