一、学习内容
1. 算法设计中五大常用算法
1) 分治法
设计思想:将一个难以直接解决的大问题分解成规模较小的相同问题,以便分而治之。
实际的应用:快速排序、归并排序
分治法在每一层递归上的基本步骤:
① 分解:将原问题分解为若干个规模较小、相互独立、与原问题形式相同的子问题。
② 解决:若子问题规模较小就直接解决,不然就递归解决各个子问题
③ 合并:将各个子问题的解合并为原问题的解
以快速排序为例理解分治法:
快速排序代码:
public static void quickSort(int a[],int low,int high){
if(low < high){
int pivotpos = partition(a,low,high);
quickSort(a,low,pivotpos-1);
quickSort(a,pivotpos+1,high);
}
}
public static int partition(int a[],int low,int high){
int pivot = a[low];
while (low < high){
while (low < high && a[high] >= pivot){
--high;
}
a[low] = a[high];
while (low < high && a[low] <= pivot){
++low;
}
a[high] = a[low];
}
a[low] = pivot;
return low;
}
① 分解:选取基准元素,将左右两侧进行划分
② 解决:分别将两个子序列进行快速排序
③ 合并:将排好的子序列合并
以两路合并排序为例理解分治法:(分治法中的合并在归并排序中体现得更加清楚)
归并排序代码:
public static void merge(int a[],int low,int mid,int high){
int[] temp = new int[high-low+1];
int i = low;
int j = mid+1;
int k = 0;
while (i <= mid && j <= high){
if(a[i] < a[j]){
temp[k++] = a[i++];
}else {
temp[k++] = a[j++];
}
}
//把左边剩下的移到数组中
while (i <= mid){
temp[k++] = a[i++];
}
//把右边剩下的移到数组中
while (j <= high){
temp[k++] = a[j++];
}
//更新原数组
for (int x = 0;x <temp.length;x++){
a[x+low] = temp[x];
}
}
public static int[] mergeSort(int a[],int low,int high){
int mid = (low+high)/2;
if(low < high){
mergeSort(a,low,mid);
mergeSort(a,mid+1,high);
//左右合并
merge(a,low,mid,high);
}
return a;
}
① 分解:将一个数组一刀切两半,递归切,直到切成单个元素
② 解决:单个元素合并成有序的小数组
③ 合并:小数组合并成大数组,最终合并完成
2) 动态规划法
设计思想:最优化原理,即一个过程的最优决策具有这样的性质:即无论其初始状态和初始决策如何,其今后诸策略对以第一个决策所形成的状态作为初始状态的过程而言,必须构成最优策略
动态规划法所要满足的条件:
① 问题中的状态满足最优化原理
② 问题中的状态必须满足无后效性,无后效性指的是下一时刻的状态只与当前的状态 有关而与当前状态的之前状态无关。
动态规划三要素:
① 问题的阶段
② 每个阶段的状态
③ 从前一个阶段转换到后一个阶段的递推关系
实际的应用:0/1背包问题 最长公共子串问题
以最长公共子串问题为例理解动态规划法:
定义dp[i][j]表示以A中第i个字符结尾的子串和B中第j个字符结尾的子串的最大公共子串,dp 的大小也为 (n + 1) x (m + 1) ,这多出来的一行和一列是第 0 行和第 0 列,初始化为 0,表示空字符串和另一字符串的子串的最长公共子串。
当我们要求dp[i][j]时,我们要先判断A的第i个元素B的第j个元素是否相同即判断A[i - 1]和 B[j -1]是否相同,如果相同它就是dp[i - 1][j- 1] + 1,相当于在两个字符串都去掉一个字符时的最长公共子串再加 1;否则最长公共子串取0。所以整个问题的初始状态为:
dp[i][0]=0,dp[0][j]=0
相应的状态转移方程为:
实现代码:
public static int findLongest(String A,String B){
int n = A.length();
int m = B.length();
if(m == 0 || n == 0){
return 0;
}
int result = 0;
int[][] dp = new int[n+1][m+1];
//初始状态
for(int i = 0; i <= n;i++){
dp[i][0] = 0;
}
for(int i = 0; i <= m;i++){
dp[0][i] = 0;
}
for(int i = 1;i <= n;i++){
for(int j = 1;j <= m;j++){
if(A.charAt(i-1) == B.charAt(j-1)){
dp[i][j] = dp[i-1][j-1]+1;
result = Math.max(result,dp[i][j]);
}else {
dp[i][j] = 0;
}
}
}
return result;
}
3) 贪心算法
设计思想:贪心算法在对问题求解时,总是做出在当前看来是最好的选择,不从整体最优上加以考虑,它所做出的仅是在某种意义上的局部最优解。贪心算法不是对所有问题都能得到整体最优解,但对范围相当广泛的许多问题他能产生整体最优解或者是整体最优解的近似解。
实际的应用:最小代价生成树之Prim算法、kruskal算法 背包问题
Prim算法:
在一个无向图中(如上图)找到一棵最小代价生成树,先确定根节点,不断地往上加边,把相关联的节点添加到增长的树上,加边的原则是,连接未在树中并且权值最小的节点
Prim算法形成的最小代价生成树如下图所示:
kruskal算法
kruskal算法与Prim算法的区别在于每次都选权值最小的边加入
Prim算法形成的最小代价生成树如下图所示:
注:上图中标记的数字表示当前边加入的顺序
4) 回溯法
5) 分支限界法
2. 对7.12-7.17的学习内容进行总结
7.12-7.17基本上是按照计划来进行,进度正常。主要对Java基础部分进行了复习对部分内容加深了理解,主要涉及的内容有集合、线程、Java虚拟机、类间关系的UML表示、IO流、Java对Excel表格的基本操作,除此以外对MYSQL的多表连接查询进行学习,但是由于没有具体的需求所以并没有写太多具体的SQL,只是利用简单的数据库表对基本的语法进行了巩固。
另外,对Dubbo框架进行了初步学习,按照官网的介绍创建了一个Dubbo demo,并且 安装了Zookeeper之后成功跑起来。
3. 弄懂TreeSet的两种排序方式
TreeSet是SortedSet接口的实现类,可以确保元素处于排序状态,它有两种排序方式, 自然排序和比较器排序。
① 自然排序:在自定义类中实现Comparerable<T>接口,并且重写compareTo方法
public class Student implements Comparable {
private String name;
private Integer age;
public Student(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
if (age == null) {
if (other.age != null)
return false;
} else if (!age.equals(other.age))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((age == null) ? 0 : age.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public int compareTo(Object o) {
if(o instanceof Student){
Student student = (Student) o;
int i = this.age.compareTo(student.age);
if (i == 0){
return this.name.compareTo(student.name);
}else {
return i;
}
}
return 0;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + ''' +
", age=" + age +
'}';
}
}
结果:
② 比较器排序:在自定义类中实现Comparetor<T>接口,重写compare方法
public class MyComparetor implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
int len1 = o1.getName().length();
int len2 = o2.getName().length();
int num = len1-len2;
int num2 = num == 0 ? o1.getName().compareTo(o2.getName()):num;
int num3 = num2 == 0 ? o1.getAge().compareTo(o2.getAge()):num2;
return num3;
}
}
学生实体不需要实现任何接口,提供构造函数和get set即可
结果:
二、存在的问题
1. 问题解决记录
1) 导入一个Java项目,Java文件左下角显示橙色Java图标,项目无法运行,如图所示:
解决办法:
找到父项目,右击选择Open Module Settings
选择Module,点击加号,选择import Module
然后一路Next即可。
三、明天学习计划
1. 建表
2. 继续算法的学习(回溯法、分支限界法)以及常见的排序算法