Java学习笔记(十一):面向对象实战:对象设计、二分搜索、0/1背包问题(回溯法,剪枝 \ 动态规划)

面向对象实战

编程要提高效率
如:0-100的求和问题,最简单的可以用一个for循环解决。
但如果用数学上总结的高斯求和(1+100) * 50,会更快更有效率。

对象设计(面向对象、多态运用)

1、设计一个Person抽象类,包含吃饭运动学习三种运动,分为工人、学生、老师三种职业
2、设计一个接口考试,只有老师和学生会考试(只有老师和学生才实现这个接口)
3、设计一个方法,模拟让人类进入考场,要求只有会考试的人才能进入,并且考试

1、设计一个Person抽象类,包含吃饭运动学习三种运动,分为工人、学生、老师三种职业
在这里插入图片描述

package Person;

import java.sql.PseudoColumnUsage;

public abstract class Person {               //设计一个Person抽象类,里面包含吃饭、运动、学习三种行为
    private String name;
    private int age;

    public Person(String name, int age){
        this.name = name;
        this.age = age;
    }

    public abstract void eat();
    public abstract void sport();
    public abstract void study();
}

三者差不多,就列一个吧

package Person;

public class Student extends Person{
    public Student(String name, int age){
        super(name, age);
    }

    @Override
    public void eat(){
        System.out.println("有些人多,有些人少");
    }

    @Override
    public void sport(){
        System.out.println("有些人大,有些人小");
    }

    @Override
    public void study(){
        System.out.println("读书是必须的");
    }


}

2、设计一个接口考试,只有老师和学生会考试(只有老师和学生才实现这个接口)
在这里插入图片描述
在老师和学生类中添加接口和方法的实现

package Person;

import Test.*;

public class Student extends Person implements Test{
    public Student(String name, int age){
        super(name, age);
    }

    @Override
    public void eat(){
        System.out.println("有些人多,有些人少");
    }

    @Override
    public void sport(){
        System.out.println("有些人大,有些人小");
    }

    @Override
    public void study(){
        System.out.println("读书是必须的");
    }


    @Override
    public void test(){
        System.out.println("我会考试");
    }
}

3、设计一个方法,模拟让人类进入考场,要求只有会考试的人才能进入,并且考试

import Person.Person;
import Test.*;
import Person.*;

public class Main {
    public static void main(String[] args) {
        Worker worker1 = new Worker("工人1", 30);
        test(worker1);

        Teacher teacher1 = new Teacher("王老师", 35);
        test(teacher1);

        Student student1 = new Student("施同学", 23);
        test(student1);
    }

    private static void test(Person person){
        if(person instanceof Test){                 //如果实现了该接口
            Test e = (Test) person;                 //强制类型转换,因为肯定是exam的实现
            e.test();
        } else {
            System.out.println("没有考试接口");
        }
    }
}


二分搜索

现在有一个有序数组(从小到大,数组长度0<n<1000000)如何快速寻找我们想要的数在哪个位置,如果存在请返回下标,不存在返回-1即可。

思路梳理
1、从中间开始找,跟两边对比,确定下一个二分的方向。
2、截断原来数组的一般,再重复1的步骤。
由于循环有对半的过程,因此时间复杂度有O(logn)

public class Main {
    public static void main(String[] args) {
        int[] arr = new int[]{1, 4, 5, 6, 7, 10, 12, 14, 20, 26};

        System.out.println(binary_search(arr, 10));
        System.out.println(binary_search(arr, 11));
    }

    public static int binary_search(int[] arr, int target){
        int start = 0, end = arr.length - 1;            //定义范围的起始和终点位置
        while(end >= start){                            //设定继续寻找的条件
            int mid = (start + end + 1) / 2;            //这里+1是为了防止奇数时找到中间前一位,偶数+1不影响。
            if(arr[mid] == target) return mid;
            if(arr[mid] < target) start = mid + 1;
            if(arr[mid] > target) end = mid - 1;
        }
        return -1;      //没找到的情况
    }
}

快速排序

快速排序其实是一种排序执行效率很高的排序算法,它利用分治法来对待排序序列进行分治排序,它的思想主要是通过一趟排序记录分隔成独立的两部分,其中的一部分比关键字小,后面一部分比关键字大,然后再对这前后的两部分分别采用这种方式进行排序,通过递归的运算最终达到整个序列有序。
递归方法:

public class Main {
    public static void main(String[] args) {
        System.out.println(test(100));
    }

    static int test(int i){
        if (i==0) return 0;
        return test(i-1) + i;
    }
}

就是从100进去,然后到第二个return,进入第二次方法,…,最后倒数第一个方法返回了0,那么倒数第二个就是返回0+1,倒数第三个就是1+2,…,慢慢再把数值返回到最外层。

快速排序思路:
先贴一个图解链接快速排序图解
再加上自己总结的内容:
1、定义left和right索引定位
2、拿到第一个位置的数作为基准
3、从right向左开始和基准做比较,出现第一个right比基准小的,那么就把right对应的索引空出来作为坑,对应位置移到第一个基准的索引处。把
4、再从left向右开始和基准比较,出现第一个left比基准大的,就把left对应的索引空出来作为坑,对应的数放到3中的坑中。
5、重复步骤直到left=right后,这么一趟排序就完成了。数组就分成了三部分,左边是比基准小的,右边是比基准大的,中间是基准。
6、对分好的两个区域重复3、4、5步。

需要的东西:基准元素,左右断点、基准数。

import javax.swing.event.MenuDragMouseListener;

public class Main {
    public static void main(String[] args) {
        int[] arr = new int[]{8, 2, 3, 5, 4, 7, 9, 1, 0};
        fast_ordering(arr, 0, arr.length - 1);
        for(int i = 0; i < arr.length; i++){
            System.out.print(arr[i] + " ");
        }
    }

    static void fast_ordering(int[] arr, int start, int end){
        if (start >= end) return;              //设置结束条件
        int base = arr[start];                  //基准
        int left = start;
        int right = end;
        while(left < right){
            while(left < right && base <= arr[right]) right--;
            arr[left] = arr[right];             //把对应位置的坑让给他

            while(left < right && base >= arr[left]) left++;
            arr[right] = arr[left];
        }

        arr[left] = base;          //一趟排完
        fast_ordering(arr, start, right - 1);       //递归前一部分
        fast_ordering(arr, right + 1, end);         //递归后一部分

    }
}

0/1背包问题

给定n件五排,每一个物品的重量为w[n],每个物品的价值为v[n]。现挑选物品放入背包中,假定背包能承受的最大重量为capacity, 求装入物品的最大价值是多少?

第一种方法:回溯法、剪枝
回溯法:
先选A,再分叉B,C,再分叉…
在这里插入图片描述
由于存在capacity,因此有些会走不下去,所以要进行剪枝处理。
在这里插入图片描述
代码段:

public class Main {

    static int[] w = {2, 3, 4, 5};              //static静态变量能够省去new实例化对象过程 weight
    static int[] v = {3, 4, 5, 6};              //value
    static int capacity = 8;

    public static void main(String[] args) {
        System.out.println(test(0, 0));                 //开始的下标、最开始的重量

    }

    static int test(int index, int weight){
        if(index >= 4) return 0;                        //结设置递归束条件
        if(capacity < weight + w[index]) return 0;

        return Math.max(v[index] + test(index + 1, weight + w[index]), test(index + 1, weight));
        //使用Math.max选两者之中大的,第一个是选这个分支,加上重量往下走,第二个是不选这个分支,不加这个重量往下走。
        //
    }
}

第二种方法:动态规划
在这里插入图片描述

其实就是从下往上写
当第三行的剩余7开始,就可以看到3和4的组合9比单个的4大,所以要换。
代码段为了方便,表就是从上往下写。

public class Main {

    static int[] w = {2, 3, 4, 5};              //static静态变量能够省去new实例化对象过程 weight
    static int[] v = {3, 4, 5, 6};              //value
    static int capacity = 8;

    public static void main(String[] args) {
        //System.out.println(test(0, 0));                 //开始的下标、最开始的重量
        dynamic_arranging();

    }

    static int test(int index, int weight){
        if(index >= 4) return 0;                        //结设置递归束条件
        if(capacity < weight + w[index]) return 0;

        return Math.max(v[index] + test(index + 1, weight + w[index]), test(index + 1, weight));
        //使用Math.max选两者之中大的,第一个是选这个分支,加上重量往下走,第二个是不选这个分支,不加这个重量往下走。
        //
    }

    static void dynamic_arranging(){
        int[][] arr = new int[5][capacity + 1];                 //创建表, 第一维为n+1,多一排0以防数组越界。
        for (int i = 1; i <= 4; i++){                       //开始填表 注意i从1开始,所以i-1是当前位置。
            for (int j = 1; j <= capacity; j++){
                if(w[i - 1] <= j) {
                    arr[i][j] = Math.max(arr[i - 1][j], arr[i - 1][j - w[i - 1]]+ v[i - 1]);     //判断是原来的大还是当前的加上上一行中剩余空间的价值加上  关键行
                }else{
                    arr[i][j] = arr[i - 1][j];
                }
            }
        }

        for (int i = 0; i < arr.length; i++){
            for (int j = 0; j < arr[i].length; j ++){
                System.out.print(arr[i][j] + " ");
            }
            System.out.println();
        }
    }
}

这一块确实有点绕,不理解的话可以在关键那一行设置个中断,带入一个具体值去算一下就理解了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值