经典算法

经典算法

背包问题系列-基础背包问题

package leetcode.dsaa.classical;

import leetcode.dsaa.classical.utils.Util;

/**
 * @author Pillar
 * @version 1.0
 * @date 2022/9/17 13:01
 * 经典背包问题
 */
public class KnapsackProblem {
    public static void main(String[] args) {
        System.out.println("Answer: "+ packageProblem1());
        System.out.println("Answer: "+ packageProblem2());
    }

    //基于更定的容量和价值的背包问题
    public static int packageProblem1() {
        //包装最大可装重量
        int packageContainWeight = 4;
        //三个物品的重量
        int[] weight = {1, 4, 2};
        //三个物品的价值
        int[] value = {150, 300, 200};
        //创建dp数组
        //其中i-1相当于weight或者value数组中的第一个数组,存入dp的时候,第一行和第一列是不存的,所以一开始
        //创建的时候长度多+1
        int[][] dp = new int[weight.length + 1][packageContainWeight + 1];
        for (int i = 1; i <= weight.length; i++) {
            for (int j = 1; j <= packageContainWeight; j++) {
                if (j >= weight[i - 1]) {
                    dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weight[i - 1]] + value[i - 1]);
                } else {
                    dp[i][j] = dp[i - 1][j];
                }
            }
        }
        Util.pringTwoIntArrays(dp);
        return dp[weight.length][packageContainWeight];
    }
    //《空间优化》,因为bp二维数组,每次用到的只是上一行的值,所以改为一维数组即可
    public static int packageProblem2() {
        //包装最大可装重量
        int packageContainWeight = 4;
        //三个物品的重量
        int[] weight = {1, 4, 2};
        //三个物品的价值
        int[] value = {150, 300, 200};
        //创建dp数组
        //其中i-1相当于weight或者value数组中的第一个数组,存入dp的时候,第一行和第一列是不存的,所以一开始
        //创建的时候长度多+1
        int[] dp = new int[packageContainWeight + 1];
        for (int i = 1; i <= weight.length; i++) {
            for (int j = packageContainWeight; j >= 1; j--) {
                if (j >= weight[i-1]) {
                    dp[j] = Math.max(dp[j], dp[j - weight[i - 1]] + value[i - 1]);
                }
            }
        }
        Util.pringIntArrays(dp);
        return dp[packageContainWeight];
    }
}

约瑟夫环

package leetcode.dsaa.classical;

import leetcode.dsaa.classical.utils.Util;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;

/**
 * @author Pillar
 * @version 1.0
 * @date 2022/9/17 17:51
 */
//约瑟夫环
    /*
        一圈人,数到第k个人就死亡,如果人数小于k就不用再死了

     */
public class JosephRing {
    public static void main(String[] args) {
        System.out.println("数组的实现:");
        Integer[] integers = solution3(7, 3);
        Util.pringIntegerArrays(integers);
        solution4(7, 3);
    }

    //数组实现
    public static Integer[] solution1(int count, int k) {
        //首先定义一个数组, 方便最后返回
        Integer[] live = new Integer[Math.min(count, k - 1)];
        //全活
        if (count < k) {
            int index = 0;
            while (index < count) {
                live[index++] = index;
            }
            return live;
        }
        ArrayList<Integer> arrayList = new ArrayList<>();
        for (int i = 1; i <= count; i++) {
            arrayList.add(i);
        }
        int number = 0;
        int pointer = 0;
        while (arrayList.size() >= k) {
            number++;
            if (pointer >= arrayList.size()) {
                pointer = 0;
            }
            if (number % k == 0) {
                arrayList.remove(pointer);
                continue;
            }
            pointer++;
        }
        return arrayList.toArray(live);
    }

    //上面的数组实现,频繁删除数组,造成元素移动,我们可以把数组中删除的元素用一个负数来填充。
    public static Integer[] solution2(int count, int k) {
        //首先定义一个数组, 方便最后返回
        Integer[] live = new Integer[Math.min(count, k - 1)];
        //全活
        if (count < k) {
            int index = 0;
            while (index < count) {
                live[index++] = index;
            }
            return live;
        }
        ArrayList<Integer> arrayList = new ArrayList<>();
        for (int i = 1; i <= count; i++) {
            arrayList.add(i);
        }
        int number = 0;
        int pointer = 0;
        //以删除的元素个数做为结束条件
        int n = count - k + 1;
        while (n > 0) {
            if (pointer >= arrayList.size()) {
                pointer = 0;
            }
            if (arrayList.get(pointer) < 0) {
                pointer++;
                continue;
            }
            number++;
            if (number % k == 0) {
                arrayList.set(pointer, -1);
                n--;
                continue;
            }
            pointer++;
        }
        int index = 0;
        for (Integer i : arrayList) {
            if (i != -1) {
                live[index++] = i;
            }
        }
        return live;
    }

    //使用链表实现,速度是要比数组快的
    public static Integer[] solution3(int count, int k) {
        //首先定义一个数组, 方便最后返回
        Integer[] live = new Integer[Math.min(count, k - 1)];
        //全活
        if (count < k) {
            int index = 0;
            while (index < count) {
                live[index++] = index;
            }
            return live;
        }
        LinkedList<Integer> linkedList = new LinkedList<>();
        for (int i = 1; i <= count; i++) {
            linkedList.addLast(i);
        }
        int number = 0;
        int pointer = 0;
        while (linkedList.size() >= k) {
            number++;
            if (pointer >= linkedList.size()) {
                pointer = 0;
            }
            if (number % k == 0) {
                linkedList.remove(pointer);
                continue;
            }
            pointer++;
        }
        return linkedList.toArray(live);
    }

    //使用队列实现
    static class MyQueue {
        // 队列的长度
        int maxSize;
        // 数组保存队列
        int[] arr;
        // 队首元素索引
        int front;
        // 队尾元素的下一个索引
        int rear;

        // 初始化队列
        public MyQueue(int maxSize) {
            this.maxSize = maxSize;
            arr = new int[maxSize];
            front = 0;
            rear = 0;
        }

        // 判断是否为空
        public boolean isEmpty() {
            return front == rear;
        }

        // 判断是否满了
        public boolean isFull() {
            return (rear + 1) % (maxSize) == front;
        }

        // 进队列
        public void addLast(int num) {
            if (isFull()) {
                System.out.println("队列已经满了,不要再进来了!");
                return;
            }
            //先放入元素,在后移队尾标记
            arr[rear] = num;
            rear = (rear + 1) % maxSize;
//        rear++;
//        rear = (rear+1)%maxSize;
//        rear++;
        }

        // 获取出队列元素
        public int removeFirst() {
            if (isEmpty()) {
                throw new RuntimeException("空的队列,别取了!");
            }
            int num = arr[front];
            front = (front + 1) % maxSize;
            return num;
        }

        public int size() {
            if (rear < front) {
                return maxSize - (front - rear + 1);
            } else if (rear > front) {
                return rear - front + 1;
            }
            return 0;
        }

        // 遍历队列中的元素
        public void show() {
            if (isEmpty()) {
                System.out.println("空的队列,别看了!");
                return;
            }
            System.out.println("遍历队列中的元素:");
            int start = front;
            while (start != rear) {
                System.out.print(arr[start] + " ");
                start = (start + 1) % maxSize;
            }
            System.out.println("\n");
        }
    }
    //队列有问题,solution4没问题
    public static void solution4(int count, int k) {
        //首先定义一个数组, 方便最后返回
        Integer[] live = new Integer[Math.min(count, k - 1)];
        //全活
        if (count < k) {
            int index = 0;
            while (index < count) {
                live[index++] = index;
            }
            Util.pringIntegerArrays(live);
            return;
        }
        MyQueue queue = new MyQueue(count);
        for (int i = 1; i <= count; i++) {
            queue.addLast(i);
        }

        int number = 1;
        while (queue.size() >= k) {
            int i = queue.removeFirst();
            if (number % k == 0) {
                number = 1;
                continue;
            }
            queue.addLast(i);
            number++;
        }
        queue.show();
        return;
    }
}

汉诺塔

package leetcode.dsaa.classical;

/**
 * @author Pillar
 * @version 1.0
 * @date 2022/9/17 19:16
 */
/*
    * 汉诺塔问题
 */
public class TowerOfHanoi {
    public static void main(String[] args) {
        int n = 3;
        hanoi(n,'A','B','C');
        //步数为2的n次方-1
        long count = (1L << n) - 1;
        System.out.println(count);
        System.out.println("共用"+"步");
    }
    public static void hanoi(int n, char A, char B, char C) {
        //如果只有一个,从A移动到C
        if (n == 1) {
            System.out.println("从" + A + "移动到" + C);
        } else {
            //把n-1从A移动到B
            hanoi(n-1,A,C,B);
            //把第n个从A移动到C
            System.out.println("从" + A + "移动到" + C);
            //把n-1从B移动到C
            hanoi(n-1,B,A,C);
        }
    }
}

青蛙跳台阶

package leetcode.dsaa.classical;

import java.util.HashMap;

/**
 * @author Pillar
 * @version 1.0
 * @date 2022/9/17 19:37
 */
/*
 * 青蛙跳台阶
 */
public class TheFrogJumpsUpTheSteps {
    public static void main(String[] args) {
        //1,2,3,5,8,13 ...
        int n = 5;
        HashMap<Integer, Integer> map = new HashMap<>();
        System.out.println(solution1(n));
        System.out.println(solution2(n, map));
        System.out.println(solution3(n));
        //测试三者的速度
        //2823352400
        //74200
        //16000
        finalABC();
    }

    //简单递归写法
    public static int solution1(int n) {
        if (n < 3) {
            return n;
        }
        return solution1(n - 1) + solution1(n - 2);
    }

    //因为递归会出现重复的solution1(n),如果计算过了,就不用再次计算了,直接使用即可,使用map来进行存储
    public static int solution2(int n, HashMap<Integer, Integer> map) {
        if (n < 3) {
            return n;
        }
        if (map.containsKey(n)) {
            return map.get(n);
        }
        //计算n的值,并存入
        int first = solution2(n - 1, map);
        int second = solution2(n - 2, map);
        int sum = first + second;
        map.put(n, sum);
        return sum;
    }

    //把递归改为非递归
    public static int solution3(int n) {
        if (n < 3) return n;
        int first = 1, second = 2, sum = 0;
        while (n-- > 2) {
            sum = second + first;
            first = second;
            second = sum;
        }
        return sum;
    }

    //分析三者的性能
    public static void finalABC() {
        int step = 45;
        long time = System.nanoTime();
        System.out.println(solution1(step));
        System.out.println("递归优化前:" + (System.nanoTime() - time));
        time = System.nanoTime();
        System.out.println(solution2(step, new HashMap<>()));
        System.out.println("递归优化后:" + (System.nanoTime() - time));
        time = System.nanoTime();
        System.out.println(solution3(step));
        System.out.println("非递归:" + (System.nanoTime() - time));
    }

    //进阶:青蛙每次能跳n个台阶,一共n个台阶
    /*
     * f(n) = f(n-1) + f(n-2) + f(n-3) +...f(1)
     * f(n-1) = f(n-2) + f(n-3) +...f(1)
     * f(n) = 2 * f(n - 1)
     */
    public static int solution4(int n) {
        if (n == 1) {
            return 1;
        }
        return solution4(n - 1) * 2;
    }

    //非递归写法
    //根据结果,可以得出,f(n) = 2 ^ (n - 1)
    public static int solution5(int n) {
        if (n == 1) {
            return 1;
        }
        return 1 << (n - 1);
    }

    //在进阶:青蛙每次能跳m个台阶,一共n个台阶
    //如果m>=n,那么回到上面的进阶问题
    //如果m<n,我们可以从n-1级跳1步,n-2级跳两步,n-m级跳m步
    //f(n) = f(n-1)+f(n-2)+...f(n-m)
    //f(n-1) = f(n-2)+f(n-m)+...f(n-m+1)
    //f(n) = 2f(n-1)-f(n-m-1)
    public static int solution6(int n, int m) {
        if (n <= 1) {
            return 1;
        }
        if (n > m){
            return 2 * solution6(n - 1, m) - solution6(n - 1 - m, m);
        }
        return 2 * solution6(n - 1, m);
    }

}
posted @   NeverLateThanBetter  阅读(24)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示