最小的K个数
注意最小堆和最大堆的写法,另外求最小K个数的时候维护的是一个K大的最大堆
另外和求第K大的数区分一下,那个用一个partition做搜索就够了,当然维护堆也不是不行…
java用priorityqueue来实现堆,常用到的方法offer加入poll拿出peek求最大堆的最大值或最小堆的最小值
import java.util.*;
public class Solution {
public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
//最大堆
PriorityQueue<Integer> max_heap=new PriorityQueue<>((n1,n2)->n2-n1);
//最小堆
// PriorityQueue<Integer> min_heap=new PriorityQueue<>(k);
ArrayList<Integer> result=new ArrayList<>();
if(input==null || k>input.length || k<=0) return result;
for(int i=0;i<input.length;i++){
if(max_heap.size()<k){
max_heap.offer(input[i]);
}else if(input[i]<=max_heap.peek()){
max_heap.poll();
max_heap.offer(input[i]);
}
}
for(int i=0;i<k;i++){
result.add(max_heap.poll());
}
//逆序输出数组的写法
Collections.reverse(result);
return result;
}
}
判断一个链表是否为回文结构
基本的思想就是做个翻转,然后两头开始找
可以优化的点是:没必要整个链表都反转,用快慢指针找到中点然后从中间断开成两个链表,把后面那个反转一下就够了
链表边界真的要我命惹
import java.util.*;
/*
* public class ListNode {
* int val;
* ListNode next = null;
* }
*/
public class Solution {
/**
*
* @param head ListNode类 the head
* @return bool布尔型
*/
public ListNode reverse(ListNode head){
if(head==null||head.next==null) return head;
ListNode pre=null,next=head.next;
while(next!=null){
head.next=pre;
pre=head;
head=next;
next=next.next;
}
head.next=pre;
return head;
}
public boolean isPail (ListNode head) {
// write code here
if(head==null || head.next==null){
return false;
}
ListNode fast=head,slow=head;
ListNode pre=null;
boolean flag=true;
while(fast!=null && fast.next!=null){
fast=fast.next.next;
pre=slow;
slow=slow.next;
}
if(fast!=null){
pre=pre.next;
slow=slow.next;
}
pre.next=null;
slow=reverse(slow);
fast=head;
ListNode temp=slow;
while (temp!=null){
if(fast.val!=temp.val)flag=false;
fast=fast.next;
temp=temp.next;
}
slow=reverse(slow);
pre.next=slow;
return flag;
}
}
反射
今天具体看一看反射,看的是B站上的课,不是原创的
- 首先我们写一个Person类,包括基本的get,set,tostring啊啥的,alt+insert写起来很快
public class Person {
private String name;
private int age;
public String a;
protected String b;
// default c
String c;
private String d;
public Person(){
}
public Person(String name,int age){
this.name=name;
this.age=age;
}
public String getName(){
return name;
}
public void setName(String name){
this.name=name;
}
public int getAge(){
return age;
}
public void setAge(int age){
this.age=age;
}
//这个生成的快捷键是alt+insert
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", a='" + a + '\'' +
", b='" + b + '\'' +
", c='" + c + '\'' +
", d='" + d + '\'' +
'}';
}
public void eat(){
System.out.println("eat");
}
//重载一下
public void eat(String food){
System.out.println("eat"+food);
}
}
- 然后介绍一下一些反射常用的方法,总的分为三大块,有field/constructor/method分别用于获取成员变量/获取构造器/获取成员方法,在获取到了这三类的对象之后,我们可以进行一些操作,对于成员变量对象常用的是get和set;对于构造器对象常用的是创建一个对象;而对于成员方法对象常用的是获取方法名。
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
//介绍一下反射常用的方法
public class Meituan {
public static void main(String[] args) throws Exception{
// 获取Person的Class对象
Class personClass=Person.class;
// 获取成员变量们
// getFields获取public成员变量,private不行哦,并且是可以指定名字的
Field[] fields=personClass.getFields();
for(Field field:fields){
System.out.println(field);
}
System.out.println("-------");
Field a=personClass.getField("a");
// 对成员变量基本的操作有get和set
Person p=new Person();
Object value=a.get(p);
// 这里因为对象默认的是null,所以value打印出来是null
System.out.println(value);
a.set(p,"张三");
System.out.println(p);
System.out.println("-------");
// 下面这个函数就是public private啥啥类型的成员变量全都能获取
Field[] declaredFields=personClass.getDeclaredFields();
for(Field declaredField :declaredFields){
System.out.println(declaredField);
}
Field d=personClass.getDeclaredField("d");
// 忽略访问权限能修饰符的安全检查.我们称之为暴力反射,这时private也可以get了,否则的话会报IllegalAccessException错
d.setAccessible(true);
Object value2=d.get(p);
System.out.println(value2);
System.out.println("-------");
// 获取构造方法,构造器就是用来创建对象的
// 传入的是类,比如说int.class
Constructor constructor=personClass.getConstructor(String.class,int.class);
// 用construstor的方法创建一个对象
Object person=constructor.newInstance("李四",23);
System.out.println(person);
// 使用空参的构造方法来创建一个对象
Constructor constructor1=personClass.getConstructor();
Object person1=constructor1.newInstance();
System.out.println(person1);
// 获取指定名称的方法,要传的参数是方法名和方法的参数列表
// 得到的是一个方法对象
Method eat_method=personClass.getMethod("eat");
Person ppp=new Person();
// 执行方法
eat_method.invoke(ppp);
Method eat_method2=personClass.getMethod("eat",String.class);
eat_method2.invoke(ppp,"饭");
// 获取所有public修饰的方法,输出这个就会发现,除了自己写的方法,还有一些Object自己本身有的方法存在其中
// 比如说notify
Method[] methods=personClass.getMethods();
for(Method method:methods){
System.out.println(method);
}
// 怎样得到方法名getname
for(Method method:methods){
String name=method.getName();
System.out.println(name);
}
// 怎样获取类名
String className=personClass.getName();
System.out.println(className);
}
}
- 最后我们介绍一个使用反射的例子:写一个框架,可以帮我们创建任意类的对象,并执行任意方法
- 比如我们有两个类person和student
public class Student {
public void sleep(){
System.out.println("sleep");
}
}
- 我们通过写一个框架类来实现person和student的使用,写完这个框架类以后只需要修改properties文件里面的类名和方法就好了,不再需要修改底层代码来完成不一样的方法实现,是不是很简单呀~
- 值得注意的是写类的时候需要写完整路径,要把包名啥的都写进去
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Properties;
//一个框架类
public class ReflectTest {
public static void main(String[] args) throws Exception{
// 可以创建任意类的对象,执行任意方法
// Person p=new Person();
// p.eat();
// Student stu=new Student();
// stu.sleep();
// 以上这个方法有一个弊端,实际生产时,不能改变该类的任何代码
// 而像上面这么写的话,就必须得person写一遍,student写一遍,需要改这个框架类的代码!
// 怎么实现不用改代码的写法呢?使用配置文件和反射
// 1.将需要创建的对象的全类名和需要执行的方法定义在配置文件中
// 2.在程序中加载读取配置文件
// 3.使用反射技术来加载类文件进内存
// 4.创建对象
// 5.执行方法
// 创建propoties对象
Properties pro=new Properties();
// 加载配置文件,转换为一个双列的集合
// 获取class目录下的配置文件
ClassLoader classLoader=ReflectTest.class.getClassLoader();
InputStream is=classLoader.getResourceAsStream("pro.properties");
pro.load(is);
// 获取配置文件中定义的数据
String className=pro.getProperty("className");
String methodName=pro.getProperty("methodName");
// 加载该类进入内存
Class cls=Class.forName(className);
// 创建对象
Object obj=cls.newInstance();
// 获取方法对象
Method method=cls.getMethod(methodName);
// 执行方法
method.invoke(obj);
// 之后就只需要改properties了,不用再改代码了,这个可以结合自己的项目讲讲
}
}
- properties文件长这个样子
className=Student
methodName=sleep
- 总结一下Class类对象阶段
获取Class对象的三种方式,这三个是一模一样的,说明同一个字节码文件.class在一次程序运行过程中,只会被加载一次,所以三种方式获取到的都是同一个:
1.Class.forName(“全类名”):将字节码文件加载进内存,返回Class对象
Class cls1=Class.forName("Person");
2.类名.class:通过类名的属性class获取
Class cls2=Person.class;
3.对象.getClass():getClass()定义在Object类中,所有的object都可以用
Person p=new Person();
Class cls3=p.getClass();
明天
明天要去实验室
哎,这学起来真慢啊…
有必要写一下各个设计模式的经典写法,明天再看吧
自己写了几百遍面试却回答不出来,准备面试还是很有必要的…