- 首先,感觉这几天做题的思路和基本概念有点懵逼,所以删掉之前的,自己从新整理认识一下它们的基本概念和用法。以待后面懵逼时来鞭策自己。
线性表的概念
- 学习一个东西之前,你得先了解这个东西是什么?线性表,从字面意思上理解,它肯定是一种线性的,或者是“一对一”的关系。
- 他的第一个数据元素没有前驱,这个数据元素被称为开始节点也乐意成为头节点;最后一个数据元素没有后续,这个数据元素称为终端节点或者尾结点;除了第一个和最后一个数据元素之外,其他数据元素有且仅有一个前驱和一个后续。
- 它是零个或多个数据元素的有限序列。零个的时候,只不过是一个空表
实现
- 概念了解完之后,下面开始用代码了解其实现方法。
- 首先其重要的一个实现接口是List,list接口主要有以下几种定义:
定义接口方法
package com.snow.线性表;
/**
* List是线性表的最终父接口
* */
public interface List<E> {
/**
* 获取线性表中元素的个数(线性表的长度)
* @return 线性表中有效元素的个数
* */
public int getSize();
/**
* 判断线性表是否为空
* @return 是否为空的布尔类型值
* */
public boolean isEmpty();
/**
* 在线性表中指定的index角标处添加元素e
* @param index 指定的角标 0<=index<=size
* @param e 要插入的元素
* */
public void add(int index,E e);
/**
* 在线性表的表头位置插入一个元素
* @param e 要插入的元素 指定在角标0处
* */
public void addFirst(E e);
/**
* 在线性表的表尾位置插入一个元素
* @param e 要插入的元素 指定在角标size处
* */
public void addLast(E e);
/**
* 在线性表中获取指定index角标处的元素
* @param index 指定的角标 0<=index<size
* @return 该角标所对应的元素
* */
public E get(int index);
/**
* 获取线性表中表头的元素
* @return 表头元素 index=0
* */
public E getFirst();
/**
* 获取线性表中表尾的元素
* @return 表尾的元素 index=size-1
* */
public E getLast();
/**
* 修改线性表中指定index处的元素为新元素e
* @param index 指定的角标
* @param e 新元素
* */
public void set(int index,E e);
/**
* 判断线性表中是否包含指定元素e 默认从前往后找
* @param e 要判断是否存在的元素
* @return 元素的存在性布尔类型值
* */
public boolean contains(E e);
/**
* 在线性表中获取指定元素e的角标 默认从前往后找
* @param e 要查询的数据
* @return 数据在线性表中的角标
* */
public int find(E e);
/**
* 在线性表中删除指定角标处的元素 并返回
* @param index 指定的角标 0<=index<size
* @return 删除掉的老元素
* */
public E remove(int index);
/**
* 删除线性表中的表头元素
* @return 表头元素
* */
public E removeFirst();
/**
* 删除线性表中的表尾元素
* @return 表尾元素
* */
public E removeLast();
/**
* 在线性表中删除指定元素e
* */
public void removeElement(E e);
/**
* 清空线性表
* */
public void clear();
}
线性表接口定义完了之后,接下来就是它的顺序存储结构。
- 概念:它指的是用一段地址连续的存储单元,依次存储线性表的数据元素。是不是跟数组很像?不只是像,其实就是用数组实现的。
- 实现:查阅API发现,List接口的子类ArrayList它本身用数组形式实现了List的方法,并且有自己特有的方法。所以直接用ArrayList实现List接口方法且实现自己的方法就可以了。
- ArrayList自己特有的方法:
代码实现:
package com.snow.线性表;
/**
* 用顺序存储结构实现的List-顺序线性表-顺序表
* */
public class ArrayList<E> implements List<E> {
private static int DEFAULT_SIZE=10; //容器的默认容量
private E[] data; //存储数据元素的容器
private int size; //线性表的有效元素的个数
//data.length表示线性表的最大容量Capacity
/**
*容量默认为10
* */
public ArrayList(){
this(DEFAULT_SIZE);
}
/**
* 容量为指定capacity
* */
public ArrayList(int capacity){
this.data=(E[]) new Object[capacity];
this.size=0;
}
/**
* 将数组封装成线性表
* */
public ArrayList(E[] arr){
data=(E[]) new Object[arr.length]; //在内部创建一个新数组
for(int i=0;i<arr.length;i++) { //将外部传入的数组对内部的数组遍历赋值
data[i]=arr[i];//赋值完成后如果外部修改传入的数组中的值,对内部的线性表的值是没有影响的
}
size=arr.length;
}
@Override
public int getSize() { //获取有效元素的长度
return size;
}
@Override
public boolean isEmpty() {//判空
return size==0;
}
@Override
public void add(int index, E e) { //在指定下标添加元素e
if(index<0 || index>size) { //如果e的角标不在数组内
throw new ArrayIndexOutOfBoundsException("角标越界");
}
if(size==data.length) { //如果有效元素的个数达到线性表的存储的最大容量进行扩容
resize(2*data.length); //调用扩容函数
}
for(int i=size-1;i>=index;i--) {//插入时得先将元素后移 再插
data[i+1]=data[i];
}
data[index]=e; //插入指定下标位置
size++;
}
//扩容
public void resize(int newLen) {
E[] newData=(E[]) new Object[newLen];//改变data的长度
for(int i=0;i<size;i++){
newData[i]=data[i];
}
data=newData;//新数组的长度
}
@Override
public void addFirst(E e) {//表头添加元素
add(0,e);
}
@Override
public void addLast(E e) { //尾部添加
add(size,e);
}
@Override
public E get(int index) { //获取指定角标的元素
if(index<0||index>size-1){//判断角标是否越过有效元素的个数,或是数组最小下标位置
throw new ArrayIndexOutOfBoundsException("get函数角标越界");
}
return data[index];
}
@Override
public E getFirst() { //获取头元素
return get(0); //获取指定头位置的下标的元素
}
@Override
public E getLast() { //获取尾元素
return get(size-1);//这里的尾元素不是数组最后一个元素,而是有效元素的最后一个位置的元素,size-1.
}
@Override
public void set(int index, E e) { //更改指定位置元素
if(index<0 || index>size-1) {
throw new ArrayIndexOutOfBoundsException("get函数角标越界");
}
data[index]=e;//直接替换
}
@Override
public boolean contains(E e) { //是否包含e元素
if(isEmpty()) { //判空,数组为空,不包含
return false;
}
for(int i=0;i<size;i++) {//不为空得话遍历查找 如果存在,则直接返回true,不再向下查找
if(data[i]==e) {
return true;
}
}
return false;
}
@Override
public int find(E e) {//查找e元素
if(isEmpty()){
return -1;
}
for(int i=0;i<size;i++){
if(data[i]==e){
return i;
}
}
return -1;
}
@Override
public E remove(int index) { //删除某一位置的元素
if(index<0 || index>size-1) {
throw new ArrayIndexOutOfBoundsException("删除的角标越界");
}
E e=get(index); //将要删除的元素取出来
for(int i=index;i<size-1;i++) {//将该元素位置后面的元素向前移动
data[i]=data[i+1];
}
size--; //注意移动完了,有效元素也减一
//注意,这里内部数组中有效元素并没有减少,只是你有效元素位置指针指向了前一个位置,当前指向的元素和后一个元素是相同的,因为在移动元素的时候,是覆盖的移动,当下次再添加元素的时候,就将原来的覆盖了。
if(data.length>DEFAULT_SIZE&&size<=data.length/4) {
resize(data.length/2);
} //缩容就是扩1/2
return e;//返回删除的元素
}
@Override
public E removeFirst() { //删除头元素
return remove(0);
}
@Override
public E removeLast() { //删除尾元素
return remove(size-1);
}
@Override
public void removeElement(E e) {
int index=find(e);//找到该元素对应的下标
if(index==-1){
throw new IllegalArgumentException("删除元素不存在");
}
remove(index);//根据下标删除
}
@Override
public void clear() {
size=0; //有效元素置空
}
@Override
public String toString() { //打印线性表的方法,优化输出效果
StringBuilder sb = new StringBuilder();
sb.append("ArrayList:szie="+size+",capacity="+data.length+"\n");
if(isEmpty()) {
sb.append("[]");
}else {
sb.append('[');
for(int i=0;i<size;i++) {
sb.append(data[i]);
if(i==size-1) {
sb.append(']');
}else {
sb.append(',');
}
}
}
return sb.toString();
}
public int getCapacity(){ //获得该线性表当前的容量大小,也就是数组的大小
return data.length;
}
public void swap(int i,int j){ //交换两个元素得角标
E temp=data[i];
data[i]=data[j];
data[j]=temp;
}
@Override
public boolean equals(Object obj) { //重写equals方法,如果两个线性表有效元素个数相同,元素也一样视为相等
if(obj==null) {
return false;
}
if(obj==this) {
return true;
}
if(obj instanceof ArrayList) { //判断传进来的对象是不是线性表
ArrayList list = (ArrayList) obj; //将传进来的对象进行强转
if(this.getSize()==list.getSize()) { //比较两个线性表有效元素个数相同是否相同
for(int i=0;i<size;i++) { //逐个比较每个元素是否相同
if(data[i]!=list.data[i]) { //不一样 返回false
return false;
}
}
return true;
}
return false;
}
return false;
}
}
- 代码中也有注释,而且是边敲代码边写的注释。花了一个小时的整理,希望自己不要再忘记啦!!!!纯自己手打,测试已经通过。