在数据结构体系中我们将整个数据结构分为两类,一类是线性结构;
线性表是一种在实际中广泛使 用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串...
另一类是非线性结构:
非线性结构,数学用语,其逻辑特征是一个结点元素可能有多个直接前驱和多个直接后继。常见的非线性结构:树、图......
直接前驱就是该数据的前一个数据,直接后继就是该数据的后一个数据。
一、 线性表的基本介绍
【百度百科】:
线性表是最基本、最简单、也是最常用的一种数据结构。线性表(linear list)是数据结构的一种,一个线性表是n个具有相同特性的数据元素的有限序列。
线性表中数据元素之间的关系是一对一的关系,即除了第一个和最后一个数据元素之外,其它数据元素都是首尾相接的(注意,这句话只适用大部分线性表,而不是全部。比如,循环链表逻辑层次上也是一种线性表(存储层次上属于链式存储,但是把最后一个数据元素的尾指针指向了首位结点)。
就好像小朋友在排队:
二、顺序表
1、顺序表的概念
【百度百科】顺序表是在计算机内存中以数组的形式保存的线性表,线性表的顺序存储是指用一组地址连续的存储单元依次存储线性表中的各个元素、使得线性表中在逻辑结构上相邻的数据元素存储在相邻的物理存储单元中,即通过数据元素物理存储的相邻关系来反映数据元素之间逻辑上的相邻关系,采用顺序存储结构的线性表通常称为顺序表。顺序表是将表中的结点依次存放在计算机内存中一组地址连续的存储单元中。
2. 创建顺序表类(ArrayList)
我们这个顺序表用到的是数组,所以我们就可以在这个顺序表中定义一个数组的成员变量,由于顺序表中的元素没有指定是什么类型,那么我们就创建一个泛型类的顺序表,以及一个泛型数组!并且我们还需要一个值来记录数组内元素的个数,那么为了方便,我们还可以利用构造方法初始化我们的数组大小。
代码如下:
public class MyArraylist<E> { public E[] elem; public int usedSize;//0 //默认容量 private static final int DEFAULT_SIZE = 10; public MyArraylist() { this.elem =(E[]) new Object[DEFAULT_SIZE]; } }
我们这里是设置为默认的容量,也可以根据自己想要的大小去重载构造器即可;例如:
public MyArraylist(int usedSize) { this.usedSize = usedSize; }
我们的数据结构的各项功能用四个字来概括,无非就是增删改查,以及实现一些特别的方法。
按照我们所说的,我们先来实现增加元素;
2. 增加元素
对于增加元素我们可以选择默认就是在表尾部进行增加,或者在我们指定的位置进行增加,而我们所实现的代码的大致思路如下:
1. 先判断所给的索引是否合理。
2. 先判断是否顺序表满了? 如果满了我们是否要对其扩容,我们实现的代码是要对其进行扩容的。
3. 将整个数组从指定位置一个一个向后移动以一个位置。
add方法:
public void add(int pos, E data) { checkIndex(pos); if (isFull()) { resize(); } for (int i = this.usedSize - 1; i >= pos; i--) { elem[i + 1] = elem[i]; } elem[pos] = data; usedSize++; }
checkIndex方法:
private void checkIndex(int pos) {//可以是private,也可以是public,这里主要体现了封装性 if(pos < 0 || pos > usedSize) { throw new IndexOutOfException ("pos位置不合法,请检查pos位置的合法性!"); } }
isFull方法:
public boolean isFull() {//访问权限与checkIndex方法同理 return this.usedSize == this.elem.length; }
resize扩容方法:
为什么我们要按照1.5倍进行扩容,我们可以参考Java提供的ArrayList类:
1.找到提供的add方法,我们发现它有一个
ensureCapacityInternal方法,ctrl+B追溯源码
2. 里面又调用了
ensureExplicitCapacity方法,继续追
3. 直到我们找到grow方法,继续追
4. 直到找到如下这句话:
原来的容量加上原来的容量向右移一位。
private void resize() { this.elem = Arrays.copyOf(this.elem, (int) 1.5 * this.elem.length); }
其他的方法我们也都是参考了ArrayList的底层逻辑,比如:isEmpty,比如contains
我们所有的方法都可以去参考,借鉴底层的逻辑思维,去帮助我们跟好的理解数据结构,我们这一门课程在进行写代码时,借鉴了大量的底层逻辑思想。
默认添加add的重载:
public void add(E data) { if (isFull()) { resize(); } elem[this.usedSize] = data; this.usedSize++; }
3. 删除元素
思路如下:
1. 先找到想要删除的数,调用了indexOf方法,如果找不到,则返回-1 ;找到则放回它的下表
public int indexOf(E toFind) { for (int i = 0; i < this.usedSize; i++) { if (this.elem[i] == toFind) { return i; } } return -1; }
2. 怎么将元素删除,我们直接将后一个元素覆盖在该元素上即可。
public void remove(E key) { int index = indexOf(key); if (index == -1 ) { throw new IndexOutOfException("key 不存在该顺序表中," + "请检查您想要删除的key"); } for (int i = index;i < usedSize-1;i++) { elem[i] = elem[i+1]; } usedSize --; elem[usedSize] = null; }
3. 我们提供了
IndexOutOfException类,用于抛出异常
这段逻辑可以任意改
public class IndexOutOfException extends RuntimeException{ public IndexOutOfException() { super(); } public IndexOutOfException(String s) { super(s); } }
4. 修改某个元素
思路如下:
我们只需要确保改下表是否合理:
private void checkGetIndex(int pos) { if(pos < 0 || pos >= usedSize) { throw new IndexOutOfException ("get获取元素的时候,位置不合法,请检查位置的合法性!"); } }
set方法:
1. public void set(int pos, E value) { 2. checkGetIndex(pos); 3. this.elem[pos] = value; 4. }
5. 查找元素
我们提供了查找某个元素和查找整个顺序表
get方法:
// 获取 pos 位置的元素 public E get(int pos) { return this.elem[pos]; }
display方法:
2. 遍历整个数据并打印
public void display() { if (isEmpty()) { return; } for (int i = 0; i < usedSize; i++) { System.out.print(elem[i] + " "); } System.out.println(); }
此外我们还提供了两个方法,一个用于获取整个表的表长size()方法,一个用于清空表的数据clear()方法。
// 获取顺序表长度 public int size() { return this.usedSize; } // 清空顺序表 public void clear() { // for (int i = 0; i < usedSize; i++) { // elem[i] = Integer.parseInt(null); // } // usedSize = 0; // 也可以这样 usedSize = 0; }
这样整个MyArrayList类就结束了(当然,还可以继续添加任意合理的方法),我们来测试一下:
Main类
代码如下:
public class Main { public static void main(String[] args) { //我们的object可以是任意类型,这里只拿Integer来进行测试 MyArraylist<Integer> myList = new MyArraylist<>(); myList.add(1); myList.add(2); myList.add(3); myList.display(); myList.add(3,2); myList.display(); myList.remove(2); myList.display(); System.out.println(myList.contains(6)); myList.set(0,7); myList.display(); myList.clear(); myList.display(); } }
完整代码如下:
MyArratList类
import java.util.Arrays; @SuppressWarnings("all") // 用于抑制所有警告,注意这里没有分号 public class MyArraylist<E> { public E[] elem; public int usedSize;//0 //默认容量 private static final int DEFAULT_SIZE = 10; public MyArraylist() { this.elem =(E[]) new Object[DEFAULT_SIZE]; } public MyArraylist(int usedSize) { this.usedSize = usedSize; } /** * 打印顺序表: * 根据usedSize判断即可 */ public void display() { if (isEmpty()) { return; } for (int i = 0; i < usedSize; i++) { System.out.print(elem[i] + " "); } System.out.println(); } // 新增元素,默认在数组最后新增 public void add(E data) { if (isFull()) { resize(); } elem[this.usedSize] = data; this.usedSize++; } /** * 判断当前的顺序表是不是满的! * * @return true:满 false代表空 */ public boolean isFull() { return this.usedSize == this.elem.length; } private boolean checkPosInAdd(int pos) { if (pos < 0 || pos > this.usedSize) { return false;//不合法 } return true;//合法 } private void checkIndex(int pos) { if(pos < 0 || pos > usedSize) { throw new IndexOutOfException ("pos位置不合法,请检查pos位置的合法性!"); } } // 在 pos 位置新增元素 public void add(int pos, E data) { checkIndex(pos); if (isFull()) { resize(); } for (int i = this.usedSize - 1; i >= pos; i--) { elem[i + 1] = elem[i]; } elem[pos] = data; usedSize++; } // 判定是否包含某个元素 public boolean contains(E toFind) { for (int i = 0; i < this.usedSize; i++) { if (this.elem[i] == toFind) { return true; } } return false; } // 查找某个元素对应的位置 public int indexOf(E toFind) { for (int i = 0; i < this.usedSize; i++) { if (this.elem[i] == toFind) { return i; } } return -1; } // 获取 pos 位置的元素 public E get(int pos) { return this.elem[pos]; } private boolean isEmpty() { return this.usedSize == 0; } private void checkGetIndex(int pos) { if(pos < 0 || pos >= usedSize) { throw new IndexOutOfException ("get获取元素的时候,位置不合法,请检查位置的合法性!"); } } // 给 pos 位置的元素设为【更新为】 value public void set(int pos, E value) { checkGetIndex(pos); this.elem[pos] = value; } /** * 删除第一次出现的关键字key * * @param key */ public void remove(E key) { int index = indexOf(key); if (index == -1 ) { throw new IndexOutOfException("key 不存在该顺序表中," + "请检查您想要删除的key"); } for (int i = index;i < usedSize-1;i++) { elem[i] = elem[i+1]; } usedSize --; elem[usedSize] = null; } //扩容 private void resize() { this.elem = Arrays.copyOf(this.elem, (int) 1.5 * this.elem.length); } // 获取顺序表长度 public int size() { return this.usedSize; } // 清空顺序表 public void clear() { // for (int i = 0; i < usedSize; i++) { // elem[i] = Integer.parseInt(null); // } // usedSize = 0; // 也可以这样 usedSize = 0; } }
Main类:
public class Main { public static void main(String[] args) { //我们的object可以是任意类型,这里只拿Integer来进行测试 MyArraylist<Integer> myList = new MyArraylist<>(); myList.add(1); myList.add(2); myList.add(3); myList.display(); myList.add(3,2); myList.display(); myList.remove(2); myList.display(); System.out.println(myList.contains(6)); myList.set(0,7); myList.display(); myList.clear(); myList.display(); } }
indexoutofexception类
public class IndexOutOfException extends RuntimeException{ public IndexOutOfException() { super(); } public IndexOutOfException(String s) { super(s); } }