Day15
今天了解一些简单的数据结构和学习LinkedList的有关知识。
1.数据结构与算法
什么是数据结构?
Sartaj Sahni在他的《数据结构、算法与应用》一书中称:“数据结构是数据对象,以及存在于该对象的实例合组成实例的数据元素之间的各种联系。这些联系可以通过定义相关的函数来给出。”他将数据对象(data object)定义为“一个数据对象是实例或值的集合”。
Clifford A.Shaffer在《数据结构与算法分析》一书中的定义是:“数据结构是 ADT(抽象数据类型Abstract Data Type) 的物理实现。”
大话数据结构:数据结构是相互之间存在一种或多种特定关系的数据元素的集合。
什么是算法?
算法是解决问题步骤的有限集合,通常用某一种计算机语言进行伪码描述。通常用时间复杂度和空间复杂度来衡量算法的优劣。
算法的五大特征:输入、输出、有穷性、确定性、可行性。
输入:零个或多个输入。
输出:一个或多个输出。
有穷性:有限步骤后在可接受时间内完成。
确定性:每个步骤都有确定含义,无二义性。
可行性:每一步都是可行的。
算法设计要求:正确性、可读性、健壮性、时间效率高和存储低。
正确性:有输入输出,无二义性,有正确答案。
可读性:方便阅读。
健壮性:输入不合法能处理
时间效率高和存储低:时间空间复杂度越低越好。
1.数组
数组是相同数据类型的元素按一定顺序排列的集合,是一块连续的内存空间。
特点:我们都知道数组中的元素在内存中连续存储的,可以根据是下标快速访问元素,因此,查询(get和set)速度很快,时间上都是O(1)的,然而插入和删除(add和remove)时,需要对元素移动空间,比较慢,操作时间上都是O(N)的。
Java中,Array就是数组,此外,ArrayList使用了数组Array作为其实现基础,它和一般的Array相比,最大的好处是,我们在添加元素时不必考虑越界,元素超出数组容量时,它会自动扩张保证容量。
Vector和ArrayList相比,主要差别就在于多了一个线程安全性,但是效率比较低下。如今java.util.concurrent包提供了许多线程安全的集合类(比如 LinkedBlockingQueue),所以不必再使用Vector了。
数组使用场景:频繁查询,很少增加和删除的情况。
2.链表
链表是一种非连续、非顺序的结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的,链表由一系列结点组成。
Java中,LinkedList 使用链表作为其基础实现。
特点:元素可以不连续内存中,是以索引将数据联系起来的,当查询(get和set)元素的时候需要从头开始查询,操作时间上都是O(N)的,而且需要额外的空间存储指向其他数据地址的项。,所以效率比较低。然而添加和删除(add和remove)的只需要修改索引就可以了,操作时间上都是O(1)的。
查找操作对于未排序的数组和链表时间上都是O(N)。
使用场景:少查询,需要频繁的插入或删除情况
3.队列
队列是一种特殊的线性表,特殊之处在于它只允许在表的前端进行删除操作,而在表的后端进行插入操作,亦即所谓的先进先出(FIFO)。
特点:先进先出,
Java中,LinkedList实现了Deque,可以做为双向队列(自然也可以用作单向队列)。另外PriorityQueue实现了带优先级的队列,亦即队列的每一个元素都有优先级,且元素按照优先级排序。
使用场景:多线程阻塞队列管理非常有用
4.栈
栈(stack)又名堆栈,它是一种运算受限的线性表。其限制是仅允许在表的一端进行插入和删除运算。这一端被称为栈顶,相对地,把另一端称为栈底。它体现了后进先出(LIFO)的特点。
特点:先进后出。
Java中,Stack实现了这种特性,但是Stack也继承了Vector,所以具有线程安全线和效率低下两个特性,最新的JDK8中,推荐用Deque来实现栈,比如:
使用场景:实现递归以及表示式
5.数组与链表的区别
数组连续,链表不连续(从数据存储形式来说)
数组内存静态分配,链表动态分配
数组查询复杂度0(1),链表查询复杂度O(n)
数组添加或删除,复杂度o(n),链表添加删除,复杂度O(1)
数组从栈中分配内存。链表从堆中分配内存。
6.集合
集合是指具有某种特定性质的具体的或抽象的对象汇总成的集体,这些对象称为该集合的元素,其主要特性是元素不可重复。
在Java中,HashSet体现了这种数据结构,而HashSet是在MashMap的基础上构建的。LinkedHashSet继承了HashSet,使用HashCode确定在集合中的位置,使用链表的方式确定位置,所以有顺序。TreeSet实现了SortedSet 接口,是排好序的集合(在TreeMap 基础之上构建),因此查找操作比普通的Hashset要快(log(N));插入操作要慢(log(N)),因为要维护有序。
2.LinkedList
LinkedList也是List接口的实现类,与ArrayList不同之处是采用的存储结构不同,ArrayList的数据结构为线性表,而LinkedList数据结构是链表。链表数据结构的特点是每个元素分配的空间不必连续、插入和删除元素时速度非常快、但访问元素的速度较慢。
其内部实现是一个双向(不循环)的链表
优点:对数据经常增加或删除,性能优于ArrayList
缺点:检索数据时不如ArrayList
LinkedList类每个结点用内部类Node表示,LinkedList通过first和last引用分别指向链表的第一个和最后一个元素,当链表为空时,first和last都为NULL值。LinkedList数据结构如下图所示:
1.在LinkedList中添加元素
LinkedList提供了多个添加元素的方法:
●boolean add(E e)
在链表尾部添加一个元素,如果成功,返回true,否则返回false。
●void addFirst(E e)
在链表头部插入一个元素。
●addLast(E e)
在链表尾部添加一个元素。
●void add(int index, E element)
在指定位置插入一个元素。
2.从LinkedList中删除元素
LinkedList提供了多个删除元素的方法:
●boolean remove(Object o)
从当前链表中移除指定的元素。
● E remove(int index)
从当前链表中移除指定位置的元素。
● E removeFirst()
从当前链表中移除第一个元素。
● E removeLast()
从当前链表中移除最后一个元素。
● E remove()
从当前链表中移除第一个元素,同removeLast()相同。
3.从LinkedList中获取元素
LinkedList提供了多个获取元素的方法:
● E get(int index)
从当前链表中获取指定位置的元素。
● E getFirst()
从当前链表中获取第一个元素。
● E getLast()
从当前链表中获取最后一个元素。
4.LinkedList的遍历方法
LinkedList可以通过迭代器、foreach语句、for循环语句等方法遍历集合的所有元素。
foreach语句效率最高,其次是迭代器,效率最差的是for循环。