合并K个已排序的链表
TreeNode结构和ListNode结构,自己先在本地写好,以及他们的构造方式,也先写好
1.一开始就应该习惯性的写出输入=null的时候应该返回什么
2.把前k个数进行一个分治,使用二分法,注意即使是分也是需要返回头结点的
3.merge时进行大小的比较,看先把谁接到排好序的节点后面
import java.util.*;
public class Solution {
public ListNode mergeKLists(ArrayList<ListNode> lists) {
if(lists.size()==0) return null;
return devide(lists,0,lists.size()-1);
}
private ListNode devide(ArrayList<ListNode> lists,int left,int right){
if(left>=right) return lists.get(left);
int mid=(left+right)/2;
ListNode node1=devide(lists,left,mid);
ListNode node2=devide(lists,mid+1,right);
ListNode res=merge(node1,node2);
return res;
}
private ListNode merge(ListNode node1,ListNode node2){
if(node1==null) return node2;
if(node2==null) return node1;
if(node1.val<node2.val){
node1.next=merge(node1.next,node2);
return node1;
}else{
node2.next=merge(node1,node2.next);
return node2;
}
}
}
矩阵的最小路径和
标准二维数组动态规划,注意第一行和第一列的初始化一定要写
import java.util.*;
public class Solution {
/**
*
* @param matrix int整型二维数组 the matrix
* @return int整型
*/
public int minPathSum (int[][] matrix) {
// write code here
if(matrix==null || matrix[0]==null) return 0;
int n=matrix.length;
int m=matrix[0].length;
int[][] dp=new int[n][m];
//边界赋初值,除了[0][0]以外,还有第一行和第一列不要写漏了
dp[0][0]=matrix[0][0];
for(int i=1;i<n;i++){
dp[i][0]=dp[i-1][0]+matrix[i][0];
}
for(int j=1;j<m;j++){
dp[0][j]=dp[0][j-1]+matrix[0][j];
}
for(int i=1;i<n;i++){
for(int j=1;j<m;j++){
dp[i][j]=Math.min(dp[(i-1)][j]+matrix[i][j],dp[i][(j-1)]+matrix[i][j]);
}
}
return dp[(n-1)][(m-1)];
}
}
最长公共子串
要想到这两个子串对象的问题仍然可以转换为一个二维数组的动态规划问题进行解决,并且通过一个index来记录我们需要得到的子串的下标
import java.util.*;
public class Solution {
public String LCS (String str1, String str2) {
int m=str1.length(),n=str2.length();
int[][] dp=new int[m+1][n+1];
int max=0,index=0;
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(str1.charAt(i)==str2.charAt(j)){
dp[i+1][j+1]=dp[i][j]+1;
if(max<dp[i+1][j+1]){
max=dp[i+1][j+1];
index=i+1;//因为是左闭右开的,所以加1
}
}
}
}
return str1.substring(index-max,index);
}
}
二叉树根节点到叶子节点的所有路径表示数之和
为什么写成这个形式的递归sumNumbers(root.left,sum)+sumNumbers(root.right,sum)而不是分开考虑左右子树然后改变sum,这是值得思考的,写过就会发现第一种形式的好处就是当sum被传入左右子树的时候都是一样的值,这样就不会对sum发生改变,所以我们写这种需要传递值的递归的时候第一步是需要重再函数,第二步则是把它拆解成几个小问题的解
要记住递归的本质就是压栈
import java.util.*;
/*
* public class TreeNode {
* int val = 0;
* TreeNode left = null;
* TreeNode right = null;
* }
*/
public class Solution {
/**
*
* @param root TreeNode类
* @return int整型
*/
public int sumNumbers (TreeNode root) {
if(root==null) return 0;
return sumNumbers(root,0);
}
public int sumNumbers(TreeNode root,int sum){
//递归终止条件
if(root==null){
return 0;
}
sum=sum*10+root.val;
if(root.left==null && root.right==null){
return sum;
}
return sumNumbers(root.left,sum)+sumNumbers(root.right,sum);
}
}
买卖股票的最好时机
基本的思想是维持一个单调栈,用的是linkedlist实现的stack,对应的查询栈首和栈尾的操作是peekfirst和peeklast而不是getfirst和getlast,弹出用到的是polllast而不是poll,加入栈尾的操作是addlast,这几个方法需要注意
import java.util.*;
public class Solution {
/**
*
* @param prices int整型一维数组
* @return int整型
*/
public static int maxProfit (int[] prices) {
// write code here
//单调栈
if(prices.length<=1) return 0;
int max=0;
LinkedList<Integer> stack=new LinkedList<>();
stack.addLast(prices[0]);
for(int i=1;i<prices.length;i++){
int last=stack.peekLast();
while(stack.size()!=0 && last>prices[i]){
stack.pollLast();
if(stack.size()!=0){
last=stack.peekLast();
}
}
stack.addLast(prices[i]);
if(prices[i]-stack.peekFirst()>max){
max=prices[i]-stack.peekFirst();
}
//测试
// StringBuilder sb=new StringBuilder();
// for(int a:stack){
// sb.append(a);
// }
// System.out.println(sb.toString());
}
return max;
}
}
看了一下别人的代码,同样的时间复杂度,空间复杂度更低,人家这个写得科学多了…只需要用一个buy来记录买入价格,用一个profit来记录利润,然后更新就好了
import java.util.*;
public class Solution {
/**
*
* @param prices int整型一维数组
* @return int整型
*/
public static int maxProfit (int[] prices) {
if(prices==null || prices.length==0){
return 0;
}
int profit=0,buy=prices[0];
for(int i=1;i<prices.length;i++){
buy=Math.min(buy,prices[i]);
profit=Math.max(profit,prices[i]-buy);
}
return profit;
}
}
设计模式之建造者模式
创建型模式:单例模式/工厂/抽象工厂模式/建造者/原型模式
可以看到这几种模式的写法都是帮我们省去了new的过程,转而用一个类来专门new一个类,也就是创建者
建造者在用户不知道对象的建造过程和细节的时候,用来创建一些复杂对象,[复杂]的时候就用建造者模式,用户只需要给定指定复杂对象的类型和内容就行,比如造车的时候需要用户说出型号,但是不知道汽车是怎么组装的.
这个模式包括director指挥抽象的builder,实现得到具体的builder,由builder生产(new)出具体的产品
- 使用指挥者
写一个director类,用于进行制造过程的编写
package shejimoshi;
//指挥:建造者模式的核心,负责指挥建造一个工程,工程如何构建,由他决定
public class Director {
//建造过程,返回的是一个产品,传入的参数是它所指挥的builder
//指挥工人按照顺序建房子,核心的建造方法其实在director里面,要改构建过程也是在director里面改,工人是不知道这些的
public Product build(Builder builder){
builder.buildA();
builder.buildB();
builder.buildC();
builder.buildD();
return builder.getproduct();
}
}
抽象的建造者builder,可以继承出各种各样的工人
package shejimoshi;
//抽象的建造者,不负责建房子,只用于定义方法和接口
public abstract class Builder {
abstract void buildA();//地基
abstract void buildB();//钢筋工程
abstract void buildC();//铺电线
abstract void buildD();//粉刷
//得到产品
abstract Product getproduct();
}
package shejimoshi;
//具体的建造者:工人
public class Worker extends Builder{
//产品
private Product product;
//让工人生产一个产品,这里就是建造者模式的精髓了
public Worker(){
product=new Product();
}
@Override
void buildA() {
product.setBuildA("地基");
System.out.println("地基");
}
@Override
void buildB() {
product.setBuildB("钢筋");
System.out.println("钢筋");
}
@Override
void buildC() {
product.setBuildC("电线");
System.out.println("电线");
}
@Override
void buildD() {
product.setBuildD("墙面");
System.out.println("墙面");
}
//返回上面得到的产品
@Override
Product getproduct() {
return product;
}
}
工人建造的产品长这个样子
package shejimoshi;
//产品:房子
public class Product {
private String buildA;
private String buildB;
private String buildC;
private String buildD;
public String getBuildA() {
return buildA;
}
public void setBuildA(String buildA) {
this.buildA = buildA;
}
public String getBuildB() {
return buildB;
}
public void setBuildB(String buildB) {
this.buildB = buildB;
}
public String getBuildC() {
return buildC;
}
public void setBuildC(String buildC) {
this.buildC = buildC;
}
public String getBuildD() {
return buildD;
}
public void setBuildD(String buildD) {
this.buildD = buildD;
}
@Override
public String toString() {
return "Product{" +
"buildA='" + buildA + '\'' +
", buildB='" + buildB + '\'' +
", buildC='" + buildC + '\'' +
", buildD='" + buildD + '\'' +
'}';
}
}
使用director时
package shejimoshi;
public class DirectorTest {
public static void main(String[] args) {
//指挥
Director director=new Director();
//指挥 具体的工人完成 产品
Product built_product=director.build(new Worker());
System.out.println(built_product.toString());
/*
跑出来结果
地基
钢筋
电线
墙面
Product{buildA='地基', buildB='钢筋', buildC='电线', buildD='墙面'}
*/
}
}
- 基于工人的
除了director的方式,也可以不用director,直接在worker里面写具体的建造过程,没看懂这个… - 建造者模式的应用场景
从上面的例子可以看出来,需要生产的产品对象有复杂的内部结构,这些产品对象具备共性,就可以用指挥
隔离复杂对象的创建和使用,并使用相同的创建过程可以创建不同的产品
建造者与抽象工厂模式相比:
建造者模式返回的是一个组装好的完整产品,而抽象工厂模式返回一系列相关的产品,这些产品位于不同的产品等级结构,构成了一个产品族(比如小米和华为那个例子)
抽象工厂模式中,客户端实例化工厂类,然后调用工厂方法获取所需产品对象,而在建造者模式中客户端可以不直接调用建造者的相关方法,而是通过指挥者来知道如何生成对象,包括对象的组装过程和建造过程,它侧重于一步步构造一个复杂对象,返回一个完整的对象
HashSet
1.8之前:哈希表=数组+链表
1.8之后:哈希表=数组+链表;哈希表=数组+红黑树(提高查询的速度)
哈希表的特点就是查询速度快
数组结构:根据哈希值把元素进行了分组,链表和红黑树结构把哈希值相同的元素连接到一起
如果链表的长度超过了8为,那么就会把链表转换成红黑树,从而提高查询速度
- Set集合不允许重复元素的原理-这其实对应了面试中常见的一个问题,就是为什么要重写hashcode和equals
import java.util.HashSet;
public class HashSetSaveString {
public static void main(String[] args) {
HashSet<String> set=new HashSet<>();
String s1=new String("abc");
String s2=new String("abc");
set.add(s1);
set.add(s2);
set.add("重地");
set.add("通话");
set.add("abc");
System.out.println(set);//[重地, 通话, abc]
}
}
set.add(s1);
add方法会调用s1的hashcode方法,计算abc的哈希值96354
在集合中找有没有96354这个哈希值的元素,发现没有
就会把s1存进去
set.add(s2);
add方法会调用s1的hashcode方法,计算abc的哈希值96354
在集合中找有没有96354这个哈希值的元素,发现有(哈希冲突)
[重点]s2会调用equals方法和哈希值相同的元素进行比较s2.equals(s1),返回true
两个元素的哈希值相同,equals返回true,认定两个元素相同
就不会存s2了
set.add(“重地”);
add方法会调用"重地"的hashcode方法,计算"重地"的哈希值1179395
在集合中找有没有1179395这个哈希值的元素,发现没有
就会把"重地"存进去
set.add(“通话”);
add方法会调用"通话"的hashcode方法,计算"通话"的哈希值1179395
在集合中找有没有1179395这个哈希值的元素,发现有(哈希冲突)
[重点]“通话"会调用equals方法和哈希值相同的元素进行比较"通话”.equals(“重地”),返回false
两个元素的哈希值相同,equals返回false,认定两个元素不同
就会存"通话"
所以用set存储元素使必须重写hashcode方法和equals方法
比如重写person这个对象的hashcode和equals,直接alt+insert就可以了
但是还是要学会自己重写哦
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age &&
Objects.equals(name, person.name) &&
Objects.equals(a, person.a) &&
Objects.equals(b, person.b) &&
Objects.equals(c, person.c) &&
Objects.equals(d, person.d);
}
@Override
public int hashCode() {
return Objects.hash(name, age, a, b, c, d);
}
= =
明天看JVM的底层结构
面试考察点:spring mvc源码,mybatis和redis使用,数据库…这几个学习从11月得开始了,然后自己做项目和项目重构