经典算法
经典算法
背包问题系列-基础背包问题
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);
}
}
本文来自博客园,作者:NeverLateThanBetter,转载请注明原文链接:https://www.cnblogs.com/do-it-520/p/classical_algorithm.html
韶华易逝,不能虚度年华。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?