栈和队列
**线性表:**连续的,每个元素都有唯一的前驱和后继
数组和链表
数组
地址是连续的
随机访问公式:数组起始地址 + 下标 * 数组单个存储单元的大小
链表
形象地说,链表就是用一串链子将结点串联起来。
结点:包含数据域和指针域。
数据域:数据
指针域:下一个结点的地址
单链表: 除了尾结点, 每一个结点都有一个后继结点
循环链表: 尾结点的下一个结点指向头结点
双线链表: 有后继结点还有前驱结点
双向循环链表: 尾结点的下一个指向头, 头结点之前指向尾结点
常用的是双链表
取中间值和判断链表是否有环的常用方法:快慢指针
反转链表:头插法
泛型
泛型: 参数化类型 ()
事先不指定数据类型,等到使用的时候再确定
泛型的好处
a. 提高了程序的安全性
b. 将运行期遇到的问题转移到了编译期
c. 省去了类型强转的麻烦
泛型的使用
泛型类
// 把泛型定义到类上, 就是泛型类 泛型类: 把泛型定义在类上 格式:class 类名 <泛型类型1,…> 注意:参数化类型必须是引用类型 // 注意2: 如果某一个代码中我们使用的时候应该传入泛型, 但是我们没有传, 那么它默认是Object类型 User user = new User("zs", 18); Object age2 = user.getAge(); // 注意3: 注意一些泛型定义的习惯问题 // 给泛型定义的时候: 习惯上 // T: type // E: element // K: key // V: value // 注意4: 不要给一个类定义超过两个泛型(不是语法不允许), 习惯上显得怪异 // 注意5: 在泛型类上定义的泛型, 作用域, 仅仅局限于类名和类体内
泛型的通配
泛型通配符: ① 泛型通配符<?> 任意类型,如果没有明确,那么就是Object以及任意的Java类了 ② ? extends E 向下限定,E及其子类 ③ ? super E 向上限定,E及其父类 ArrayList<?> list = new ArrayList<Character>(); // 报错的原因: 因为从编译角度, 仅能推断出?代指一个不确定的类型 // 从java语法角度, 一个不确定的类型, 没有存储其它类型(String存储Integer肯定无法运行) // 为了避免运行时出现问题, 直接不让添加 // list.add("zs");// 报错: // list.add(new Object());// 报错 ArrayList<? extends F> list = new ArrayList<S2>(); // 报错的原因: 因为从编译角度, 仅能推断出? extends F代指一个不确定的f或者f的子类型 // 从java语法角度, 一个不确定的类型, 没有存储其它类型 // 为了避免运行时出现问题, 直接不让添加 //list.add("zs");//报错 //list.add(new Object());//报错 //list.add(new F());//报错 //list.add(new S1());//报错 ArrayList<? super F> list = new ArrayList<F>(); // list.add("zs");//报错 // list.add(new Object());//报错 list.add(new F()); list.add(new S1()); list.add(new S2()); class F {} class S1 extends F{} class S2 extends F{}
泛型擦除
java中的泛型并不是真正的泛型, java中的泛型只存在于代码编译之前, 代码编译的时候, 这些泛型的写法统统会被擦除, 变成Object以及类型强转.
栈
FILO
链表实现栈时运用头插法
栈的应用
应用场景:
- 函数调用栈
- 反序字符串
实现reNumber(str)方法,反转字符串 - 括号匹配问题
实现judgeBracket(str)方法来判断括号匹配 - 编译器利用栈实现表达式求值
- 浏览器的前进后退功能
- 利用栈实现 DFS: depth-first-search 深度优先遍历
- 波兰表达式
队列
FIFO
用链表实现队列时,头节点作为队列头实现出队列,尾节点作为队列尾实现入队列
普通队列的应用场景是很有限的,一般在工程中用到的是阻塞队列。
阻塞队列:常用于生产者-消费者模型中。
队列大小固定:
当队列满的时候,入队列就阻塞。
当队列空的时候,出队列就阻塞。
队列应用场景:
缓存