基础编程模型
-
递归
- 递归总有一个最简单的情况—方法的第一条语句总是一个包含return的条件语句
- 递归调用总是去尝试解决一个规模更小的子问题
- 递归调用的父问题和尝试解决的子问题不应该有交集
-
二分查找
public int rank(int key,int[] a) {//数组必须是有序的 //二分查找 int lo = 0; int hi = a.length - 1; while (lo <= hi) { int mid = lo + (hi - lo) / 2; if (key < a[mid]) { hi = mid - 1; } else if (key > a[mid]) { lo = mid + 1; } else { return mid; } } return -1; }
数据抽象
-
对象:能承载数据类型的值的实体,有三大特性:状态(数据类型的值)、标识(将一个对象区别于另一个对象)、行为(数据类型的操作)
-
但用例调用new()
- 为新的对象分配内存空间
- 调用构造函数初始化对象中的值
- 返回该对象的一个引用,被变量关联
-
使用引用类型的赋值语句将会创建该引用的一个副本。赋值语句不会创建新的对象,而只是创建另一个已经指向某个已经存在的对象的引用。这种情况被称为别名:两个变量同时指向一个对象。容易引起bug(按值传递)
-
字符串
[外链图片转存失败(img-ALF8Hyh9-1562589069735)(C:\Users\z1664\AppData\Roaming\Typora\typora-user-images\1560926056140.png)]
-
等价性:如果我们将想通类型的两个引用变量a和b进行等价性车市(a==b),我们检测的是其表示是否相同,即引用是否相同,即只有a和b关联了通一个对象的引用,a = = b才为true,如果我们想要检测两个对象的状态(数据类型的值)是否相等,需要重载equals()方法
背包、队列。栈
背包(Bag),队列(Queue),栈(Stack)是三种与对象的集合有关的基础数据类型,它们的区别在于删除或者访问对象的顺序不同
-
背包:不支持从中删除元素,迭代的顺序不确定且于用例无关,使用Bag说明元素的处理顺序不重要
public class Bag<item> Bag() 创建一个新背包 void add(Item item) 添加一个元素 boolean isEmpty() 是否为空 int size() 元素数量
-
队列:先进先出(FIFO)
public class queue<item> Queue() 创建一个新队列 enqueue()(入队)加入元素 dequeue() (出队)除去元素 boolean isEmpty() 是否为空 int size() 元素的数量
-
栈:后进先出(LIFO)
public class stack<item> stack() 创建一个新栈 void push (入栈 ) 插入元素 item pop (出栈)除去元素 boolean isEmpty() 栈是否为空 int size() 栈中元素的数量
import java.util.Iterator; /** * 可动态改变数组大小的下压栈的实现,可作为背包,队列等数据结构实现的模板 */ public class ResizingArrayStack<Item> implements Iterable<Item> { private Item[] a = (Item[]) new Object[1];//栈元素 java不支持泛型的数组 private int N = 0;//元素数量 public boolean isEmpty() { return N == 0; } public int size() { return N; } private void resize(int max) {//动态改变数组的大小,该方法私有,用户不必知道实现的具体细节 //将栈移动到一个大小为max的新数组 Item[] temp = (Item[]) new Object[max]; for (int i = 0; i < N; i++) { temp[i] = a[i]; } a = temp; } public void push(Item item) { //将元素添加到栈顶 if (N == a.length) resize(2 * a.length); a[N++] = item; } public Item pop() { //从栈顶删除元素 if (isEmpty()) { return null; } else { //return a[--N];直接返回 但会产生对象游离 Item item = a[--N]; a[N] = null;//N已经被减 if (N > 0 && N == a.length / 4) resize(a.length / 2); return item; } } @Override public Iterator iterator() { return new ReverseArrayIterater(); } private class ReverseArrayIterater implements Iterator<Item> { private int i = N;//以下方法对i进行操作而不改变N的值 @Override public boolean hasNext() { return i > 0; } @Override public Item next() { return a[--i]; } } }
-
链表(与数组并列,分别对应链式存储和顺序存储)
-
定义:一种递归的数据结构,它或者为空,或者是指向一个结点(node)的引用,该结点含有一个泛型的元素和一个指向另一条链表的引用。
private class Node{//私有 不必为用例所知道 Item item; Node next; }
-
构造链表
Node first = new Node(); Node second = new Node(); Node third = new Node(); first.item = "to"; second.item = "be"; thir.item = "or"; first.next = second; second.next = third;
链表表示的是一列元素,所不同于数组之处,在于其增删更为方便
-
在表头插入结点(所需时间和链表的长度无关)
Node oldFirst = first; first = new Node(); first.item = "not"; first.next = oldFirst;
-
在表头删除结点
first = first.next;
-
在表尾插入结点
Node oldLast = lsat; last = new Node(); last.item = "not"; oldLast.next = last;
-
实现任意插入和删除 需使用双向链表
private class Node{ Node previous; Node next; Item item; } //假设已有结点a,b,c,d,欲删除b结点 b.previous.next = b.next; b.next.previous = b.previous; //假设已有a,c,d结点,欲在c之前添加b结点 c.previous.next = b; b.next = c; b.previous = c.previous; c.previous = b;
-
import java.util.Iterator;
/**
* 下压堆栈(链表实现)
*/
public class Stack<Item> implements Iterable<Item> {
private Node first;//栈顶(最近添加的元素)
private int N = 0;//元素数量
private class Node {
Item item;
Node next;
}
public boolean isEmpty() {
return N == 0;//或first==null;
}
public int size() {
return N;
}
public void push(Item item) {
//向栈顶添加元素
Node oldfirst = first;
first = new Node();
first.item = item;
first.next = oldfirst;
N++;
}
public Item pop() {
//从栈顶删除元素
Item item = first.item;
first = first.next;
N--;
return item;
}
@Override
public Iterator<Item> iterator() {
return new ListIterator();
}
private class ListIterator implements Iterator<Item> {
private Node current = first;
@Override
public boolean hasNext() {
return current != null;
}
@Override
public Item next() {
Item item = current.item;
current = current.next;
return item;
}
}
}
import java.util.Iterator;
/**
* 先进先出队列的链表实现
*/
public class Queue<Item> implements Iterable<Item> {
private Node first;//最早添加的结点
private Node last;//最晚添加的节点
private int N;
private class Node {
Node next;
Item item;
}
public boolean isEmpty() {
return first == null;
}//或:N==0
public int size() {
return N;
}
public void enqueue(Item item) {
//向表尾添加元素
Node oldlast = last;
last = new Node();
last.item = item;
if (isEmpty()) {//链表为空,将first和last都指向新创建的结点
first = last;
} else {
oldlast.next = last;
}
N++;
}
public Item dequeue() {
//从表头删除元素
Item item = first.item;
first = first.next;
if (isEmpty()) {//如果链表为空,将last更新为null
last = null;
}
N--;
return item;
}
@Override
public Iterator<Item> iterator() {
return new ListIterator();
}
private class ListIterator implements Iterator<Item> {
private Node current = first;
@Override
public boolean hasNext() {
return current != null;
}
@Override
public Item next() {
Item item = current.item;
current = current.next;
return item;
}
}
}
import java.util.Iterator;
/**
* 背包(链表实现)
*/
public class Stack<Item> implements Iterable<Item> {
private Node first;//链表首结点
private int N = 0;//元素数量
private class Node {
Item item;
Node next;
}
public boolean isEmpty() {
return N == 0;//或first==null;
}
public int size() {
return N;
}
public void add(Item item) {
//和stack的push方法完全相同
Node oldfirst = first;
first = new Node();
first.item = item;
first.next = oldfirst;
N++;
}
//三种数据结构对于迭代的实现相同 但stack和queue的访问顺序不同
@Override
public Iterator<Item> iterator() {
return new ListIterator();
}
private class ListIterator implements Iterator<Item> {
private Node current = first;
@Override
public boolean hasNext() {
return current != null;
}
@Override
public Item next() {
Item item = current.item;
current = current.next;
return item;
}
}
}
算法分析
- 执行最频繁的指令(内循环)决定了程序运行的总时间
- 内存:(内存的使用会被填充为8字节的整数倍)
- 对象
- 实例变量
- 对象本身的开销(一般为16字节):
- 一个指向对象的类的引用(内存地址 占用8个字节)(不包含其所指的对象占用的内存)
- 垃圾收集信息
- 同步信息
- 链表 嵌套有内部类,还需额外的8字节(用于一个指向外部类的引用),所以一个Node对象占用40字节(16字节的对象开销、8字节的额外开销、对item和node对象的引用各占用8字节)
- 数组
- 头信息 24字节
- 对象开销16字节时
- 长度记录 4字节
- 填充4字节
- 两类情况:
- 保存的是原始数据类型 则占用长度乘以数据类型所占用的字节数之积个字节, 如int[N]占用(24+4N)个字节(会被填充为8字节的倍数)
- 保存的是对象的引用,此部分内存占用引用所占的内存与对象所占用的内存之和
- 头信息 24字节
- 字符串对象 40字节(字符所占用的内存另计)(String类型的char数组常常在多个字符串之间共享)
- 16字节表示对象
- 数组引用8字节
- 3个int实例(偏移量、长度、散列值)12字节
息- 同步信息
- 链表 嵌套有内部类,还需额外的8字节(用于一个指向外部类的引用),所以一个Node对象占用40字节(16字节的对象开销、8字节的额外开销、对item和node对象的引用各占用8字节)
- 数组
- 头信息 24字节
- 对象开销16字节时
- 长度记录 4字节
- 填充4字节
- 两类情况:
- 保存的是原始数据类型 则占用长度乘以数据类型所占用的字节数之积个字节, 如int[N]占用(24+4N)个字节(会被填充为8字节的倍数)
- 保存的是对象的引用,此部分内存占用引用所占的内存与对象所占用的内存之和
- 头信息 24字节
- 字符串对象 40字节(字符所占用的内存另计)(String类型的char数组常常在多个字符串之间共享)
- 16字节表示对象
- 数组引用8字节
- 3个int实例(偏移量、长度、散列值)12字节
- 4个填充字节
- 对象