第十八节:认识一些经典递归过程

一 暴力递归就是尝试

1,把问题转化为规模缩小了的同类问题的子问题

2,有明确的不需要继续进行递归的条件(base case)

3,有当得到了子问题的结果之后的决策过程

4,不记录每一个子问题的解

二 打印n层汉诺塔从最左边移动到最右边的全部过程

2.1 描述

打印n层汉诺塔从最左边移动到最右边的全部过程

2.1 分析

第一步

从以下三个步骤来进行移动,补齐相应的函数


 

第二步

leftToMid(n - 1);

其实就是定义六个过程

总结

2.3代码

package class17;

import java.util.HashSet;
import java.util.Stack;

public class Code02_Hanoi {

    public static void hanoi1(int n) {
        leftToRight(n);
    }

    // 请把1~N层圆盘 从左 -> 右
    public static void leftToRight(int n) {
        if (n == 1) { // base case
            System.out.println("Move 1 from left to right");
            return;
        }
        leftToMid(n - 1);
        System.out.println("Move " + n + " from left to right");
        midToRight(n - 1);
    }

    // 请把1~N层圆盘 从左 -> 中
    public static void leftToMid(int n) {
        if (n == 1) {
            System.out.println("Move 1 from left to mid");
            return;
        }
        leftToRight(n - 1);
        System.out.println("Move " + n + " from left to mid");
        rightToMid(n - 1);
    }

    public static void rightToMid(int n) {
        if (n == 1) {
            System.out.println("Move 1 from right to mid");
            return;
        }
        rightToLeft(n - 1);
        System.out.println("Move " + n + " from right to mid");
        leftToMid(n - 1);
    }

    public static void midToRight(int n) {
        if (n == 1) {
            System.out.println("Move 1 from mid to right");
            return;
        }
        midToLeft(n - 1);
        System.out.println("Move " + n + " from mid to right");
        leftToRight(n - 1);
    }

    public static void midToLeft(int n) {
        if (n == 1) {
            System.out.println("Move 1 from mid to left");
            return;
        }
        midToRight(n - 1);
        System.out.println("Move " + n + " from mid to left");
        rightToLeft(n - 1);
    }

    public static void rightToLeft(int n) {
        if (n == 1) {
            System.out.println("Move 1 from right to left");
            return;
        }
        rightToMid(n - 1);
        System.out.println("Move " + n + " from right to left");
        midToLeft(n - 1);
    }

    public static void hanoi2(int n) {
        if (n > 0) {
            func(n, "left", "right", "mid");
        }
    }

    public static void func(int N, String from, String to, String other) {
        if (N == 1) { // base
            System.out.println("Move 1 from " + from + " to " + to);
        } else {
            func(N - 1, from, other, to);
            System.out.println("Move " + N + " from " + from + " to " + to);
            func(N - 1, other, to, from);
        }
    }

    public static class Record {
        public int level;
        public String from;
        public String to;
        public String other;

        public Record(int l, String f, String t, String o) {
            level = l;
            from = f;
            to = t;
            other = o;
        }
    }

    // 之前的迭代版本,很多同学表示看不懂
    // 所以我换了一个更容易理解的版本
    // 看注释吧!好懂!
    // 你把汉诺塔问题想象成二叉树
    // 比如当前还剩i层,其实打印这个过程就是:
    // 1) 去打印第一部分 -> 左子树
    // 2) 打印当前的动作 -> 当前节点
    // 3) 去打印第二部分 -> 右子树
    // 那么你只需要记录每一个任务 : 有没有加入过左子树的任务
    // 就可以完成迭代对递归的替代了
    public static void hanoi3(int N) {
        if (N < 1) {
            return;
        }
        // 每一个记录进栈
        Stack<Record> stack = new Stack<>();
        // 记录每一个记录有没有加入过左子树的任务
        HashSet<Record> finishLeft = new HashSet<>();
        // 初始的任务,认为是种子
        stack.add(new Record(N, "left", "right", "mid"));
        while (!stack.isEmpty()) {
            // 弹出当前任务
            Record cur = stack.pop();
            if (cur.level == 1) {
                // 如果层数只剩1了
                // 直接打印
                System.out.println("Move 1 from " + cur.from + " to " + cur.to);
            } else {
                // 如果不只1层
                if (!finishLeft.contains(cur)) {
                    // 如果当前任务没有加入过左子树的任务
                    // 现在就要加入了!
                    // 把当前的任务重新压回去,因为还不到打印的时候
                    // 再加入左子树任务!
                    finishLeft.add(cur);
                    stack.push(cur);
                    stack.push(new Record(cur.level - 1, cur.from, cur.other, cur.to));
                } else {
                    // 如果当前任务加入过左子树的任务
                    // 说明此时已经是第二次弹出了!
                    // 说明左子树的所有打印任务都完成了
                    // 当前可以打印了!
                    // 然后加入右子树的任务
                    // 当前的任务可以永远的丢弃了!
                    // 因为完成了左子树、打印了自己、加入了右子树
                    // 再也不用回到这个任务了
                    System.out.println("Move " + cur.level + " from " + cur.from + " to " + cur.to);
                    stack.push(new Record(cur.level - 1, cur.other, cur.to, cur.from));
                }
            }
        }
    }

    public static void main(String[] args) {
        int n = 3;
        hanoi1(n);
        System.out.println("============");
        hanoi2(n);
        System.out.println("============");
        hanoi3(n);
    }

}

2.4 启发 将上面的六个过程进行抽象话 增加参数如下

上面的六个过程都可以用 from to other 来代替

第一步

第二步

   public static void hanoi2(int n) {
        if (n > 0) {
            func(n, "left", "right", "mid");
        }
    }
 //1-N 在 from
//        去:to 
//       宁一个 other    
    public static void func(int N, String from, String to, String other) {
        if (N == 1) { // base
            System.out.println("Move 1 from " + from + " to " + to);
        } else {
            func(N - 1, from, other, to);
            System.out.println("Move " + N + " from " + from + " to " + to);
            func(N - 1, other, to, from);
        }
    }

三 打印一个字符串的全部子序列(可以不连续的)

3.2 分析

3.3 代码

package class17;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

public class Code03_PrintAllSubsquences {

    // s -> "abc" ->
    public static List<String> subs(String s) {
        char[] str = s.toCharArray();
        String path = "";
        List<String> ans = new ArrayList<>();
        process1(str, 0, ans, path);
        return ans;
    }

    // str 固定参数
    // 来到了str[index]字符,index是位置
    // str[0..index-1]已经走过了!之前的决定,都在path上
    // 之前的决定已经不能改变了,就是path
    // str[index....]还能决定,之前已经确定,而后面还能自由选择的话,
    // 把所有生成的子序列,放入到ans里去
    public static void process1(char[] str, int index, List<String> ans, String path) {
        if (index == str.length) {
            ans.add(path);
            return;
        }
        // 没有要index位置的字符
        process1(str, index + 1, ans, path);
        // 要了index位置的字符
        process1(str, index + 1, ans, path + String.valueOf(str[index]));
    }

//打印一个字符串的全部子序列,要求不要出现重复字面值的子序列
    public static List<String> subsNoRepeat(String s) {
        char[] str = s.toCharArray();
        String path = "";
        HashSet<String> set = new HashSet<>();
        process2(str, 0, set, path);
        List<String> ans = new ArrayList<>();
        for (String cur : set) {
            ans.add(cur);
        }
        return ans;
    }

    public static void process2(char[] str, int index, HashSet<String> set, String path) {
        if (index == str.length) {
            set.add(path);
            return;
        }
        String no = path;
        process2(str, index + 1, set, no);
        String yes = path + String.valueOf(str[index]);
        process2(str, index + 1, set, yes);
    }

    public static void main(String[] args) {
        String test = "acccc";
        List<String> ans1 = subs(test);
        List<String> ans2 = subsNoRepeat(test);

        for (String str : ans1) {
            System.out.println(str);
        }
        System.out.println("=================");
        for (String str : ans2) {
            System.out.println(str);
        }
        System.out.println("=================");

    }

}

3.4 打印一个字符串的全部子序列,要求不要出现重复字面值的子序列

    public static List<String> subsNoRepeat(String s) {
        char[] str = s.toCharArray();
        String path = "";
        HashSet<String> set = new HashSet<>();
        process2(str, 0, set, path);
        List<String> ans = new ArrayList<>();
        for (String cur : set) {
            ans.add(cur);
        }
        return ans;
    }

    public static void process2(char[] str, int index, HashSet<String> set, String path) {
        if (index == str.length) {
            set.add(path);
            return;
        }
        String no = path;
        process2(str, index + 1, set, no);
        String yes = path + String.valueOf(str[index]);
        process2(str, index + 1, set, yes);
    }

四 打印一个字符串的全部排列

4.1 描述

4.2 分析

for 循环里面是跑支路,每到下一个支路的时候都要将之前删除的加回来,恢复现场

4.3 分析二

4.4代码

package class17;

import java.util.ArrayList;
import java.util.List;

public class Code04_PrintAllPermutations {

    public static List<String> permutation1(String s) {
        List<String> ans = new ArrayList<>();
        if (s == null || s.length() == 0) {
            return ans;
        }
        char[] str = s.toCharArray();
        ArrayList<Character> rest = new ArrayList<Character>();
        for (char cha : str) {
            rest.add(cha);
        }
        String path = "";
        f(rest, path, ans);
        return ans;
    }

    public static void f(ArrayList<Character> rest, String path, List<String> ans) {
        if (rest.isEmpty()) {
            ans.add(path);
        } else {
            int N = rest.size();
            for (int i = 0; i < N; i++) {
                char cur = rest.get(i);
                rest.remove(i);
                f(rest, path + cur, ans);
                rest.add(i, cur);
            }
        }
    }

    public static List<String> permutation2(String s) {
        List<String> ans = new ArrayList<>();
        if (s == null || s.length() == 0) {
            return ans;
        }
        char[] str = s.toCharArray();
        g1(str, 0, ans);
        return ans;
    }
分析二交换的代码
    public static void g1(char[] str, int index, List<String> ans) {
        if (index == str.length) {
            ans.add(String.valueOf(str));
        } else {
            for (int i = index; i < str.length; i++) {
                swap(str, index, i);
                g1(str, index + 1, ans);
                swap(str, index, i);
            }
        }
    }

    public static List<String> permutation3(String s) {
        List<String> ans = new ArrayList<>();
        if (s == null || s.length() == 0) {
            return ans;
        }
        char[] str = s.toCharArray();
        g2(str, 0, ans);
        return ans;
    }

    public static void g2(char[] str, int index, List<String> ans) {
        if (index == str.length) {
            ans.add(String.valueOf(str));
        } else {
      //4.4 打印一个字符串的全部排列,要求不要出现重复的排列
            boolean[] visited = new boolean[256];//去除重复的
            for (int i = index; i < str.length; i++) {
                if (!visited[str[i]]) {
                    visited[str[i]] = true;
                    swap(str, index, i);
                    g2(str, index + 1, ans);
                    swap(str, index, i);
                }
            }
        }
    }

    public static void swap(char[] chs, int i, int j) {
        char tmp = chs[i];
        chs[i] = chs[j];
        chs[j] = tmp;
    }

    public static void main(String[] args) {
        String s = "acc";
        List<String> ans1 = permutation1(s);
        for (String str : ans1) {
            System.out.println(str);
        }
        System.out.println("=======");
        List<String> ans2 = permutation2(s);
        for (String str : ans2) {
            System.out.println(str);
        }
        System.out.println("=======");
        List<String> ans3 = permutation3(s);
        for (String str : ans3) {
            System.out.println(str);
        }

    }

}

4.4 打印一个字符串的全部排列,要求不要出现重复的排列

4.4.1 分析 判断每一个支路看这个字符是否试过了

4.4.2 代码

public static List<String> permutation3(String s) {
        List<String> ans = new ArrayList<>();
        if (s == null || s.length() == 0) {
            return ans;
        }
        char[] str = s.toCharArray();
        g2(str, 0, ans);
        return ans;
    }

    public static void g2(char[] str, int index, List<String> ans) {
        if (index == str.length) {
            ans.add(String.valueOf(str));
        } else {
            boolean[] visited = new boolean[256];
            for (int i = index; i < str.length; i++) {
                if (!visited[str[i]]) {
                    visited[str[i]] = true;
                    swap(str, index, i);
                    g2(str, index + 1, ans);
                    swap(str, index, i);
                }
            }
        }
    }

五 仰望好的尝试?这个题练递归特别好

5.1 描述

给你一个栈,请你逆序这个栈,

不能申请额外的数据结构,

只能使用递归函数。 如何实现?

5.2 分析

5.3 代码

package class17;

import java.util.Stack;

public class Code05_ReverseStackUsingRecursive {

    public static void reverse(Stack<Integer> stack) {
        if (stack.isEmpty()) {
            return;
        }
        int i = f(stack);
        reverse(stack);
        stack.push(i);
    }

    // 栈底元素移除掉
    // 上面的元素盖下来
    // 返回移除掉的栈底元素
    public static int f(Stack<Integer> stack) {
        int result = stack.pop();
        if (stack.isEmpty()) {
            return result;
        } else {
            int last = f(stack);
            stack.push(result);
            return last;
        }
    }

    public static void main(String[] args) {
        Stack<Integer> test = new Stack<Integer>();
        test.push(1);
        test.push(2);
        test.push(3);
        test.push(4);
        test.push(5);
        reverse(test);
        while (!test.isEmpty()) {
            System.out.println(test.pop());
        }

    }

}

5.4 不用递归代码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/665224.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【STM32】定时器与PWM的LED控制

目录 一、要求二、定时器中断与PWM介绍1、定时器类型2、定时器中断基本结构3、预分频器时序与计数器时序&#xff08;1&#xff09;预分频器时序&#xff08;2&#xff09;计数器时序 3、PWM&#xff08;1&#xff09;简介&#xff08;2&#xff09;输出比较模式&#xff08;3&…

密闭空间作业应如何做好安全防护?

在现代工业与日常工作中&#xff0c;密闭空间作业已逐渐成为许多行业不可或缺的一部分。然而&#xff0c;这些看似寻常的空间却隐藏着诸多不为人知的风险。从窒息性气体到易燃易爆物质&#xff0c;从物理性危险到心理压力&#xff0c;每一项都足以威胁到作业人员的生命安全。因…

【Qt】【模型-视图架构】代理模型示例

文章目录 1. 基本排序/过滤模型Basic Sort/Filter Model Example2. 自定义排序/过滤模型Custom Sort/Filter Model ExampleFilterLineEdit类定义及实现MySortFilterProxyModel类定义及实现 1. 基本排序/过滤模型Basic Sort/Filter Model Example 官方提供的基本排序/过滤模型示…

004 仿muduo实现高性能服务器组件_Buffer模块与Socket模块的实现

​&#x1f308;个人主页&#xff1a;Fan_558 &#x1f525; 系列专栏&#xff1a;仿muduo &#x1f339;关注我&#x1f4aa;&#x1f3fb;带你学更多知识 文章目录 前言Buffer模块Socket模块 小结 前言 这章将会向你介绍仿muduo高性能服务器组件的buffer模块与socket模块的实…

打造你的首个QT 5计算器应用

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、引言&#xff1a;QT 5的力量与我们的计算器 二、QT 5基础&#xff1a;理解UI设计与文件…

mac安装allure及allure:command not fund问题解决

一、下载 下载连接&#xff1a;https://github.com/allure-framework/allure2/releases 选择任意压缩包进行下载 二、解压 解压后是一个文件夹 三、打开终端 # bash终端 vim ~/.bash_profile # zsh终端 vim ~/.zshrc四、配置环境变量 export PATH/usr/bin:/bin:/usr/sb…

「浏览器」服务端渲染

前言 服务端渲染&#xff08;Server-Side Rendering&#xff0c;SSR&#xff09;是一种常见于网页应用的技术&#xff0c;它指的是在服务器上将网页的内容生成&#xff0c;然后发送完整的HTML页面到客户端的浏览器的过程。这与传统的客户端渲染&#xff08;Client-Side Render…

LC 旋转 - 模拟对象

原文链接 链接 液晶 (LC) 旋转网格属性允许您以 theta、phi 为单位指定空间变化的 LC 导向。 液晶由杆状分子结构组成&#xff0c;这些分子结构具有相对于长轴的旋转对称性。因此&#xff0c;液晶具有空间变化的单轴光学特性。 相对于分子长轴和分子短轴的折射率称为非寻常 ne …

STM32使用ST-LINK下载程序中需要注意的几点

使用keil5的ST-link下载界面 前提是ST-LINK已经连接好&#xff0c;&#xff08;下图中是没有连接ST-link设备&#xff09;&#xff0c;只是为了展示如何查看STlink设备是否连接的方式 下载前一定设置下载完成后自启动 这个虽然不是必须&#xff0c;但对立即看到新程序的现象…

python爬取每日天气情况

python爬取每日天气情况 一、项目简介二、完整代码一、项目简介 本次爬取的目标数据来源于天气网,数据所在的页面如下图所示,本次任务较为简单,按照正常操作流程操作即可,即抓包分析数据接口,发送请求获取数据,解析数据并持久化存储。发送请求使用requests库,解析数据使…

2.11 下载安装Oracle数据库(Win10 JP)

2.11 下载安装Oracle数据库 目录一、下载Oracle 11g客户端安装包1. 官网选数据库配置2. Oracle下载器 下载安装包 二、 安装Oracle 11g客户端1. 安装 Oracle Database Client 11.2.0.1.02. 安装 Oracle Database 11.2.0.4.0 for HP OpenVMS Itanium 三、下载Oracle 12c数据库安…

三相智能电表通过Modbus转Profinet网关与PLC通讯案例

Modbus转Profinet网关&#xff08;XD-MDPN100/300&#xff09;的主要功能是实现Modbus协议和Profinet协议之间的转换和通信。Modbus转Profinet网关集成了Modbus和Profinet两种协议&#xff0c;支持Modbus RTU主站/从站&#xff0c;并可以与RS485接口的设备&#xff0c;它自带网…

C盘文件被格式化了,要怎么恢复?

C盘通常是操作系统(如Windows)的默认安装目录。它包含了操作系统的核心文件、驱动程序及系统所需的各种支持文件。这些文件对于计算机的正常运行至关重要。但在使用的过程中&#xff0c;有时可能会因为各种原因导致C盘被格式化&#xff0c;从而丢失了这些重要文件。这无疑是一个…

定个小目标之每天刷LeetCode热题(5)

今天这是一道简单题&#xff0c;解决方法是递归&#xff0c;遍历二叉树最简便的方法就是递归了&#xff0c;我们以递归三大要素对此题进行简单分析一下 第一要素&#xff1a;明确你这个函数想要干什么 在这道题中&#xff0c;我们需要定义一个传入任意二叉树根节点可以左右翻…

【移动端】商场项目路由设计

1&#xff1a;路由设计配置&#xff1a; 一级路由配置 分析项目&#xff0c;设计路由&#xff0c;配置一级路由 一级路由&#xff1a;单个页面独立展示的&#xff0c;都是一级路由&#xff0c;例如&#xff1a;登录页面&#xff0c;首页架子&#xff0c;报错页面 二级路由&…

Java项目对接redis,客户端是选Redisson、Lettuce还是Jedis?

JAVA项目对接redis&#xff0c;客户端是选Redisson、Lettuce还是Jedis&#xff1f; 一、客户端简介1. Jedis介绍2. Lettuce介绍3. Redisson介绍 二、横向对比三、选型说明 在实际的项目开发中&#xff0c;对于一个需要对接Redis的项目来说&#xff0c;就面临着选择合适的Redis客…

基于Vue+Node.js的购物网站设计与实现-计算机毕业设计源码28500

摘 要 近年来&#xff0c;随着移动互联网的快速发展&#xff0c;电子商务越来越受到网民们的欢迎&#xff0c;电子商务对国家经济的发展也起着越来越重要的作用。简单的流程、便捷可靠的支付方式、快捷畅通的物流快递、安全的信息保护都使得电子商务越来越赢得网民们的青睐。现…

将局部变量指针传递给某个c++类,离开类时数据发生变化

最近遇到一个c的问题&#xff0c;将一个局部变量的值传递给某个类&#xff0c;类中没有对该数据进行任何显式修改&#xff0c;结果该变量的值发生变化并且不可访问。 我开始很奇怪为何会发生这样的事情&#xff0c;后来经过调试&#xff0c;发现原来是该类发生了异常&#xff…

迅为RK3562开发板专为3562编写10大分类2900+页文档

iTOP-3562开发板采用瑞芯微RK3562处理器&#xff0c;内部集成了四核A53Mali G52架构&#xff0c;主频2GHZ&#xff0c;内置1TOPSNPU算力&#xff0c;RK809动态调频。支持OpenGLES1.1/2.0/3.2、0penCL2.0、Vulkan 1.1内嵌高性能2D加速硬件。 内置独立NPU, 算力达 1TOPS,可用于轻…

中学生学人工智能系列:如何用AI学化学

经常有读者朋友给公众号《人工智能怎么学》留言咨询如何使用人工智能学习语文、数学、英语、化学等科目。这些都是中学教师、中学生朋友及其家长们普遍关注的问题。仅仅使用留言回复的方式&#xff0c;不可能对这些问题做出具体和透彻的解答&#xff0c;因此本公众号近期将推出…