3. 表、栈、队列
3.1 抽象数据类型(ADT)
- 表的一些简单操作可以通过使用数组来完成,如果发生对表的一些插入和删除操作,特别是对表的前端进行,那么数组就不是一种很好的选择了,就需要使用链表了。链表的查找效率低不如数组,但是其插入和删除方便。
3.2 Java CollectionsAPI中的表
Java.util包中Collection接口的子集有:size()、isEmpty()、clear()、contains()、add()、remove()、iterator()。
3.2.1 Iterator 接口
- Iterator接口集合必提供一个称为iterator()的方法,该方法返回一个叫做Iterator类型的对象。其包含的方法有hasNext()、next()、remove();hasNext用来告诉是否存在下一项。
3.2.2 List接口,ArrayList类和LinkedList类
- List(有序,可重复)接口继承了Collection接口的所有方法,外加其他的一些方法。如:get(int idx)、set(int idx,AnyType newVd)、add(int idx,AnyType x),remove(int idx),listIterator(int pos).
ArrayList、LinkedList、vector区别:
ArrayList:底层数据结构是数组。特点:查询快、增删慢、线程不同步、效率高。
LinkedList:底层数据结构是链表。特点:查询慢、增删快、线程不同步、效率高。
vector:底层数据结构是数组。特点:查询快、增删慢、线程同步、效率低,被ArrayList代替了。
LinkedList对get的调用效率不高,对remove调用同样低效,为了提高效率用一个迭代器一步步遍历该表。
public static void removeEventver3(List<Integer>lst)
{
Iterator <Integer> itr=lst.iterator();
while(itr.hasNext())
if(itr.next()%2==0)
itr.remove();
}
删除表中的偶数,对ArrayList是二次的,但对LinkedList是线性的。
3.2.1 关于ListIterator 接口
ListIterator扩展了List的Iterator 功能,方法pervious,hasprevious使得对表从后向前的遍历得以完成,add方法将一个新的项以当前位置放入表中,还有一个set()方法
3.3ArrayList类的实现
内部方法包括:
clear(),size(),isEmpty(),trimTosize(),get(int idx),set(int idx,AnyType newval),ensureCapacity(int newCapacity),add(AnyType x),add(int idx,AnyType x),remove(int idx),iterator()...
3.3LinkedList类的实现
链表的插入图:
Node newNode=new Node(x,p.prev,p);//1,2步
p.prev.next=newNode;//3步
p.prev=newNode;//4步
第3步,和第4步合并后:
Node newNode=new Node(x,p.prev,p);//1,2
p.prev=p.prev.next=newNode;//3,4
LinkedList包括的方法有:clear(),size(),isEmpty(),add(AnyType x),add(int idx,AnyType x),get(int idx),set(int idx,Anytype newval),remove(int idx),addBefore(Node<Anytype>p,Anytype x),remove(Node<Anytype> p),getNode(int idx).
从双链表删除右P指定的节点:
p.next.prev=p.prev;
p.prev.next=p.next;
3.4 栈的ADT
栈:限制插入与删除只能在一个位置的表,有时又叫LIFO(后进后出表),通过push向栈输入,通过pop和top从栈中输出。
- 由于栈是一个表,因此如何实现表的方法都可以实现栈
计算后缀表达式花费的时间是:O(N)
3.4 队列的ADT
像栈一样,队列(queue)也是表,使用队列时,插入在一端删除在另一端进行。
enqueue(入队),在末端(堆尾(rear))插入一个元素
dequeue(出队),在表头(又叫队头(front))删除
链表实现和数组实现都给出了快速的o(1)运行时间。
4. 树
- 对于大量的输入数据,链表的运行时间太慢,不宜使用。本章将讨论一种简单的数据结构,其大部分运行时间平均为o(log N)
- 二叉查找数是两种库集合TreeSet和TreeMap实现的基础
4.1 具备知识
- 树(Tree)的一种自然方式是递归方式,一颗树是一些节点的集合,如图:
- 树的遍历分为:前序遍历,后序遍历,中序遍历,层次遍历
4.2 二叉树
二叉树其深度平均值为:o(logN).4.2 表达式树
4.3 查找树ADT---二叉查找树
性质:对于树中的每个节点X,它的左子树所有项小于X中项,而它的右子树所有的项大于X中的项。
二叉查找树的一些方法:contains()、findMin()、findMax()
采用递归方式:
private BinaryNode<AnyType> findMin(BinaryNode<Anytype> t)
{ if(t==null) return null; else if(t.left==null) return t; return fi
ndMin(t.left);
}
//采用非递归方式:
private BinaryNode<AnyType> findMax(BinaryNode<Anytype> t) { if(t!=null) return null; while(t.right!=null) t=t.right; return t; }
//insert方法:
private BinaryNode<AnyType> insert(Anytype x,BinaryNode<Anytype> t)
{
if(t!=null)
return new BinaryNode<AnyType>(x,null,null);
int compareResult=x.compareTo(t.element);
if(compareResult<0)
t.left=insert(x,t.left);
else if(compareResult>0)
t.right=iinsert(x,t.right);
else
;
return t;
}
4.4 AVL树
Avl树是带有平衡条件的二叉查找树,它保证了树的深度是o(logn),要求左右子树具有相同的高度。
- 插入操作隐含困难在于,插入一个节点可能破坏AVL树的特性,我们通过旋转来对它进行修正。
分以下两种情况:
1. 第一种情况在”外边”的情况:
左---左或者右---右 单旋转
2. 第二种情况在“内部”情况:
左---右或者右---左 双旋转