数据结构与算法分析总结
二分查找:
非递归法:
package lession.arithme;
public class BinarySearch {
public static void main(String[] args) {
int[] arr = {1, 3, 8, 10, 11, 67, 10};
System.out.println(binarySearch(arr, 67));
}
/**
* @param arr 待查找的数组
* @param target 返回对应下标
* @return
*/
public static int binarySearch(int[] arr, int target) {
int left = 0;
int right = arr.length - 1;
while (left <= right) {
int mid = (left + right) / 2;
if (arr[mid] == target) {
return mid;
} else if (arr[mid] > target) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return -1;
}
}
递归法:
递归1:
package lession.search;
import java.util.ArrayList;
import java.util.List;
//二分查找必须是有序数组
public class BinarySearch {
public static void main(String[] args) {
int[] arr = {1, 8, 10, 89, 1000, 1000, 1000, 1000, 1234};
List<Integer> list = binarySearch2(arr, 0, arr.length - 1, 1000);
System.out.println(list);
}
//二分查找算法
/**
* @param arr 带查找数组
* @param left 左边索引
* @param right 右边索引
* @param findValue 带查找值
* @return 找到返回下标, 没有找到返回-1
*/
//查找一个
public static int binarySearch(int[] arr, int left, int right, int findValue) {
if (left > right) {
return -1;
}
int mid = (left + right) / 2;
int midVal = arr[mid];
if (findValue > midVal) {
return binarySearch(arr, mid + 1, right, findValue);
} else if (findValue < midVal) {
return binarySearch(arr, left, mid - 1, findValue);
} else {
return mid;
}
}
//找到所有出现的数的下标
public static List<Integer> binarySearch2(int[] arr, int left, int right, int findValue) {
if (left > right) {
return null;
}
int mid = (left + right) / 2;
int midVal = arr[mid];
if (findValue > midVal) {
return binarySearch2(arr, mid + 1, right, findValue);
} else if (findValue < midVal) {
return binarySearch2(arr, left, mid - 1, findValue);
} else {
//向左扫描
int tmp = mid - 1;
List<Integer> list = new ArrayList<>();
while (tmp >= 0 || arr[tmp] == findValue) {
list.add(tmp);
tmp -= 1;
}
list.add(mid);
//向右扫描
tmp = mid + 1;
while (tmp <= arr.length - 1 || arr[tmp] == findValue) {
list.add(tmp);
tmp += 1;
}
return list;
}
}
}
递归2:
package exercise.luogu.binary;
import java.util.Scanner;
public class P2249 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int m = sc.nextInt();
int[] arr = new int[n];
int[] crr = new int[m];
for (int i = 0; i < arr.length; i++) {
arr[i] = sc.nextInt();
}
for (int i = 0; i < crr.length; i++) {
crr[i] = sc.nextInt();
int index = binarySearch(arr, crr[i]);
if (index == -1) {
System.out.print(-1 + " ");
} else {
System.out.print((index + 1) + " ");
}
}
}
public static int binarySearch(int[] arr, int findValue) {
return binarySearchHelper(arr, 0, arr.length - 1, findValue, -1);
}
public static int binarySearchHelper(int[] arr, int left, int right, int findValue, int result) {
if (left > right) {
return result;
}
int mid = left + (right - left) / 2;
if (arr[mid] < findValue) {
return binarySearchHelper(arr, mid + 1, right, findValue, result);
} else if (arr[mid] > findValue) {
return binarySearchHelper(arr, left, mid - 1, findValue, result);
} else {
return binarySearchHelper(arr, left, mid - 1, findValue, mid);
}
}
}
分治算法
分治算法介绍
-
分治法是一种很重要的算法。字面上的解释是"分而治之",就是把一个复杂的问题分
成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题。…直到最后子问题,可以简单的直接求解,原问题的解即子问题的解的合并。这个技巧是很多高效算法的基础,如排序算法(快速排序,归并排序),傅立叶变换(快速傅立叶变换). -
分治算法可以求解的一些经典问题
2. 二分搜索
3. 大整数乘法
4. 棋盘覆盖
5. 合并排序
6. 快速排序
7. 线性时间选择
8. 最接近点对问题
9. 循环赛日程表
10. 汉诺塔
汉洛塔:
1.如果只有一个盘, A->C
2.如果有你>=2个盘,我们总是可以看成两个盘,1.最下边的盘2.上边的盘
3.先把上面的盘移到B,在把最下边的盘移到C,在B上的盘移到C
代码如下:
package lession.arithme;
//分治算法
public class HanLuoTa {
public static void main(String[] args) {
hanLuoTa(5, 'A', 'B', 'C');
}
/**
* @param num 盘的数量
* @param a A位置
* @param b B位置
* @param c C位置
*/
public static void hanLuoTa(int num, char a, char b, char c) {
if (num == 1) {
System.out.println("第1个盘从:" + a + "->" + c);
} else {
//总是看出两个盘,
// 1先把上边的盘移动到B,中间媒介C
hanLuoTa(num - 1, a, c, b);
//2把最下边移到c
System.out.println("第" + num + "个盘从:" + a + "->" + c);
//3把b的所有盘移到c
hanLuoTa(num - 1, b, a, c);
}
}
}
动态规划
动态规划算法介绍
- 动态规划(dynamicprogramming)算法的核心思想是:将大问题划分为小问题进 行解决,从而一步步获取最优解的处理算法
- 动态规划算法与分治算法类似,其基本思想也是将待求解问题分解成若干个子 问题,先求解子问题,然后从这些子问题的解得到原问题的解。
- 与分治法不同的是,适合于用动态规划求解的问题,经分解得到子问题往往不
是互相独立的。(即下一个子阶段的求解是建立在上一个子阶段的解的基础上, 进行进一步的求解) - 动态规划可以通过填表的方式来逐步推进,得到最优解,
动态规划算法最佳实践-背包问题
思路分析和图解
背包问题主要是指一个给定容量的背包,若干具有一定价值和重量的物品,如何选
择物品放入背包使物品的价值最大。其中又分01背包和完全背包含背包指的是:
每种物品都有无限件可用)
这里的问题属于01背包,即每个物品最多放一个。而无限背包可以转化为01背包。
思路分析和图解
算法的主要思想,利用动态规划来解决。每次遍历到的第个物品,根据w们和v们来
确定是否需要将该物品放入背包中。即对于给定的几个物品,设v们,w们分别为第个
物品的价值和重量,c为背包的容量。再令v们们表示在前个物品中能够装入容量为
的背包中的最大价值。则我们有下面的结果:
(1)v门【0]-v[o]-0;//表示填入表第一行和第一行和第一列是0
(2)当w门》时:v门门-v[i-1]//当准备加入新增的商品的容量大于当前背包的容量
时,就直接使用上一个单元格的装入策略
(3)当j>-w[门时:Math.max(v[ i-1 ] [ j ] , v[ i ] + v[ i-1 ][ j - w[ i ] ] )
i/当准备加入的新增的商品的容量小于等于当前背包的容量
装入的方式: 动态规划背包思路
lvi-110:就是上一个单元格的装入的最大值
lv们:表示当前商品的价值v[-110-w门:装入-1商品,到剩余空间;w目的最大值
[NOIP2005 普及组] 采药
题目描述
辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”
如果你是辰辰,你能完成这个任务吗?
输入格式
第一行有 2 2 2 个整数 T T T( 1 ≤ T ≤ 1000 1 \le T \le 1000 1≤T≤1000)和 M M M( 1 ≤ M ≤ 100 1 \le M \le 100 1≤M≤100),用一个空格隔开, T T T 代表总共能够用来采药的时间, M M M 代表山洞里的草药的数目。
接下来的 M M M 行每行包括两个在 1 1 1 到 100 100 100 之间(包括 1 1 1 和 100 100 100)的整数,分别表示采摘某株草药的时间和这株草药的价值。
输出格式
输出在规定的时间内可以采到的草药的最大总价值。
样例 #1
样例输入 #1
70 3
71 100
69 1
1 2
样例输出 #1
3
提示
【数据范围】
- 对于 30 % 30\% 30% 的数据, M ≤ 10 M \le 10 M≤10;
- 对于全部的数据, M ≤ 100 M \le 100 M≤100。
【题目来源】
NOIP 2005 普及组第三题
【代码如下】
package exercise.luogu.dp;
import java.util.Scanner;
//暴力
public class P1048 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int t = sc.nextInt();
int n = sc.nextInt();
int[] val = new int[n];
int[] time = new int[n];
int dp[][] = new int[n + 1][t + 1];
for (int i = 0; i < n; i++) {
time[i] = sc.nextInt();
val[i] = sc.nextInt();
}
for (int i = 1; i <= val.length; i++) {
for (int j = 1; j <= t; j++) {
if (time[i - 1] > j) {
dp[i][j] = dp[i - 1][j];
} else {
dp[i][j] = Math.max(dp[i - 1][j], val[i - 1] + dp[i - 1][j - time[i - 1]]);
}
}
}
int maxValue = dp[val.length][t];
System.out.println(maxValue);
}
}
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/qq_74748121/article/details/136073655
暴力匹配
package lession.arithme;
//暴力匹配
public class ViolenceMatch {
public static void main(String[] args) {
String s1 = "硅硅谷尚硅谷你尚硅尚硅谷你尚硅谷你尚硅你好";
String s2 = "尚硅谷你尚硅你";
System.out.println(index(s1, s2));
}
//暴力匹配1
public static int violenceMatch(String s1, String s2) {
char[] charArray1 = s1.toCharArray();
char[] charArray2 = s2.toCharArray();
int len1 = charArray1.length;
int len2 = charArray2.length;
int i = 0;
int j = 0;
while (i < len1 && j < len2) {//保证匹配,不越界
if (charArray1[i] == charArray2[j]) {
i++;
j++;
} else {
i = i - (j - 1);//重新赋值i
j = 0; //s2重新赋值为开始 即j=0
}
}
if (j == len2) {
return i - j;
}
return -1;
}
//暴力匹配2
private static int index(String main, String child) {
int i = 0, m = main.length(), n = child.length();
if (m < n) {
return 0;
}
while (i <= m-n) {
String substring = main.substring(i, i + n);
if (!substring.equals(child)) {
i++;
} else {
return i;
}
}
return 0;
}
}
KMP算法
kmp是一个解决模式串在文本串是否出现过,如果出现过,最早出现的位置的
经典算法,knuth-morris-pratt字符串查找算法,简称为"kmp算法",常用于在一个文本
串s内查找一个模式串p的出现位置,这个算法由donaldknuth,vaughanpratt,.
jamesh.morris三人于1977年联合发表,故取这3人的姓民命名此算法。
kmp方法算法就利用之前判断过信息,通过一个next数组,保存模式事中前后
最长公共子序列的长度,每次回溯时,通过next数组找到,前面匹配过的位置,
省去了大量的计算时间
"部分匹配值"就是"前级"和"后级"的最长的共有元素的长度。以"abcdabd"为例,
一"a"的前缀和后缀都为空集,共有元素的长度为0;
一"ab"的前缀为【a],后缀为【b,共有元素的长度为0;
"abc"的前缀为【ab],后缀为【bc,c],共有元素的长度l0:
- "abcd"的前缀为【a,abc],后缓为【bcd,cd,d],共有元素的长度为0;
- “abcda"的前缓为的ab,abc,abcd],后缓为【bcda,da,da,a]: al,共有元素为”,长度为1;
- “abcdab"的前缓为1a,abc,abcd,abcdal,后缓为【bcdab,cdab,cdab,dab,ab,b),共有元素为"ab”,长度为2;
- "abcdabd"的前袋为【a,ab,abc,abcd,abcda,abcdab];后缓为【bcdabd,cdabd,cdabd,dabd,abd,d],
共有元素的长度为0.
【代码如下】
package lession.arithme;
public class KMP {
public static void main(String[] args) {
String s1 = "bbc abcdab abcdabcdabde";
String s2 = "abcdabd";
int[] next = kmpNext(s2);
System.out.println(kmpSearch(s1, s2, next));
}
//写出kmp搜索方法
/**
* @param s1 原字符串
* @param s2 字串
* @param next (字串对应)部分匹配表
* @return 如果不匹配则返回-1,匹配的起始位置
*/
public static int kmpSearch(String s1, String s2, int[] next) {
//遍历
for (int i = 0, j = 0; i < s1.length(); i++) {
while (j > 0 && s1.charAt(i) != s2.charAt(j)) {
j = next[j - 1];
}
if (s1.charAt(i) == s2.charAt(j)) {
j++;
}
if (j == s2.length()) {
return i - j + 1;
}
}
return -1;
}
//获取到字串部分匹配
public static int[] kmpNext(String s2) {
//船舰一个数组保存匹配表,首先我们都是在找是否有最大公共前后缀
int[] next = new int[s2.length()];
next[0] = 0;//如果字符串长度为1,那么他的无前后缀则为0
//i表示后缀,j表示前缀
for (int i = 1, j = 0; i < s2.length(); i++) {
while (j > 0 && s2.charAt(i) != s2.charAt(j)) {
j = j - 1;// 回溯找到能与当前字符匹配的位置
// 貌似懂了,但是好迷糊
}
//部分匹配需要加1
if (s2.charAt(i) == s2.charAt(j)) {
j++;
}
next[i] = j;
}
return next;
}
}
【代码如下】
**public class test1 {
public static void main(String[] args) {
String text = "ABABDABACDABABCABAB";
String pattern = "ABABCABAB";
int[] next = new int[pattern.length()];
calculateNext(pattern, next);
int index = match(text, pattern, next);
if (index != -1) {
System.out.println("为序:" + index);
} else {
System.out.println("找不到为序");
}
}
public static void calculateNext(String pattern, int[] next) {
int len = pattern.length();
for (int i = 1; i < len; i++) {
next[i] = -1;
for (int j = 0; j < i; j++) {
if (pattern.charAt(i) == pattern.charAt(j) && (i - j) > next[i]) {
next[i] = next[j];
}
}
}
}
public static int match(String text, String pattern, int[] next) {
int m = text.length();
int n = pattern.length();
for (int i = 0; i <= m - n; i++) {
int j;
for (j = 0; j < n; j++) {
if (text.charAt(i + j) != pattern.charAt(j)) {
break;
}
}
if (j == n) {
return i;
} else if (next[j] != -1) {
i = i + next[j];
}
}
return -1; // pattern not found in text
}
}**
贪心算法
贪心算法介绍
-
贪婪算法(贪心算法)是指在对问题进行求解时,在每一步选择中都采取最好或
者最优(即最有利)的选择,从而希望能够导致结果是最好或者最优的算法 -
贪婪算法所得到的结果不一定是最优的结果(有时候会是最优解),但是都是相
对近似(接近)最优解的结果
【深基12.例1】部分背包问题
题目描述
阿里巴巴走进了装满宝藏的藏宝洞。藏宝洞里面有 N ( N ≤ 100 ) N(N \le 100) N(N≤100) 堆金币,第 i i i 堆金币的总重量和总价值分别是 m i , v i ( 1 ≤ m i , v i ≤ 100 ) m_i,v_i(1\le m_i,v_i \le 100) mi,vi(1≤mi,vi≤100)。阿里巴巴有一个承重量为 T ( T ≤ 1000 ) T(T \le 1000) T(T≤1000) 的背包,但并不一定有办法将全部的金币都装进去。他想装走尽可能多价值的金币。所有金币都可以随意分割,分割完的金币重量价值比(也就是单位价格)不变。请问阿里巴巴最多可以拿走多少价值的金币?
输入格式
第一行两个整数 N , T N,T N,T。
接下来 N N N 行,每行两个整数 m i , v i m_i,v_i mi,vi。
输出格式
一个实数表示答案,输出两位小数
样例 #1
样例输入 #1
4 50
10 60
20 100
30 120
15 45
样例输出 #1
240.00
import java.util.*;
class Vw {
double weight;
double value;
double rate;
public Vw(double weight, double value, double rate) {
this.weight = weight;
this.value = value;
this.rate = rate;
}
}
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
double n = sc.nextInt();
double t = sc.nextInt();
List<Vw> list = new ArrayList<>();
for (int i = 0; i < n; i++) {
double weight = sc.nextInt();
double value = sc.nextInt();
list.add(new Vw(weight, value, value / weight));
}
Collections.sort(list, new Comparator<Vw>() {
@Override
public int compare(Vw o1, Vw o2) {
return Double.compare(o2.rate, o1.rate);
}
});
double values = 0;
for (Vw vw : list) {
if (t >= 0) {
if (t >= vw.weight) {
values += vw.value;
t = t - vw.weight;
} else {
values = values + t * vw.rate;
t = 0;
}
} else {
break;
}
}
System.out.printf("%.2f", values);
}
}