1.初识集合框架
1. 什么是集合框架?
官方教程:https://docs.oracle.com/javase/tutorial/collections/index.html
Java 集合框架 Java Collection Framework ,又被称为容器 container ,是定义在 java.util 包下的一组接口 interfaces 和其实现类 classes 。 其主要表现为将多个元素 element 置于一个单元中,用于对这些元素进行快速、便捷的存储 store 、检索 retrieve 、 管理 manipulate ,即平时我们俗称的增删查改 CRUD 。 例如,一副扑克牌(一组牌的集合)、一个邮箱(一组邮件的集合)、一个通讯录(一组姓名和电话的映射关系)等等。
主要特点:
- 统一接口:所有集合类共享一套接口(如
Collection
、List
、Set
、Map
),操作一致性强。 - 多种实现:针对不同应用场景,提供多种高效的集合实现(如
ArrayList
、HashSet
、TreeMap
)。 - 内置算法支持:提供常用算法(如排序、搜索、遍历)的方法。
2. 集合框架的重要性
开发中的使用 使用成熟的集合框架,有助于我们便捷、快速的写出高效、稳定的代码
学习背后的数据结构知识,有助于我们理解各个集合的优缺点及使用场景
-
高效管理数据
- 提供了多种数据存储结构(如动态数组、链表、哈希表等),帮助开发者高效存储和管理大量数据。
-
代码简洁
- 集合框架封装了常见的数据结构和操作,减少开发者手动实现的复杂性。
-
可扩展性
- 开发者可以根据需求,扩展已有集合类或自定义集合实现。
-
标准化
- 通过统一的接口设计,开发者可以快速掌握集合框架的使用方式,无需关心底层实现细节。
-
内置工具方法
- 提供诸如排序(
Collections.sort
)、搜索(Collections.binarySearch
)等便捷工具方法。
- 提供诸如排序(
笔试及面试题
腾讯-Java后台开发面经
1. HashMap 了解不,介绍一下,如果一个对象为 key 时,hashCode 和 equals 方法的用法要注意什么?
2. HashSet 和 HashMap 的区别是什么?
3. HashMap 是线程安全的么?那需要线程安全需要用到什么?
阿里巴巴-Java后台开发面经
1. ArrayList 和 LinkedList 的区别是什么?
2. 有了解过 HashMap 的具体实现么?
3. HashMap 和 ConcurrentHashMap 哪个效率更高?
今日头条-Java后台开发面经
1. 编程题:判断一个链表是否是一个回文链表。
2. Redis 的 zset 类型对应到 java 语言中大致是什么类型?
3. hashCode 主要是用来做什么用的?
3. 背后所涉及的数据结构以及算法
3.1 什么是数据结构
数据结构(Data Structure)是计算机存储、组织数据的方式,指相互之间存在一种或多种特定关系的数据元素的 集合。
3.2 容器背后对应的数据结构
该阶段,我们主要学习以下容器,每个容器其实都是对某种特定数据结构的封装,大概了解一下,后序会给大家详 细讲解并模拟实现:
1. Collection:是一个接口,包含了大部分容器常用的一些方法
2. List:是一个接口,规范了ArrayList 和 LinkedList中要实现的方法
ArrayList:实现了List接口,底层为动态类型顺序表
LinkedList:实现了List接口,底层为双向链表
3. Stack:底层是栈,栈是一种特殊的顺序表
4. Queue:底层是队列,队列是一种特殊的顺序表
5. Deque:是一个接口
6. Set:集合,是一个接口,里面放置的是K模型
HashSet:底层为哈希桶,查询的时间复杂度为O(1)
TreeSet:底层为红黑树,查询的时间复杂度为O( ),关于key有序的
7. Map:映射,里面存储的是K-V模型的键值对
HashMap:底层为哈希桶,查询时间复杂度为O(1)
TreeMap:底层为红黑树,查询的时间复杂度为O( ),关于key有序
一、数据结构的定义
数据结构(Data Structure)是计算机中用来组织、存储和管理数据的一种方式,它不仅描述了数据本身,还描述了数据之间的关系以及操作这些数据的方法。
-
基本定义:
- 数据结构是数据元素之间逻辑关系和存储形式的集合。
- 数据结构决定了数据的组织方式,从而影响算法操作的效率。
-
作用:
- 提供高效存储和操作数据的机制。
- 直接影响程序运行效率和内存使用效率。
-
关键组成部分:
- 逻辑结构:数据元素之间的关系。
- 存储结构:数据在计算机中的表示方式(如内存中的存储方式)。
- 数据操作:对数据的增删改查等操作。
二、数据结构的分类
数据结构按照逻辑结构、存储结构以及操作方式,可以分为以下几个大类:
可视化工具:Dynamic Programming - Fibonacci Sequence 动态演示算法执行过程。
https://visualgo.net/ 直观展示数据结构和算法。
1. 按逻辑结构分类
数据的逻辑结构指数据之间的逻辑关系,即如何在概念上组织这些数据。
-
线性结构:
- 定义:数据元素之间存在一对一的线性关系。
- 特点:每个元素只有一个直接前驱和一个直接后继(除了第一个和最后一个)。
- 常见例子:
- 数组(Array):一组连续存储的相同类型元素。
- 链表(Linked List):通过指针连接的一组节点。
- 栈(Stack):先进后出(LIFO)的特殊线性结构。
- 队列(Queue):先进先出(FIFO)的特殊线性结构。
-
非线性结构:
- 定义:数据元素之间存在多对多或层次关系。
- 常见类型:
- 树(Tree):
- 每个节点最多只有一个父节点和多个子节点,形成层次结构。
- 如二叉树、平衡树、红黑树等。
- 图(Graph):
- 数据元素之间是多对多关系。
- 如社交网络中的好友关系图。
- 集合(Set):
- 元素之间无序且无重复的结构。
- 树(Tree):
2. 按存储结构分类
数据的存储结构是数据在计算机内存中的具体表现形式,决定了数据如何在内存中组织和访问。
-
顺序存储结构:
- 数据按线性顺序存储在一组连续的内存单元中。
- 优点:随机访问效率高(支持直接通过索引定位)。
- 缺点:插入和删除时需要移动大量元素。
- 例子:数组、字符串。
-
链式存储结构:
- 数据存储在一组非连续的内存单元中,通过指针链接相邻的元素。
- 优点:动态分配内存,插入和删除操作效率高。
- 缺点:随机访问效率低。
- 例子:链表。
-
索引存储结构:
- 在数据元素之外,附加一个索引表,用于快速定位数据。
- 例子:数据库中的索引。
-
散列存储结构(Hash):
- 通过哈希函数将数据映射到存储地址。
- 优点:查找速度快。
- 缺点:需要解决哈希冲突。
3. 按操作特点分类
-
动态结构:
- 数据的数量和存储空间可以动态调整。
- 例子:链表、栈、队列。
-
静态结构:
- 数据存储空间在初始化时确定,不能动态调整。
- 例子:数组。
三、数据结构分类图示
1、逻辑结构与存储结构的对应关系
- 逻辑结构和存储结构是数据结构的两个视角:
- 逻辑结构:研究数据元素之间的关系。
- 存储结构:研究这些关系在计算机中的存储方式。
- 例子:
- 逻辑上是线性结构(如链表)可以对应链式存储结构。
- 树的逻辑结构既可以顺序存储(如堆),也可以链式存储(如二叉树)。
2.算法与复杂度分析
-
什么是算法?
- 算法是解决问题的一组清晰指令。
- 具备以下特点:
- 有穷性:算法在有限步骤内终止。
- 确定性:每一步都有明确的定义。
- 输入:算法需要零个或多个输入。
- 输出:算法至少有一个输出。
- 可行性:算法的每一步操作都可以有效执行。
-
算法设计目标
- 正确性:算法能正确解决问题。
- 可读性:算法易于理解和维护。
- 健壮性:算法能处理意外输入或边界情况。
- 高效性:算法执行速度快,占用资源少。
二、算法复杂度分析
复杂度分析是评估算法性能的重要工具。它主要分为时间复杂度和空间复杂度两部分。
1. 算法效率
算法效率分析分为两种:第一种是时间效率,第二种是空间效率。时间效率被称为时间复杂度,而空间效率被称作 空间复杂度。 时间复杂度主要衡量的是一个算法的运行速度,而空间复杂度主要衡量一个算法所需要的额外空间, 在计算机发展的早期,计算机的存储容量很小。所以对空间复杂度很是在乎。但是经过计算机行业的迅速发展,计 算机的存储容量已经达到了很高的程度。所以我们如今已经不需要再特别关注一个算法的空间复杂度。
2. 时间复杂度
时间复杂度是衡量算法运行时间随输入规模变化的增长趋势。
时间复杂度的定义:在计算机科学中,算法的时间复杂度是一个数学函数,它定量描述了该算法的运行时间。一个 算法执行所耗费的时间,从理论上说,是不能算出来的,只有你把你的程序放在机器上跑起来,才能知道。但是我 们需要每个算法都上机测试吗?是可以都上机测试,但是这很麻烦,所以才有了时间复杂度这个分析方式。一个算 法所花费的时间与其中语句的执行次数成正比例,算法中的基本操作的执行次数,为算法的时间复杂度。
常见表示法
- Big-O 表示法(O 表示法):
- 描述算法运行时间的上限,即最坏情况。
- 忽略低阶项和常数系数,关注主要增长趋势。
- 例如:如果一个算法的运行时间为 T(n)=2n2+3n+1T(n) = 2n^2 + 3n + 1T(n)=2n2+3n+1,则时间复杂度为 O(n2)O(n^2)O(n2)。
常见时间复杂度及其含义
-
常数时间 O(1):
- 算法的执行时间与输入规模无关。
- 例子:访问数组中的某一元素。
-
对数时间 O(logn):
- 每次操作将问题规模缩小一半。
- 例子:二分查找。
-
线性时间 O(n):
- 算法的执行时间与输入规模成正比。
- 例子:遍历数组。
-
线性对数时间 O(nlogn):
- 线性操作加对数级别的递归。
- 例子:归并排序、快速排序。
-
平方时间 O(n2):
- 嵌套循环导致的时间复杂度。
- 例子:冒泡排序、选择排序。
-
指数时间 O(2n):
- 输入规模增大时,运行时间指数增长。
- 例子:解决所有可能组合问题(如汉诺塔问题)。
-
阶乘时间 O(n!):
- 输入规模增大时,运行时间以阶乘速度增长。
- 例子:旅行商问题的暴力求解。
常见时间复杂度比较
3.如何推导算法的时间复杂度
-
找出基本操作:
- 定位算法中执行次数最多的操作(通常是循环体内的操作)。
- 基本操作的执行次数决定了时间复杂度。
-
分析算法结构:
- 关注循环嵌套、递归调用等结构。
- 对每部分执行的次数进行分析。
-
表示执行次数的函数:
- 用输入规模 nnn 表示执行次数。
- 例如,单层循环的执行次数通常是 nnn,嵌套循环可能是 n2n^2n2。
-
选择增长最快的项:
- 忽略低阶项和常数系数,只保留主导增长的项。
-
用大 O 表示法表示结果。
常见代码结构的时间复杂度推导
1. 顺序结构
- 顺序执行的代码块,时间复杂度由执行时间最长的部分决定。
- 示例:
- 时间复杂度分析:
- 第一部分和最后一部分是常数时间 O(1)。
- 中间循环的时间复杂度为 O(n)。
- 整体时间复杂度:O(n)。
2. 循环结构
- 单层循环:
- 时间复杂度与循环次数成正比。
- 示例:
-
执行次数:n,时间复杂度为 O(n)。
-
嵌套循环:
- 总复杂度等于嵌套循环的总执行次数。
-
示例:
-
- 总执行次数:n×n=n2。
- 时间复杂度:O(n2)。
3.递归结构
- 递归算法的时间复杂度通常用递推公式表示,最终结果通过公式求解。
- 示例:
-
递推公式:
T(n)=T(n−1)+O(n) -
求解:
- 展开递推公式:T(n)=T(n−1)+n=T(n−2)+(n−1)+n=...=O(n2)。
-
时间复杂度:O(n2)。
4、综合实例:推导复杂度
例 1:排序算法
- 分析:
- 外层循环执行 n−1次。
- 内层循环依次执行 n−1,n−2,...,1次。
- 总执行次数为:)(n−1)+(n−2)+...+1=2n(n−1)≈O(n2)。
- 时间复杂度:O(n2)。
例 2:二分查找
- 递推公式:T(n)=T(n/2)+O(1)
- 解法:T(n)=O(logn)
- 时间复杂度:O(logn)。
4、推导时间复杂度的关键点
-
关注循环结构和递归结构:
- 循环的执行次数。
- 递归的递推公式。
-
只保留最高阶项:
- 忽略低阶项和常数系数,只关注主要增长趋势。
-
掌握常见模式:
- 单层循环 O(n)、嵌套循环 O(n2)、分治法 O(nlogn)。
空间复杂度
空间复杂度是衡量算法运行过程中占用的存储空间随输入规模变化的增长趋势。
空间复杂度分析的关键点
- 固定部分:
- 算法本身的常量空间(如变量、常量)。
- 输入数据空间:
- 存储输入数据所需的空间。
- 辅助空间:
- 算法运行时临时存储所需的空间(如递归栈、临时数组)。
常见空间复杂度
- O(1)O(1)O(1):常数空间,如交换两个变量。
- O(n)O(n)O(n):线性空间,如动态分配一个大小为 nnn 的数组。
- 递归栈空间:
- 递归深度为 nnn,每次递归需要额外空间,空间复杂度为 O(n)O(n)O(n)。
三、复杂度分析方法
1. 渐进分析法
通过渐进分析,忽略低阶项和常数系数,聚焦于规模 nnn 的主要增长趋势。
2. 最好、最坏、平均情况
-
最好情况:
- 输入导致算法运行时间最短的情况。
- 例如:冒泡排序中,数据已排好序。
-
最坏情况:
- 输入导致算法运行时间最长的情况。
- 例如:冒泡排序中,数据完全逆序。
-
平均情况:
- 对所有可能输入求期望运行时间。
- 通常最具参考意义。
3. 递归算法的复杂度
递归算法的时间复杂度通常使用递推公式求解。
- 常见公式:
- T(n)=T(n/2)+O(1)
解决:O(logn)(二分查找)。 - T(n)=2T(n/2)+O(n)
解决:O(nlogn)(归并排序)。 - T(n)=T(n−1)+O(1)
解决:O(n)(递归求和)。
- T(n)=T(n/2)+O(1)
四、常见例子
1. 排序算法的时间复杂度
2. 数据结构操作的时间复杂度
五、算法与复杂度分析的实际意义
- 帮助选择适合的算法:
- 通过比较时间和空间复杂度,选择性能最优的算法。
- 优化程序性能:
- 分析算法的瓶颈,优化时间或空间使用。
- 指导大规模数据处理:
- 在处理大规模数据时,关注算法的增长趋势至关重要。
总结:
一、数据结构是个什么“鬼”?
数据结构,就是管理数据的方式。你可以把它想象成一系列工具,比如整理房间的柜子、架子、抽屉,用来帮你把各种数据合理地“放进格子里”。有的格子很“刚”,就是一个死板的长方形(比如数组),有的格子灵活自如,可以动态变换位置(比如链表)。
每种数据结构都有它的脾气,比如数组说:“我随叫随到,随便你直接来访问我的第几个位置!” 而链表则悠然地说:“慢慢来嘛,要访问我得一个个走到那个位置呀~”
二、打怪指南——各路数据结构
-
数组(Array)——直接的硬汉
- “我喜欢一条道走到黑!”——数组里的元素一字排开,所有数据都在连续的内存中。
- 优点:随机访问超级快,想要第 5 个元素?索引直接到!
- 缺点:插入删除你就得头疼了,每次动它们就像搬家,挪来挪去……
-
链表(Linked List)——灵活的小鱼儿
- “跟着我一个一个游呀游!”——链表就像是一群互相拉着小手的数据。
- 优点:插入和删除那是相当灵活,链表头一动,“大家散开”。
- 缺点:你想直接跳到中间某个位置?哦抱歉,我们不走直线,只能从头开始慢慢摸索过去……
-
栈(Stack)——后进先出的小霸王
- “上来的最后,走得最早!”——栈就像摞碗,最后放上去的碗,得先拿走。
- 口号:后进先出(LIFO)!不服?先来个括号匹配给你看看!
-
队列(Queue)——排队的乖宝宝
- “先来先服务!”——队列像排队买奶茶,先进来的先喝到,公平公正公开。
- 应用:排队打游戏、排队等公交、缓冲队列,统统交给我!
-
树(Tree)——家族大树
- “父子连心,枝繁叶茂”——树是一种多层次结构,根节点是老大,其他节点是子孙。
- 二叉树、红黑树、平衡树:“这家伙比我强得多,我得保持平衡!”——平衡的树才能效率高。
- 应用:查找、排序、管理层次、数据压缩样样精通。
-
图(Graph)——社交达人
- “大家都是朋友~”——图结构像社交网络,点和边的关系代表了不同的人际关系。
- 应用:城市地图、社交网络、航班路线,无一不是它的领域。
-
堆(Heap)——谁是最牛的?
- “我永远在顶上!”——最小堆、最大堆,总是把最小或者最大的元素顶上来。
- 应用:快速找到最大或最小的元素,比如优先队列。
三、学习数据结构的趣味小建议
-
把它们当成超级英雄来认识:
- 每种数据结构都有独特的超能力,数组是速度之王,链表是灵活王者,栈和队列是秩序守护者。
-
让生活场景帮助你理解:
- 链表像穿珍珠项链,栈就像一个摞碗架,队列像排队买票。生活中随处都是数据结构!
-
别怕犯错,逐步驯服它们:
- 就像学习魔法,刚开始可能会不小心变成青蛙(指代码报错),但多练几次就能得心应手。
-
刷题练习就像闯关打怪:
- LeetCode、Codeforces 是你练习打怪的最佳战场,逐一击败各类问题中的小怪物,掌握每种数据结构的真正奥义!
四、数据结构学习的最终思考
- **为什么要学数据结构?**因为它是“程序员的武器库”!每种数据结构都是你对抗各种编程问题的必备技能,用对了武器,效率倍增。
- **如何学得开心?**记住它们不是教科书里的死知识,而是你在编程世界里遇到的各种“朋友”,它们可以帮你解决一个个难题,像是合作打游戏的队友。