数据结构前瞻

集合框架

JAVA的集合框架是定义在java.util包下的一组接口和实现类,用于将多个元素置于一个单元中,对这些元素进行快速,便捷的存储,减速和管理,即增删查改

下面的格子,黄色代表接口,蓝色代表抽象类,棕色代表类

是动态数组(顺序表)优先级队列

是双向列表 底层就是一颗红黑树

重要的四个接口


算法效率

时间复杂度

即算法中基本操作的执行次数

比如这张图,因为for循环执行了两次,每次都有n个目标的执行

F(N) = N^2 + 2N + 10

大O渐进表示法

1.用常数1取代运行时间中的所有加法常数

2.修改后的运行次数函数中,只保留最高阶项。

3.如果最高阶项存在且不是1,则去除与这个项目相乘的常数。得到的结果就是大O阶
最坏情况:任意输入规模的最大运行次数 ( 上界 )
平均情况:任意输入规模的期望运行次数
最好情况:任意输入规模的最小运行次数 ( 下界 )

F(N) = 2 * N + 10

O(N) = N

最好情况下:比较一次发现sorted是true直接退出循环,此时时间复杂度是O(N)(因为要对N个数据对N次遍历)

最坏情况下:n-1+n-2+n-3+......+2+1

等差数列计算公式(1+n-1)*(n-1)/2 = (n^2-n)/2 --> O(n^2)

这里不能单纯只看到一个循环就说O(N),我们要从代码思想上分析

最坏情况:二分到最后一个才被找到

n/2^x = 1 (x表示次数)

n = 2^x -- > x = logn(二进制中下底2省略)--> O(logn)

递归

递归的时间复杂度 = 递归的次数*每次递归后执行的次数

因为这里是三目运算,所以递归执行次数为1,那递归的次数就看factorial(N-1),就是O(N-1)

算到F(1) = 2^n + 2^(n-1)+ ...+ 2+ 1

1*(1-2^n)/1-2 = 2^n -1 --> O(2^n)


空间复杂度

临时占用存储空间大小的量度

int\[\] array是一个数组,也就是一个对象,占用一块空间,也就是O(1)

求第n个斐波那契数字?需要开辟n块空间存储n个数字。O(n)

O(n)


包装类

基本数据类型 包装类
byte Byte
short Short
int Integer
long Long
float Float
double Double
char Character
boolean Boolean

java 复制代码
    public static void main1(String[] args) {
        Integer a = 10;//装包   自动装箱
        int i = 99;
        Integer b = i;
        //基本类型 给转变为 包装类型
        System.out.println(a);//10
        System.out.println(b);//99
        Integer aa = Integer.valueOf(10);//显示装箱
    }

valueOf底层

java 复制代码
    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];//装箱
        return new Integer(i);
    }

拆包,拆箱

java 复制代码
        Integer a = 10;
        int i = a;//把包装拆成类型
        System.out.println(i);

java 复制代码
    public static void main(String[] args) {
        Integer a = 100;
        Integer b = 100;
        System.out.println(a == b);

        Integer a1 = 200;
        Integer b1 = 200;
        System.out.println(a1 == b1);

    }

输出结果:true false

为什么第二个结果是false呢?我们返回来看看valueOf定义的范围

说明:IntegerCache.low = -128, 前面加个负号负负得正

100还在这个范围里面,true就是理所当然

但是200明显超过最大值,数组越界,每次Integer 变量 就会产生新的对象,每个新的对象就会产生新的地址,两个地址不一样就导致 false


泛型:适用于很多很多类型

实现一个类,类中包含一个数组成员,使得数组中可以存放任何类型的数据,也可以根据成员方法返回数组中某个下标的值

java 复制代码
class MyArray  {
    
    public Object[] array = new Object[10];
    //默认放到数组的最后一个位置
    public void setValue(int pos, Object val) {
        array[pos] = val;
    }

    public Object getValue(int pos) {
        return array[pos];
    }

明明我拿1去找hello这个字符串,为什么找不到?因为getValue返回值是Object,所以我们需要进行强转

复制代码
String str = (String)myArray.getValue(1);//这样就行啦!

但是很麻烦的一点,每一次找value都要知道这个value是什么类型,如果有成千上百对数据,找起来不就炒鸡麻烦

为了解决这个问题,我们引入泛型

java 复制代码
/**
 *
 * @param <T> 当前类是一个泛型类,只是一个占位符
 * 注意:<>里面的类型必须是包装型,不能是简单数据类型
 */
class MyArray <T> {

    //public T[] array = new T[10];
    //默认放到数组的最后一个位置
    public Object[] array = new Object[10];
    public void setValue(int pos, T val) {
        array[pos] = val;
    }

    public T getValue(int pos) {
        return (T)array[pos];//把返回的类型 强转为指定类型
    }
java 复制代码
    public static void main(String[] args) {
        MyArray<Integer> myArray = new MyArray<Integer>();//只能放包装类
        myArray.setValue(0,10);
        myArray.setValue(1,100);

        int a = myArray.getValue(1);

        System.out.println(a);

        MyArray<String> myArray2 = new MyArray<>();
        myArray2.setValue(0,"abcd");
        myArray2.setValue(1,"efg");
        String ret = myArray2.getValue(1);
        System.out.println(ret);
    }

以前我们只能传入值,但是现在我们可以传入指定的类型

不能new泛型类型的数组,泛型是编译时期存在的,当程序运行起来到JVM之后,就没有泛型的概念了,new T 无法确定

泛型在编译的时候有个擦除机制,擦除成了Object

现在又有问题了
1 、那为什么, T\[\] ts = new T5; 是不对的,编译的时候,替换为 Object ,不是相当于: Object\[\] ts = new Object5吗?
2、还是说,我们把泛型类型数组实例化一下就行了?
像这样:

复制代码
public T[] array = (T[])new Object[10];

这其实是一个欺骗编译器的小伎俩,瞒天过海让编译器以为是Object类型就让你过了

倘若我在MyArray里面加多一个方法,阁下又该如何应对

java 复制代码
    public static void main(String[] args) {
        MyArray<Integer> myArray1 = new MyArray<>();
        //[1,  "fdsfa"  ]
        Object[] integers = myArray1.getArray();
    }

程序报错

即便强转也无济于事

复制代码
Object[] integers = (Object[]) myArray1.getArray();

因为返回的Object数组里面肯能存放的是任何的数据类型,可能是String, Person之类的,把这些数据类型通通变成Integer,编译器认为不安全就不给过

public Object\[\] array = new Object10;

所以这么写就行啦!


泛型的上界

java 复制代码
//T一定是Number或者Number的子类
class TestGeneric <T extends Number>{
    
}

Integer 和 Double都是Number的子类,传入不会报错;但是String不是Number的子类,所以传入报错

Number是Integer和Double的上界,也就是父类


写一个泛型类,求一个数组中的最大值

欸,这不对吧,怎么报错啊

这里的T一定是引用数据类型,最终被擦除为了Object类,而Object是不能进行比较的

那怎么约束这个T让它可以用来比较大小呢

复制代码
class Algf<T extends Comparable<T>>

这句话表示T一定是实现了Comparable接口的,在下面我们可以调用compareTo方法

java 复制代码
class Alg<T extends Comparable<T>>{
    public T findMax(T[] array){
        T max = array[0];
        for (int i = 1; i < array.length; i++) {
            if(max.compareTo(array[i])< 0){
                max = array[i];
            }
        }
        return max;
    }
}

在main函数里面调用

如果想用Person类型的话,直接调用那指定是报错的

在Person类实现这个接口并重写方法就没问题了

java 复制代码
class Person implements Comparable<Person>{
    @Override
    public int compareTo(Person o) {
        return 0;
    }
}

泛型方法,把放在类实现的接口语句扔到方法的访问限定符的后面就行了

java 复制代码
class Alg2{
    public<T extends Comparable<T>> T findMax(T[] array){
        T max = array[0];
        for (int i = 1; i < array.length; i++) {
            if(max.compareTo(array[i])< 0){
                max = array[i];
            }
        }
        return max;
    }
}
java 复制代码
        Alg2 alg2 = new Alg2();
        Integer[] integers1 = {1,2,3,4,5,6,7};
        //类型推导:根据实参传值来推导此时的类型,所以前面可以不用指定类型
        System.out.println(alg2.findMax(integers1));
相关推荐
Jerry1 小时前
LeetCode 28. 找出字符串中第一个匹配项的下标
算法
Jerry3 小时前
LeetCode 459. 重复的子字符串
算法
闲猫5 小时前
Spring AI 对接Deepseek ChatModel 聊天对话
java·前端·spring
海石5 小时前
1500分的题目,确实有实力,不过还是我略胜一筹
算法·leetcode
海石5 小时前
【记忆化搜索】条条大路通AC,走好适合你的那一条,走到后再考虑走得快
算法·leetcode
自信的未来7 小时前
JSON 工具|Web Worker 工程化打包 + 语法自动修复 + 多语言代码生成实战
java·前端·json
Brookty7 小时前
【JavaEE】线程安全(一).4:写块串行保安全、CAS
java·开发语言·java-ee·多线程·线程安全
Jerry7 小时前
LeetCode 151. 反转字符串中的单词
算法
怕孤单的草丛8 小时前
缓存管理面临的主要问题
java·数据库·缓存