【算法】算法模板

文章目录

  • 一、二分查找(BinarySearch)
    • 约定
    • 模板 1:while (left <= right)
    • 模板 2:while (left < right),推荐使用
    • 示例
  • 二、广度优先搜索(Breadth First Search,BFS)
    • 示例
  • 三、深度优先搜索(Depth First Search,DFS)
    • 示例

一、二分查找(BinarySearch)

详细请查看:【算法】二分查找(Java 版)
时间复杂度:每次折半查找,所以时间复杂度为 O(logN)
空间复杂度:只使用了 3 个变量,所以空间复杂度为 O(1)

约定

nums 为一个升序数组,我们把待查找区间的左边界下标设为 left,右边界下标设为 right,中间位置下标设为 mid

模板 1:while (left <= right)

思路:在循环体内部查找元素(解决简单问题时有用),即考虑下一轮目标元素应该在哪个区间。

	/**
	 * 二分查找法
*
  • 模板 1:while (left <= right)

  • * @param nums 待查找数组 * @param target 待查找目标值 * @return 目标值在数组中的下标
    * 未查找到就返回 -1 */
    public static int binarySearch1(int[] nums, int target) { // 特殊用例判断 int len = nums.length; if (len == 0) { return -1; } // 在 [left, right] 区间里查找 target int left = 0; int right = len - 1; while (left <= right) { // 为了防止 left + right 整形溢出,写成如下形式 int mid = left + (right - left) / 2; // 找到目标值,直接返回 if (nums[mid] == target) { return mid; } else if (nums[mid] > target) { // [mid, right]都大于目标值,下一轮查找区间:[left, mid - 1] right = mid - 1; } else { // 此时:nums[mid] < target // [left, mid]都小于目标值,下一轮查找区间:[mid + 1, right] left = mid + 1; } } return -1; }

    模板 2:while (left < right),推荐使用

    思路:在循环体内部排除元素(解决复杂问题时非常有用),即考虑中间元素 nums[mid] 在什么情况下不是目标元素。

    	/**
    	 * 二分查找法
    *
  • 模板 2(下取整):while (left < right)

  • * @param nums 待查找数组 * @param target 待查找目标值 * @return 目标值在数组中的下标
    * 未查找到就返回 -1 */
    public static int binarySearch2_floor(int[] nums, int target) { // 特殊用例判断 int len = nums.length; if (len == 0) { return -1; } // 在 [left, right] 区间里查找 target int left = 0; int right = len - 1; while (left < right) { // 选择中位数时下取整 int mid = left + (right - left) / 2; if (nums[mid] < target) { // [left, mid]都小于目标值,下一轮查找区间是 [mid + 1, right] left = mid + 1; } else { // 此时:nums[mid] >= target // [mid, right]都大于等于目标值,下一轮查找区间是 [left, mid] right = mid; } } // 退出循环的时候 left == right,程序只剩下一个元素没有看到。 // 视情况,是否需要单独判断 left(或者 right)这个下标的元素是否符合题意。 return nums[left] == target ? left : -1; }

    示例

    【LeetCode】1095. 山脉数组中查找目标值(二分查找)

    二、广度优先搜索(Breadth First Search,BFS)

    思路:类似于树的按层次遍历的过程,使用队列保存未被检测的节点,节点按照由近及远的次序被检测和进出队列。

    广度优先搜索就是把一些问题抽象成图,从一个点开始,向四周开始扩散(齐头并进),可以回答两类问题:

    • 从节点 A 出发,有前往节点 B 的路径吗(连通性)?
    • 从节点 A 出发,前往节点 B 的哪条路径最短(最优解)?
    public class TreeNode {
        public int val;
        public TreeNode left;
        public TreeNode right;
    
        public TreeNode(int x) {
            val = x;
        }
    }
    
    public class BFS {
    
        public static void main(String[] args) {
    
            TreeNode root = new TreeNode(1);
            root.left = new TreeNode(2);
            root.right = new TreeNode(3);
            root.left.left = new TreeNode(4);
    
            int target = 4;
    
            System.out.println(bfs(root, target));
        }
    
        /**
         * BFS 遍历二叉树
         * @param root 二叉树根节点
         * @param target 目标值
         * @return 返回目标值距根节点的最短距离
         */
        public static int bfs(TreeNode root, int target) {
    
            // BFS 使用队列
            Queue<TreeNode> q = new LinkedList<>();
            // 访问标志,避免走回头路
            Set<TreeNode> visited = new HashSet<>();
    
            // 初始节点放入队列 q
            q.offer(root);
            visited.add(root);
            // 记录扩散的步数
            int step = 0;
    
            while (!q.isEmpty()) {
                int size = q.size();
                // 将当前队列中的所有节点向四周扩散
                for (int i = 0; i < size; i++) {
                    TreeNode cur = q.poll();
                    // 注意:判断是否到达终点
                    if (cur.val == target)
                        return step;
                    // 将与 cur 相邻且未被访问的节点加入队列
                    // 对于二叉树,就是左右子节点;
                    // 对于二维数组,就是未越界的上下左右 4 个元素。
                    if (cur.left != null) {
                        if (!visited.contains(cur.left)) {
                            q.offer(cur.left);
                            visited.add(cur.left);
                        }
                    }
                    if (cur.right != null) {
                        if (!visited.contains(cur.right)) {
                            q.offer(cur.right);
                            visited.add(cur.right);
                        }
                    }
                }
    
                // 划重点:更新步数在这里
                step++;
            }
    
            return step;
        }
    }
    

    示例

    【LeetCode】200. 岛屿数量(BFS | DFS)

    三、深度优先搜索(Depth First Search,DFS)

    思路:类似于树的先根遍历,使用保存未被检测的节点,节点按照深度优先的次序被访问并依次被压入栈中,并以相反的次序出栈进行新的检测。

    深度优先搜索是一条路径走到底(不撞南墙不回头),可以回答:

    • 从节点 A 出发,有前往节点 B 的路径吗(连通性)?

    深度优先搜索实际上采用的就是回溯算法
    解决一个回溯问题,实际上就是一个决策树的遍历过程。你只需要思考 3 个问题:
    1、选择路径:已经做出的选择;
    2、选择列表:你当前可以做的选择;
    3、结束条件:到达决策树底层,无法再做选择的条件。

    public class Permute_46 {
    
        private List<List<Integer>> res = new LinkedList<>();
    
        public List<List<Integer>> permute(int[] nums) {
    
            // 记录路径
            LinkedList<Integer> track = new LinkedList<>();
    
            backTrack(nums, track);
    
            return res;
        }
    
        /**
         * DFS 全排列
         * 
  • 选择路径:记录在track中

  • *
  • 选择列表:nums中不存在于track中的那些元素

  • *
  • 结束条件:nums中的元素都在track中出现

  • * * @param nums 待排列数组 * @param track 选择路径 */
    public void backTrack(int[] nums, LinkedList<Integer> track) { // 触发结束条件(找到一种排列) if (track.size() == nums.length) { res.add(new LinkedList<Integer>(track)); return; } for (int i = 0; i < nums.length; i++) { // 排除不合法的选择(选择的元素不能重复) // contains 的时间复杂度是 O(N),可以通过访问数组用空间换时间 if (track.contains(nums[i])) { continue; } // 做选择 track.add(nums[i]); // 进入下一层决策树 backTrack(nums, track); // 撤销选择 track.removeLast(); } } }

    示例

    【LeetCode】200. 岛屿数量(BFS | DFS)

    你可能感兴趣的:(Java,算法,LeetCode)