数据结构
本文主要总结几种常见的数据结构的基本要点:
数组和字符串是两种最基本的数据结构,分别用连续内存存储数字和字符。
链表和树需要操作大量的指针,所以使用时要留意代码的鲁棒性。
序列分先进先出FIFO,先进后出FILO 。FIFO在Java中又叫Queue 队列 ,FILO在Java中又叫Stack 栈。栈与递归紧密相关,队列与广度优先遍历算法紧密相关。
1.数组
- 1.1特点
占用连续内存,顺序存储数据。
创建数组时首先需要指定数组容量大小,然后根据大小分配内存。也正因如此,数组的空间效率较低,经常会有空闲区域没有充分利用。但是因为内存的连续,可以根据下标在O(1)时间读/写任何元素,所以数组的时间效率很高。- 优点以及应用
可以根据其时间效率高的优点,采用数组实现简单的哈希表来解决问题:把数组的下标设为哈希表的键值(Key),把数组中的每一个数字设为哈希表的值(Value),这样每一个下标和该下标对应的数字就组成了一个“键值-值”的配对。 - 缺点以及改进
为了解决空间效率低的问题,设计实现了多种动态数组,为了解决数组的局限性,引入容器类的概念。最常见的容器类是ArrayList,容器的容量会随着对象的增加,自动增长。但同时每一次扩充容量时的额外操作也会对时间性能有一定负面影响。
- 优点以及应用
2.字符串
在Java中,字符串是一个类,所以我们见到的字符串都是对象。创建字符串的手段有:
1)每当有一个字面值出现的时候,虚拟机就会创建一个字符串;
2)调用String的构造方法创建一个字符串对象;
3)通过+加号进行字符串拼接也会创建新的字符串对象。
- 2.1 特点
1)String被修饰为final,所以是不能被继承的;
2)String是immutable,所以是不可改变的(即不能增加/减少长度、不能插入/删除/修改字符)。String的表现就像是一个常量。
3)可以有长度为0的字符串即空字符串。 - 2.2 操纵字符串的常用函数
charAt:获取字符
toCharArray:获取对应的字符数组
subString:截取子字符串
split:分隔
trim:去掉首尾空格
toLowerCase/toUpperCase:大小写
indexOf/lastIndexOf/contains:定位
replaceAll/replaceFirst:替换 - 2.3 比较字符串
- 比较是否为同一对象
str1和str2的内容是一样的,但是不是同一个字符串对象 。
编译器在每碰到一个字符串的字面值,就会创建一个新的对象,所以在第6行会创建一个新的字符串”the light”,但是在第7行,编译器发现已经存在”the light”,就会直接拿来使用,不会进行重复创建。 - 比较内容是否相同
equals进行字符串内容比较,必须大小写一致;
equalsIgnoreCase,忽略大小写判断内容是否一致。
- 比较是否为同一对象
- 2.4 缺点以及改进
str本身的内容不会发生改变,用String进行连续多次修改,每一次修改都会产生一个临时对象,这样会使得空间开销太大影响效率。由此引入StringBuffer,采用StringBuffer可以大大减少时间消耗。
StringBuffer是可变长的字符串,常见方法有:
1)append追加
2)delete 删除
3)insert 插入
4)reverse 反转
3.链表
-
3.1 特点
链表是一种动态数据结构,在创建链表的时候,不需要知道链表的长度,也正是因为内存分配不是在创建链表时一次性完成,而是在每添加一个节点分配一次内存。- 优点:因为没有闲置的内存,所以链表的空间效率比数组高。
- 缺点:同时,由于链表中的内存不是一次性分配的,所以我们无法保证链表内存的连续性,所以查找第i个节点只能从头节点开始,时间效率为O(n),从而使得链表的时间效率较低。
-
3.2 java中的链表
Java语言中包含一些普通数据结构的实现,该语言的这一部分通常叫做Collections API。Collections API位于java.util包中,包含size()、isEmpty()、contains()、add()、remove()等函数。同时Collections接口扩展了Iterable接口,实现Iterable接口的那些类可以拥有增强for循环以观察它们所有的项。
在java.util包中的List接口指定了表。List接口继承了Collection接口。使用get()和set()使得用户可以访问或改变通过由位置索引给定的表中指定位置上的项。List ADT有两种流行的实现方式:-
3.2.1 ArrayList
提供了List ADT的一种可增长数组的实现。
优点:对定位数据快。
缺点:对插入、删除数据慢。 -
3.2.2 LinkedList
提供了List ADT的双链表实现。除了实现双向链表Deque,还是实现了Queue接口(队列),其常用方法有:offer 在最后添加元素,poll 取出第一个元素,peek 查看第一个元素。
优点:插入、删除数据快。
缺点:定位数据慢。
-
4.树
对于大量的输入数据,链表的线性访问时间太慢。由此引入树的概念。
- 4.1 树
一棵树是一些节点的集合。这个集合可以是空集;若不是空集,则树由称作根的节点r以及0个或多个非空的子树组成,这些子树中每一棵的根都被来自根r的一条有向的边所连接。定义树的一种自然的方式是递归的方式。树的逻辑很简单:除根节点之外的每个节点只有一个父节点,根节点没有父节点;除叶节点之外的所有节点都有一个或多个子节点,叶节点没有子节点。 - 4.2 二叉树
二叉树是树的一种特除的结构,在二叉树中每个节点最多只能有两个子节点。学习二叉树最重要的操作就是遍历,通常有三种遍历方式:
前序遍历:根左右
中序遍历:左根右
后序遍历:左右根
这3种遍历都有递归和循环两种不同的实现方法,每种遍历的递归实现都比循环实现要简介很多,学习的时候应该对这3中遍历的6种实现方法都了如指掌。
除此以外,宽度优先遍历也是较为常见的遍历方式
同时,二叉树有很多特例:- 4.2.1二叉搜索树
在二叉搜索树(二叉查找树/二叉排序树)中,左子节点总是小于根节点,而右子节点总是大于根节点。平均可以在O(logn)的时间内根据数值在二叉搜索树中找到一个节点。 - 4.2.2二叉平衡树(AVL树)
带有平衡条件的二叉查找树。一棵AVL树是其每个节点的左子树和右子树的高度最多差1的二叉查找树。 - 4.2.3堆
堆分为最大堆和最小堆。在最大堆中根节点的值最大,在最小堆中根节点的值最小。有很多需要快速找到最大值或者最小值的问题都可以用堆来解决。 - 4.2.4红黑树
红黑树是把树中的节点定位红、黑两种颜色,并通过规则确保从根节点到叶节点的最长路径的长度不超过最短路径的两倍。
- 4.2.1二叉搜索树
5.栈和队列
-
5.1栈
栈在计算机领域被广泛应用,如操作系统会给每个线程创建一个栈用来存储函数调用时各个函数的参数、返回地址及临时变量等。- 5.1.1栈的特点
后进先出 - 5.1.2栈的基本操作
栈是一个限制插入和删除只能在一个位置上进行的表。该位置在表的末端,叫做栈的顶(top)。对栈的基本操作有push(进栈)和pop(出栈),最后插入的元素可以通过使用top在只能pop之前进行考查。对空栈进行pop或top一般被认为是栈ADT中的一个错误。同时,当运行push时空间用尽是一个实现限制,但不是ADT错误。
通常栈是一个不考虑排序的数据结构,我们需要在O(n)时间才能找到栈中最大或者最小的元素。如果想要在O(1)时间内得到栈的最大值或最小值,则需要对栈做特殊的设计。 - 5.1.3栈的实现
由于栈是一个表,所以任何实现表的方法都能实现栈。ArrayList和LinkedList都支持栈操作。栈的实现主要有两种方式:
(1)栈的链表实现
使用单链表,在表的顶端插入来实现push,通过删除表顶端元素实现pop。Top操作为考查表顶端元素并返回它的值。
(2) 栈的数组实现
该方法模仿ArrayList的add操作。创建数组theArray以及变量topOfStack。使topOfStack增1然后设置theArray[topOfStack]=x来实现push x元素的操作;通过设置返回值为theArray[topOfStack]同时使topOfStack减1来实现pop操作。该方法可以大大提升时间效率。
- 5.1.1栈的特点
-
5.2队列
和栈一样,队列也是表,但是队列是插入在一端进行而删除在另一端进行的表。- 5.2.1 队列的特点
先进先出 - 5.2.2 队列的基本操作
队列的基本操作是enqueue(入队)和dequeue(出队)。enqueue在表的末端插入一个元素,dequeue删除在表的开头的元素。 - 5.2.3 队列的实现
由于队列是一个表,所以任何表的方法都能实现队列。
(1) 队列的链表实现 比较简单,暂不详述。
(2) 队列的数组实现
我们创建数组theArray和变量front和back分别代表队列的两端位置。同时还要记录实际存在于队列中的元素的个数currentSize。入队操作为:让currentSize和back增1,然后设置theArray[back]=x;出队操作:设置返回值为theArray[front],同时currentSize减1,同时front增1。同时为了避免潜在的问题,针对front或back增1导致超越了数组,设置其值重置到数组的第一个位置。
- 5.2.1 队列的特点
-
5.3 两者联系
根据两者的特点,可以采用两个队列实现栈,或者两个栈实现队列等操作。
参考文献
【1】“剑指Offer第二版,何海涛,电子工业出版社,北京,2019.3”
【2】数据结构与算法分析 Java语言描述,Mark Allen Weiss(著)冯舜玺(译),机械工业出版社,2014.12”
【3】http://how2j.cn/?p=41528