线性表是最基本、最简单、也是最常用的一种数据结构。一个线性表是n个具有相同特性的数据元素的有限序列
前序元素:若A元素在B元素的前面,则称A为B的前驱元素
后继元素:若B元素在A元素的后面,则称B为A的后继元素
线性表的特征:
1、第一个数据元素没有前驱,这个数据元素被称为头结点。
2、最后一个数据元素没有后继,这个数据元素被称为尾结点。
3、除了第一个和最后一个数据元素外,其他数据元素有且仅有一个前驱和一个后继。
线性表的分类:
1、线性表中数据存储的方式可以是顺序存储,也可以是链式存储,按照数据的存储方式不同,可以把线性表分为顺序 表和链表。
-- 顺序表: 顺序表是在计算机内存中以数组的形式保存的线性表,线性表的顺序存储是指用一组地址连续的存储单元,依次存 储线性表中的各个元素、使得线性表中再逻辑结构上响铃的数据元素存储在相邻的物理存储单元中,即通过数据元 素物理存储的相邻关系来反映数据元素之间逻辑上的相邻关系。
-- API设计:
-- 代码:
/**
* 顺序表
* @param <T>
*/
public class SequenceList<T> {
// 存储元素的数组
private T[] eles;
// 记录当前元素的个数
private int N;
// 构造方法
public SequenceList(int size) {
eles = (T[]) new Object[size];
N = 0;
}
// 将一个线性表置为空
public void clear() {
N = 0;
}
// 判断当前线性表是否为空
public boolean isEmpty() {
return N == 0;
}
// 获取线性表长度
public int length() {
return N;
}
// 获取指定位置的元素
public T get(int i) {
// 判断获取的位置是否合法
if(i < 0 || i > N - 1) {
throw new RuntimeException("当前元素不存在!");
}
// 返回当前元素
return eles[i];
}
// 向线性表中添加元素t
public void insert(T t) {
// 判断容器是否已满
if(N == eles.length) {
throw new RuntimeException("当前表已满!");
}
// 把t元素放进线性表中
eles[N++] = t;
}
// 在i索引处插入元素t
public void insert(int i,T t) {
// 判断容器是否已满
if(N == eles.length) {
throw new RuntimeException("当前表已满!");
}
// 判断插入位置是否合法
if(i < 0 || i > N) {
throw new RuntimeException("插入位置不合法!");
}
// 把i位置空出来,i位置及其后面的元素一次向后移动一位
for(int index = N;index > i;index--) {
eles[index] = eles[index-1];
}
// 把t放到i位置处
eles[i] = t;
// 元素数量+1
N++;
}
// 删除指定位置i处的元素,并返回该元素
public T remove(int i) {
// 判断删除位置是否合法
if(i < 0 || i > N-1) {
throw new RuntimeException("当前要删除的元素不存在!");
}
// 记录i位置处的元素
T result = eles[i];
// 把i位置后面的元素向前移动一位
for(int index = i;i < N - 1;i++) {
eles[index] = eles[index+1];
}
// 当前元素数量-1
N--;
// 返回删除的元素
return result;
}
// 查找t元素第一次出现的位置
public int indexOf(T t) {
// 判断元素是否合法
if(t == null) {
throw new RuntimeException("查找的元素不合法!");
}
// 循环遍历进行查找
for(int i = 0;i < N;i++) {
if(eles[i].equals(t)) {
return i;
}
}
return -1;
}
}
-- 测试代码:
public class SequenceListTest {
public static void main(String[] args) {
//创建顺序表对象
SequenceList<String> sl = new SequenceList<>(10);
//测试插入
sl.insert("姚明");
sl.insert("科比");
sl.insert("麦迪");
sl.insert(1,"詹姆斯");
//测试获取
String getResult = sl.get(1);
System.out.println("获取索引1处的结果为:"+getResult);
//测试删除
String removeResult = sl.remove(0);
System.out.println("删除的元素是:"+removeResult);
//测试清空
sl.clear();
System.out.println("清空后的线性表中的元素个数为:"+sl.length());
}
}
-- 运行效果图:
-- 顺序表的遍历:
1、一般作为容器存储数据,都需要向外部提供遍历的方式,因此我们需要给顺序表提供遍历方式。
2、在java中,遍历集合的方式一般都是用的是foreach循环,如果想让我们的SequenceList也能支持foreach循环,
则 需要做如下操作:
2.1、SequenceList实现Iterable接口,重写iterator方法。
2.2、在SequenceList内部提供一个内部类SIterator,实现Iterator接口,重写hasNext方法和next方法。
-- 代码:
/**
* 顺序表
* @param <T>
*/
public class SequenceList<T> implements Iterable<T> {
// 存储元素的数组
private T[] eles;
// 记录当前元素的个数
private int N;
// 构造方法
public SequenceList(int size) {
eles = (T[]) new Object[size];
N = 0;
}
// 将一个线性表置为空
public void clear() {
N = 0;
}
// 判断当前线性表是否为空
public boolean isEmpty() {
return N == 0;
}
// 获取线性表长度
public int length() {
return N;
}
// 获取指定位置的元素
public T get(int i) {
// 判断获取的位置是否合法
if(i < 0 || i > N - 1) {
throw new RuntimeException("当前元素不存在!");
}
// 返回当前元素
return eles[i];
}
// 向线性表中添加元素t
public void insert(T t) {
// 判断容器是否已满
if(N == eles.length) {
throw new RuntimeException("当前表已满!");
}
// 把t元素放进线性表中
eles[N++] = t;
}
// 在i索引处插入元素t
public void insert(int i,T t) {
// 判断容器是否已满
if(N == eles.length) {
throw new RuntimeException("当前表已满!");
}
// 判断插入位置是否合法
if(i < 0 || i > N) {
throw new RuntimeException("插入位置不合法!");
}
// 把i位置空出来,i位置及其后面的元素一次向后移动一位
for(int index = N;index > i;index--) {
eles[index] = eles[index-1];
}
// 把t放到i位置处
eles[i] = t;
// 元素数量+1
N++;
}
// 删除指定位置i处的元素,并返回该元素
public T remove(int i) {
// 判断删除位置是否合法
if(i < 0 || i > N-1) {
throw new RuntimeException("当前要删除的元素不存在!");
}
// 记录i位置处的元素
T result = eles[i];
// 把i位置后面的元素向前移动一位
for(int index = i;i < N - 1;i++) {
eles[index] = eles[index+1];
}
// 当前元素数量-1
N--;
// 返回删除的元素
return result;
}
// 查找t元素第一次出现的位置
public int indexOf(T t) {
// 判断元素是否合法
if(t == null) {
throw new RuntimeException("查找的元素不合法!");
}
// 循环遍历进行查找
for(int i = 0;i < N;i++) {
if(eles[i].equals(t)) {
return i;
}
}
return -1;
}
//======================================================================================
// 实现for-each遍历的
@Override
public Iterator<T> iterator() {
return new SIterator();
}
class SIterator implements Iterator<T> {
private int cur;
// 构造方法
public SIterator() {
this.cur = 0;
}
// 是否还有下一个
@Override
public boolean hasNext() {
return cur < N;
}
// 获取当前的元素
@Override
public T next() {
return eles[cur++];
}
}
}
-- 测试代码:
public class SequenceListTest {
public static void main(String[] args) throws Exception {
SequenceList<String> squence = new SequenceList<>(5);
//测试遍历
squence.insert(0, "姚明");
squence.insert(1, "科比");
squence.insert(2, "麦迪");
squence.insert(3, "艾佛森");
squence.insert(4, "卡特");
for (String s : squence) {
System.out.println(s);
}
}
}
-- 运行效果图:
--顺序表的容量可变:
1、添加元素时,应该检查当前数组的大小是否能容纳新的元素,如果不能容纳,则需要创建新的容量更大的数组,我
们这里创建一个是原数组两倍容量的新数组存储元素。
2、移除元素时,应该检查当前数组的大小是否太大,比如正在用100个容量的数组存储10个元素,这样就会造成内存
空间的浪费,应该创建一个容量更小的数组存储元素。如果我们发现数据元素的数量不足数组容量的1/4,则创建一个
是原数组容量的1/2的新数组存储元素。
-- 代码:
/**
* 顺序表
* @param <T>
*/
public class SequenceList<T> implements Iterable<T> {
// 存储元素的数组
private T[] eles;
// 记录当前元素的个数
private int N;
// 构造方法
public SequenceList(int size) {
eles = (T[]) new Object[size];
N = 0;
}
// 将一个线性表置为空
public void clear() {
N = 0;
}
// 判断当前线性表是否为空
public boolean isEmpty() {
return N == 0;
}
// 获取线性表长度
public int length() {
return N;
}
// 获取指定位置的元素
public T get(int i) {
// 判断获取的位置是否合法
if(i < 0 || i > N - 1) {
throw new RuntimeException("当前元素不存在!");
}
// 返回当前元素
return eles[i];
}
// 向线性表中添加元素t
public void insert(T t) {
// 判断容器是否已满
if(N == eles.length) {
// 扩容两倍
resize(eles.length * 2);
}
// 把t元素放进线性表中
eles[N++] = t;
}
// 在i索引处插入元素t
public void insert(int i,T t) {
// 判断容器是否已满
if(N == eles.length) {
// 扩容两倍
resize(eles.length * 2);
}
// 判断插入位置是否合法
if(i < 0 || i > N) {
throw new RuntimeException("插入位置不合法!");
}
// 把i位置空出来,i位置及其后面的元素一次向后移动一位
for(int index = N;index > i;index--) {
eles[index] = eles[index-1];
}
// 把t放到i位置处
eles[i] = t;
// 元素数量+1
N++;
}
// 删除指定位置i处的元素,并返回该元素
public T remove(int i) {
// 判断删除位置是否合法
if(i < 0 || i > N-1) {
throw new RuntimeException("当前要删除的元素不存在!");
}
// 记录i位置处的元素
T result = eles[i];
// 把i位置后面的元素向前移动一位
for(int index = i;i < N - 1;i++) {
eles[index] = eles[index+1];
}
// 当前元素数量-1
N--;
// 如果当前容量不足数组的1/4,则重置数组的大小
if(N > 0 && N < eles.length / 4) {
resize(eles.length / 2);
}
// 返回删除的元素
return result;
}
// 查找t元素第一次出现的位置
public int indexOf(T t) {
// 判断元素是否合法
if(t == null) {
throw new RuntimeException("查找的元素不合法!");
}
// 循环遍历进行查找
for(int i = 0;i < N;i++) {
if(eles[i].equals(t)) {
return i;
}
}
return -1;
}
// 返回当前的容量
public int capacity() {
return eles.length;
}
//==============================================================================
// 实现for-each遍历的
@Override
public Iterator<T> iterator() {
return new SIterator();
}
class SIterator implements Iterator<T> {
private int cur;
// 构造方法
public SIterator() {
this.cur = 0;
}
// 是否还有下一个
@Override
public boolean hasNext() {
return cur < N;
}
// 获取当前的元素
@Override
public Object next() {
return eles[cur++];
}
}
//==============================================================================
// 实现可变容量的代码
public void resize(int newSize) {
// 记录旧的数组
T[] temp = eles;
// 创建新的数组
eles = (T[]) new Object[newSize];
// 把旧的数组中的元素拷贝到新的数组
for(int i = 0;i < N;i++) {
eles[i] = temp[i];
}
}
}
-- 测试代码:
public class SequenceListTest {
public static void main(String[] args) throws Exception {
SequenceList<String> squence = new SequenceList<>(5);
//测试遍历
squence.insert(0, "姚明");
squence.insert(1, "科比");
squence.insert(2, "麦迪");
squence.insert(3, "艾佛森");
squence.insert(4, "卡特");
System.out.println(squence.capacity());
squence.insert(5,"aa");
System.out.println(squence.capacity());
squence.insert(5,"aa");
squence.insert(5,"aa");
squence.insert(5,"aa");
squence.insert(5,"aa");
squence.insert(5,"aa");
System.out.println(squence.capacity());
squence.remove(1);
squence.remove(1);
squence.remove(1);
squence.remove(1);
squence.remove(1);
squence.remove(1);
squence.remove(1);
System.out.println(squence.capacity());
}
}
-- 运行效果图:
@ 以上内容属于个人笔记