总结 栈和队列都可以用数组和链表(这里说双向链表,因为如果是单向链表的话,他的查找复杂度为O(N),)实现,但是对于栈来说,先进后出,对于删除和添加都是操作数组或者链表最后一个元素,所以对于只操作尾巴的元素实现栈用数组和链表都是复杂度为O(1).对于队列来说是频繁的在删除队头,添加队尾,用链表复杂度为O(1),用数组的话复杂度为在数组头部添加或者移除元素需要将数组中的每个元素依次移动到当前索引的下一个或者上一个索引,才能将元素(放到或者删除)索引为0的位置记队头.复杂度为O(n),所以队列用链表实现比较好
循环队列(Circle Queue)
队列一般只能从队尾入队,队头出队(用链表实现队列)
双端队列 是能在头尾两端添加,删除的队列
用栈实现队列
但其实队列底层也可以使用动态数组实现,并且各项接口可以优化到O(1)的时间复杂度,这个用数组实现并且优化之后的队列也叫循环队列
只能头删除,尾添加 是循环队列,头尾都可以删除添加操作是循环队列叫循环双端队列
// 下面的这个功能本质上就是将之前的索引映射到现在循环数组的真实索引,这个操作是将之前的索引转换为循环队列的真实索引
elements[(front + size) % elements.length] = element;
尽量避免使用乘* 除/ 模% 浮点数运算,效率低下
private int mapCircleQueueIndex(int index) {
return (front + index) % elements.length;
}
写为
private int mapCircleQueueIndex(int index) {
// return (front + index) % elements.length;
index += front;
// return index % elements.length;
// 因为队列中最大的index + front也不会超过2被的elements.length所以可以套用下面的公式
return index - (index >= elements.length ? elements.length : 0);
}
推理过程
int n = 19;
int m = 10;
// n % m
if (n >= m) {
System.out.println( n - m);
}else{
System.out.println( n - 0);
}
// n % m的条件是m > 0, n >= 0, n < 2m
System.out.println(n - (n >= m ? m : 0));
n % m = n < m ? n : n - m;
动态数组:开辟,销毁内存空间的次数相对较少,缺点是造成内存空间大量浪费
双向链表:开辟,销毁内存空间的次数相对较多,用多少就申请多少内存(单向链表只有后驱指针,而双向链表前驱后记两个指针,单向循环链表,指最后一个元素记索引为size -1的元素的next指向firstNode,双向循环链表指firstNode的pre 指向LinkedList的最后一个元素(记索引为size -1的元素 ),LinkedList的最后一个Node的next为firstNode.)
动态数组明显的缺点是造成内存空间大量浪费,用多少就申请多少内存用链表(Linked List),
顺序存储:所有申请的内存空间都是连续的
链式存储:内存空间不连续的线性表
链表反转
node* reverseList(node* H)
{
if (H == NULL || H->next == NULL) //链表为空或者仅1个数直接返回
return H;
node* p = H, *newH = NULL;
while (p != NULL) //一直迭代到链尾
{
node* tmp = p->next; //暂存p下一个地址,防止变化指针指向后找不到后续的数
p->next = newH; //p->next指向前一个空间
newH = p; //新链表的头移动到p,扩长一步链表
p = tmp; //p指向原始链表p指向的下一个空间
}
return newH;
}
ArrayList中如果是数组中存在
size:表示真正有元素的是多少个,是算真正存在的数的,不是算整体容量的
elements的容量是elements所指向数组的长度
size
int[] elements
public static final Element_OUT_FOUND = -1
构造函数public ArrayList(int capacity)
public void clear();
public void add(int index, E element);
public void add(int element, int index) {
ensureCapcity(size+1);
int value = elements[i];
for (int i = size; i > index; i--) {
elements[i] = elements[i-1];
}
elements[index] = value;
size++;
}
public E remove(int index)
public E remove(int index) {
rangeCheck(index);
E old = elements[index];
for (int i = index + 1; i < size; i++) {
elements[i - 1] = elements[i];
}
elements[--size] = null;
return old;
}
privat void ensureCapacity(int capacity)
private void ensureCapcity(int capacity) {
int oldCapacity = elements.length;
if (oldCapacity >= capacity) {
return;
}else{
int newCapacity = oldCapacity + (oldCapacity >> 1);
int[] newElements = new int[newCapacity];
for (int i = 0; i < size; i++) {
newElements[i] = elements[i];
}
elements = newElements;
}
}
public static final int DEFAULT_CAPACITY = 10;
java中私有常量,java编程规范常量为大写
final是常量的意思相等与别的编程语言const
static是静态的意思,静态能保证它的内存只有一份
框架内部的东西是自己设计,别人用我们的东西
比如ArrayList中的size内部的设计和外面的语义可以不一样
public boolean isEmpty() {
return size == 0;
}