1、算法、数据结构、数据模型?
- 算法:数学里面的运算在计算器里面特有的显示,包括一些指令来得到相应的一些结果,比如下五子棋的时候,里面对应的规则和策略就好比是算法。
- 数据模型:而数据结构就好比是数据的模型,对于五子棋来说,外部的人,棋子,棋谱的表示。
- 数据结构:数据之间相互存在的一种或者多种特定的关系的元素的集合。
2、逻辑结构分析
- 1、集合结构:各节点之间没有任何关系,而且里面的节点不能有重复的
- 2、线性结构:一条线
- 3、树形结构:只有一个根节点,下面都是一些子树节点
- 4、图形结构 :数据之间存在一对多的关系
3、物理结构(存储结构)
- 1、顺序存储结构()
数据地址(栈的空间地址)按照一定的顺序排列起来,那这就是顺序存储结构。由1能得到2 ,由2能得到3 ….
假设现在来一个人插队的话,后面的所有数据都会往后面退一步。
- 2、链式存储结构()
数据不是按照顺序排列起来的,它们之间是单线联系,比如潜伏里面,1是2的上司,2是3的上司,假如有一天,2被干掉,3就会迷失。但是好处就是不会被一锅端掉。。哈哈
抽象数据类型
一个数字模型以及定义在该模型上的一组抽象,相当于面向对象里面的类有一些公共的属性,然后将这些属性提取出来作为一种抽象类,让其子类去继承
线性表(List)
其中,a1是a2的前驱,a(i+1)是ai的后继,a1是没有前驱,an是没有后驱(n是线性表的长度,若n==0时,线性表为空表)
1、下面的就是一个典型的线性表
2、下面看看线性表的顺序存储方式
存储的位置是连续的,可以很方便的计算各个元素的地址。比如说a2就等于a1的地址+a1的长度
上面的图说明,如果这个骗子走了,后面的人都得顶上去。这就说明了这种方式的实现比较复杂,消耗的代价也很大。
总结:
查找的时候效率特别高,但是插入和删除的时候效率比较低
3、分析一下ArrayList的源码
在java里面,数组都是一个很典型的线性表的存储结构,而ArrayList里面肯定包含了一个数组,所以我们来分析一下它的部分源码
上面的ArrayList继承AbstractList,AbstractList是一个抽象类,里面定义了对数据的增删改查操作。同样的对我们后面的LinkList(链式列表)来说,同样也存在一些增删改查,那么也会继承这个抽象类。
那么我们看一下,它的add方法在子类ArrayList里面的实现
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return <tt>true</tt> (as specified by {@link Collection#add})
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1); // 主要是是为了扩容,并且判断是否为空
elementData[size++] = e;
return true;
}
上面主要是干了两件事,第一件是给判断size是否满了,如果满了就扩容一次。第二件时给elementData数组赋值。这里面我们能看出来ArrayList里面其实也就是数组的添加
下面我们看一下扩容里面干了些什么事情
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);//取最大值
}
ensureExplicitCapacity(minCapacity);
}
上面判断当enementData里面没有数据的时候,我们来获取取Size的大小。取minCapacity和DEFAULT_CAPACITY中的最大值
接着往后看
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
modCount++这其实可以理解为用来计量的,也就是说,只要数据动一下,我就计量一次。if里面我们就能分析出来,当我们传入的minCapacity比这个elementData.length大的时候,我们就进行扩容。
/**
* Increases the capacity to ensure that it can hold at least the
* number of elements specified by the minimum capacity argument.
*
* @param minCapacity the desired minimum capacity
*/
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
上面的方法都很好理解,主要就是解决给扩容多少。接着 Arrays.copyOf就是拷贝数据了,最后我们获得的数组就是这个最新的数组了。
这样就完成了ArrayList数据的添加了
4、下面看看线性表的链式存储方式
一组任意的存储单元存储线性表的数据元素,它们之间可以是连续的,也可以使不连续的
上面的图p包含两个部分,ai代表的是数据域,后面白色的代表的是指针域,里面存放的是p后面那个p->next元素的地址
- 优缺点
优点:删除、插入的效率高
缺点:查询的效率低
插入
删除
这个图p、p->next两个节点,现在要在这两个之间插进去一个元素,只需要将P节点中指向下一个节点的地址到e节点,而e节点的指向下一个节点的地址指向**p–>nex**t就可以了,而后面的数据都不需要改变。足以说明链式表的插入和删除的效率是相当的高
查询的时候,得一个一个的从头最开始的地方查,所以效率很低
5、分析一下LinkList的源码
首先,先上张图
LinkList里面存放了一个头指针,当有数据来的时候,只需要将头指针后面的指针域指向下一个数据,这样就形成了一条循环的列表,也成为单循环列表。如果没有数据的话,next就会指向自己
源码中Node就是我们的指针,里面有first,last说明是个双向链表。
接着我们看一下add方法
通过上面的注释也能看出来是在末尾添加一个元素,接着往下看
- 第一步是找到最后这个element
- 第二步是将传捡来的E封装成新Node对象
- 第三步是将这个新的Node对象赋值给最后这个element
- 判断最后这个element是不是为null,如果是null的话,就让自己为这个新的element,如果不是null的话,让它的下一个为element
- 最后就是操作++,长度++
在画个图来说明一下,双向链表的添加
1的后驱指向2的前驱,以此类推,直到最后,5的后驱又指向1的前驱,下面那个2的前驱指向1的后驱,以此类推,1的前驱也指向了5的后驱。这样就是一个双向链表。
如果我们要添加元素的话,就将中间的两个链打断,按照上面的连接,也就是4步操作就可以了。