数据结构
学习笔记文档
作者--Alianer
前言/主要参考
前言:
主要记录数据结构的核心思想和学习上目前遇到的问题及其解决方法,记录自己的学习思想。部分的简单的方法只写具体思想和注意事项,具体实现不再概述,基本上都是符合条件下的元素互换等等的操作。
主要参考文献及视频资料如下:
- 文献:百度,CSDN等大佬分享
- 视频:哔哩哔哩学习网(主要是黑马的视频)
线性表
描述:
- 定义:线性表是具有相同类型的 n (n>=0) 个元素的有限序列,其中 n 为表长,当 n=0 时,该表为空表。
- 分类:我们说“线性”和“非线性”,只在逻辑层次上讨论,而不考虑存储层次,所以双向链表和循环链表依旧是线性表。在数据结构逻辑层次上细分,线性表可分为一般线性表和受限线性表。一般线性表也就是我们通常所说的“线性表”,可以自由的删除或添加结点。受限线性表主要包括栈和队列,受限表示对结点的操作受限制。
- 优点:线性表的逻辑结构简单,便于实现和操作。因此,线性表这种数据结构在实际应用中是广泛采用的一种数据结构。
特点:
- 表中元素个数有限
- 表中元素具有逻辑上的顺序性,在序列中各个元素排序有其先后次序
- 表中元素都是数据元素,每个元素都是单个元素
- 表中元素的数据类型都相同,这意味着每个元素占有相同大小的存储空间
- 表中元素具有抽象性,即讨论元素间一对一的逻辑关系,而不考虑元素究竟表示的内容
- 线性表是一种逻辑结构,表示元素之间一对一相邻的关系
顺序表
描述:
顺序表是在计算机内存中以数组的形式保存的线性表,线性表的顺序存储是指用一组地址连续的存储单元,依次存储线性表中的各个元素、使得线性表中再逻辑结构上相邻的数据元素存储在相邻的物理存储单元中,即通过数据元素物理存储的相邻关系来反映数据元素之间逻辑上的相邻关系。
核心思想:
顺序表基础是数组形式,所以基本可以使用数组的思想实现
方法 | 实现方式 |
---|---|
构造方法 | 初始化数组a[ ],初始化表长N=0 |
清空表 | 循环遍历表的元素并且赋值位null,最后把表长置0 |
判断是否空表 | 可以直接获取表长N判断是否为空,空返回true,否则返回false |
获取表长 | return N |
获取指定位置元素 | return a[位置] |
添加元素 | 判断添加时候如果顺序表容量不够则扩容 1.在表尾添加元素:a[N++] = 元素,N++ 2.在指定位置插入元素 :指定位置及其后面的元素依次后移,然后再该位置插入元素,N++ |
删除指定元素 | 判断删除的元素是否是尾部元素,如果是直接删除,然后N– 如果不是尾部元素,删除元素,然后指定位置之后的元素依次前移,然后N– 判断删除元素后剩余容量过大而对顺序表进行缩容 |
查找元素第一次出现的位置 | 通过for循环遍历数组a[ ],依次比较元素和目标的值,如果找到则返回数组位置下标,如果遍历完顺序表都没找到则返回 -1 |
修改指定位置元素 | 直接获取该元素的位置a[位置],然后直接修改元素即可,a[位置] = 修改值 |
遍历表 | 主要实现顺序表支持迭代/foreach循还(增强型的for循环) 1.实现Iterator接口重写iterator方法 2.在顺序表类内部提供一个内部类用于实现Iterator接口,重写hasNext和next方法 |
遍历表支持迭代和foreach循环的具体实现:
public class SequenceList<T> implements Iterable<T>{
// 存放元素的数组
private T[] a;
// 记录元素个数
private int N;
//中间的基本的一些方法省略 ...
}
@Override
public Iterator<T> iterator() {
// 返回的Iterator对象,创建一个内部类实现这个接口
return new SIterator();
}
// 创建一个内部类实现Iterator接口
public class SIterator implements Iterator{
// 定义一个遍历的游标
private int cusor;
public SIterator(){
// 初始化为0索引位置
this.cusor=0;
}
//重写两个方法
@Override
public boolean hasNext() {
// 这个方法判断是否超出最大索引,如果超出会停止遍历
return cusor<N;
}
@Override
public Object next() {
// 这个方法会遍历得每个元素
return a[cusor++];
}
}
容量可变具体实现方法:
// 重置顺序表a大小为newSize
public void resize(int newSize){
// 定义一个临时数组指向原数组
T[] temp = a;
// 创建新数组
a=(T[])new Object[newSize];
// 拷贝原数组到新数组
for(int i=0;i<N;i++)
a[i] = temp[i];
}
在添加插入数据前添加扩容判断:
// 如果容量不足则扩容至原来的2倍
if(N==a.length)
resize(2*a.length);
在删除元素结束时候添加缩容判断:
// 如果容量小于原数组容量的1/4则缩容1/2
if(N<a.length/4)
resize(a.length/2);
性能分析:
增:需要把指定位置及其以后的元素后移所以要n次操作,O(n)
删:删除元素后需要把指定位置以后的元素依次前移,操作n次,O(n)
查/改:不论顺序表长度多大,都只需要一次就可以获取指定位置元素然后操作,所以时间复杂度O(1)
所以对于需要较多的查询和较少插入删除,长度容易计算的数据,应当采用顺序表的结构
链表
描述:
链表是一种物理存储单元上非连续、非顺序的存储结构,其物理结构不能只管的表示数据元素的逻辑顺序,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列的结点(链表中的每一个元素称为结点)组成,结点可以在运行时动态生成。
类别:
单向链表是链表的一种,它由多个结点组成,每个结点都由一个数据域和一个指针域组成,数据域用来存储数据,指针域用来指向其后继结点。链表的头结点的数据域不存储数据,指针域指向第一个真正存储数据的结点。
双向链表也叫双向表,是链表的一种,它由多个结点组成,每个结点都由一个数据域和两个指针域组成,数据域用来存储数据,其中一个指针域用来指向其后继结点,另一个指针域用来指向前驱结点。链表的头结点的数据域不存储数据,指向前驱结点的指针域值为null,指向后继结点的指针域指向第一个真正存储数据的结点。
核心思想(双链表):
方法 | 实现方式 |
---|---|
节点类Node | 创建一个内部类Node,用于存储数据item,pre节点(前驱节点),next节点(后继节点) |
构造方法 | 初始化头节点head = new Node(null,null,null),尾节点last=null,初始化表长N=0 |
清空表 | 把头节点的next,和pre指向空,尾节点指向空,删除数据,并且重置链表长度N=0 |
判断是否空表 | 可以直接获取表长N判断是否为空,空返回true,否则返回false |
获取表长 | return N; |
获取指定位置元素 | 创建一个节点对象n,通过循环,从头节点开始依次往后找,找(位置)次,即可找到对应位置的对应元素,找到返回n.item |
添加元素 | 1.如果链表为空,创建新的节点成为尾节点,让头节点指向尾节点,然后表长N++ 2.默认插入数据即直接在表尾插入元素:找到尾节点,然后创建新节点保存元素,让尾节点指向新节点,新节点成为尾节点,然后表长N++ 3.指定位置插入元素:通过循环找到对应位置的前一个节点a,然后找到对应位置节点b。创建新节点n,并且新节点n需要指向原来位置的节点b,原来位置前一个节点a指向新节点n,然后表长N++ (a->b ===> a->n->b) |
删除指定元素 | 通过循环找到对应位置节点,如果该节点的前驱是头节点,则直接把该节点指向空即可,如果该节点是表尾,则直接删除该节点,然后找到前一个节点成为新的尾节点,否则找到对应位置的前一个节点a,对应位置节点b,和对应位置下一个节点c,a的后继节点指向节点c,c的前驱节点指向a,删除节点b,然后表长N-- (a->b-c ===> a->c) |
查找元素第一次出现的位置 | 通过循环从头节点开始,依次找到下一个节点,取出数据item,和目标元素比较,如果相同则找到,返回元素位置,否则返回-1 |
修改指定位置的元素 | 通过循环,从头节点开始,找到对应位置的元素然后更新元素(n.item = newdata ) |
遍历表 | 主要实现顺序表支持迭代/foreach循还(增强型的for循环) 1.实现Iterator接口重写iterator方法 2.在顺序表类内部提供一个内部类用于实现Iterator接口,重写hasNext和next方法 |
构造方法以及支持迭代的具体实现: