大话数据结构读书笔记二

Posted by Cfeng on March 8, 2019

线性表

定义

  • 零个或多个数据元素的有限序列 有序,头无前驱,尾无后驱,其他只有一个前驱和后驱。 有限,每个元素有自己确定的位置 星座排序可以看成一个线性表

Java中,数据结构中的线性表,对应着Collection中的List接口。

List

int size();//Returns the number of elements in this list.大小
boolean isEmpty();//Returns {@code true} if this list contains no elements.是否空
boolean contains(Object o);//是否包含
Iterator<E> iterator();//返回一个迭代器,没有用过。。原理待会儿继续看
Object[] toArray();//将列表变为数组返回
boolean add(E e);//在列表末尾增加一个元素
boolean remove(Object o);//移除第一个o元素,没有就不更改
boolean containsAll(Collection<?> c);//是否所有都在
boolean addAll(Collection<? extends E> c);//全部添加
default void sort(Comparator<? super E> c)//排序
void clear();//清空
boolean equals(Object o);//判断相等
int hashCode();//获得hash指
E get(int index);//
E set(int index, E element);//
void add(int index, E element);//添加到指定位置,后续元素向后移
E remove(int index);//移除第index个元素
int indexOf(Object o);//返回第一个元素o第下标
int lastIndexOf(Object o);//返回最后一个元素o第下标
List<E> subList(int fromIndex, int toIndex);//截取列表并返回

大致常用的就这些(第一次看源码诶)

顺序存储结构

  • 用一段地址连续的存储单元一次存储线性表的数据单元 大小固定,起始位置固定。等于占了一块地。
  • 三个属性: 1.存储空间的起始位置:数组data,它的存储位置就是存储空间的存储位置 2.线性表的最大存储容量 3.线性表的当前长度

    数组的长度与线性表的长度

    数组的长度是存放线性表存储空间的长度,存储分配后这个表是不变的。 线性表的长度是线性表中数据元素的个数,随着线性表的插入和删除操作的进行会改变 线性表的长度应该永远小于等于数组的长度

    ArrayList

    ``` private int size;//表示数组大小 private static final int DEFAULT_CAPACITY = 10;//初始长度为10 private static final Object[] EMPTY_ELEMENTDATA = {};//有定义长度的实例初始化new ArrayList(0) private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};//没有定义大小的new ArrayList() //创建 public ArrayList(int initialCapacity) { if (initialCapacity > 0) { this.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) { this.elementData = EMPTY_ELEMENTDATA; } else { throw new IllegalArgumentException(“Illegal Capacity: “+ initialCapacity); } } public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } //插入元素实现( add(E e)调用下面这个 ),如果满了就扩充 private void add(E e, Object[] elementData, int s) { if (s == elementData.length) elementData = grow(); elementData[s] = e; size = s + 1; } //在指定位置插入 public void add(int index, E element) { rangeCheckForAdd(index); modCount++; final int s; Object[] elementData; if ((s = size) == (elementData = this.elementData).length) elementData = grow(); System.arraycopy(elementData, index, elementData, index + 1, s - index); elementData[index] = element; size = s + 1; }

//获得元素 public E get(int index) { Objects.checkIndex(index, size);//判断下标是否合法 return elementData(index); }

//删除元素 public E remove(int index) { Objects.checkIndex(index, size); final Object[] es = elementData; @SuppressWarnings(“unchecked”) E oldValue = (E) es[index]; fastRemove(es, index); return oldValue; } private void fastRemove(Object[] es, int i) { modCount++; final int newSize; if ((newSize = size - 1) > i) System.arraycopy(es, i + 1, es, i, newSize - i); es[size = newSize] = null; }

### 优点
* 无须为表示表中元素之间的逻辑关系而增加额外的存储空间
* 可以快速存取表中任一位置的元素
### 缺点
* 插入和删除操作需要移动大量元素
* 当线性表长度变化很大时,难以确定存储空间容量
* 造成存储空间的碎片
## 链式存储结构
### 头指针与头结点的异同
头指针:
* 头指针是指链表中指向第一个结点的指针,若链表有头指针,则是指向头结点的指针
* 头指针具有标示作用,所以常用头指针冠以链表名字
* 无论链表是否为空,头指针均不为空。头指针是链表**必要元素**

头结点:
* 头结点是为了操作的统一和方便而设立的,放在第一元素的结点之前,其数据意一般无意义(或者放链表长度)。
* 有了头结点,对在第一元素结点前插入元素和删除第一结点,其操作与其他结点的操作就统一起来了。
* 头结点**不是必要元素**

因为单链表比较熟悉,就不多写了。
### 单链表结构与顺序存储的优缺点
* 存储分配方式 :顺序用一段连续的存储单元一次存储线性表的数据元素;单链表采用链式结构,用一组任意的存储单元存放线性表元素。
* 时间性能:查找 顺序O(1),单链表O(n);插入和删除 顺序平均移动表长一半的元素O(n),单链表 在找出某位置的指针后只要O(1)
* 空间性能: 顺序需要预分配存储空间,分大了浪费,分小了易发生上溢;单链表不需要分配存储空间,元素个数也不受限制

## 静态链表
* 用数组实现的链表,首位存第一个结点的下标相当于头指针,最后一个元素村第一个插入的元素下标,相当于头结点。
## 循环链表
* 将单链表中端结点端指针端由空结点改为指向头结点,就使整个单链表形成一个环,简称循环链表。

## 双向链表
* 在单链表中的每个结点,在设置一个指向其前驱结点的指针域。

//结点定义 private static class Node { E item; Node next; Node prev; Node(Node prev, E element, Node next) { this.item = element; this.next = next; this.prev = prev; } }

### LinkedList各种方法的实现

transient int size = 0;//大小 transient Node first;//指向第一个 transient Node last;//指向最后一个 //创建一个空的list public LinkedList() { } //在头部添加 private void linkFirst(E e) { final Node f = first; final Node newNode = new Node<>(null, e, f); first = newNode; if (f == null) last = newNode; else f.prev = newNode; size++; modCount++; } //在尾部添加 void linkLast(E e) { final Node l = last; final Node newNode = new Node<>(l, e, null); last = newNode; if (l == null) first = newNode; else l.next = newNode; size++; modCount++; }

//在中间某处添加——– //在某结点前添加 void linkBefore(E e, Node succ) { // assert succ != null; final Node pred = succ.prev; final Node newNode = new Node<>(pred, e, succ); succ.prev = newNode; if (pred == null) first = newNode; else pred.next = newNode; size++; modCount++; } //查找第index个结点 Node node(int index) { if (index < (size >> 1)) { Node x = first; for (int i = 0; i < index; i++) x = x.next; return x; } else { Node x = last; for (int i = size - 1; i > index; i--) x = x.prev; return x; } } //按照坐标添加 public void add(int index, E element) { checkPositionIndex(index); if (index == size) linkLast(element); else linkBefore(element, node(index)); }

//删除元素 E unlink(Node x) { final E element = x.item; final Node next = x.next; final Node prev = x.prev; if (prev == null) { first = next; } else { prev.next = next; x.prev = null; } if (next == null) { last = prev; } else { next.prev = prev; x.next = null; } x.item = null; size--; modCount++; return element; } ```