在介绍顺序表和Arraylist之前,我们就要介绍一下线性表的系一些内容.线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结
构,常见的线性表:顺序表、链表、栈、队列…
线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物
理上存储时,通常以数组和链式结构的形式存储。以下面俩种为主
如果我们还是没理解的话,我们可以继续举几个例子,来让我们感受一下,什么是线性表.如下图所示
我们了解了,线性表以后,接下来让我们看看,线性表的抽象数据类型,它具体能干些什么事情呢?
这样我们现来举个例幼儿园小朋友的例子,老师为了让小朋友有秩序地出入,所以就考虑给他们排一个队,并且是长期使用的顺序,这个考虑和安排的过程其实就是一个线性表的创建和初始化过程。
一开始没经验,把小朋友排好队后,发现有的高有的矮,队伍很难看,于是就让小朋友解散重新排—这是一个线性表重置为空表的操作。
排好了队,我们随时可以叫出队伍某一位置的小朋友名字及他的具体情况。比如有家长问,队伍里第五个孩子,怎么这么调皮,他叫什么名字呀,老师可以很快告诉这位家长,这就是封清扬的儿子,叫封云卞。我在旁就非常扭捏,看来是我给儿子的名字没取好,儿子让班级“风云突变”了。这种可以根据位序得到数据元素也是一种很重要的线性表操作。
还有什么呢,有时我们想知道,某个小朋友,比如麦兜是否是班里的小朋友,老师会告诉我说,不是,麦兜在春田花花幼儿园里,不在我们幼儿园。这种查找某个元素是否存在的操作很常用。
而后有家长问老师,班里现在到底有多少个小朋友呀,这种获得线性表长度的问题也很普遍。
显然,对于一个幼儿园来说,加入一个新的小朋友到队列中,或因某个小朋友生病,需要移除某个位置,都是很正常的情况。对于一个线性表来说,插入数据和删除数据都是必须的操作。
综合上面的例子,我们了解到了,线性表的操作如下
//初始化顺序表
public void InitList();
//打印顺序表
public void display();
//获取顺序表长度
public void size();
//判断是否包含某个元素
public boolean contains();
//查找某个元素对应的位置
public int indexOf(int toFind);
// 新增元素
public void add(int data);
//判断是否为满
public boolean isFull();
// 在 pos 位置新增元素 O(N)
public void add(int pos, int data);
// 获取 pos 位置的元素
public int get(int pos);
//删除数据
public boolean remove(int toRemove);
//清空顺训表
public void clear();
以上就是线性表的一些操作,接下来我会用俩种方式来实现以上的操作,一种是顺序结构,一种是链式结构.
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。
说白了,就跟排队一样,就是在内存中找了块地儿,通过占位的形式,把一定内存空间结占了, 然后把相同数据类型的数据 素依次存放在这块空地中。
这样我列举一张图,大家可以看一下
这就是我们具体的线性表的顺序结构.
接下来我们就来介绍顺序表的相关操作吧.我会用俩种数据结构,这种使用数组实现的.
顾名思义就是我们有一个容器,我要给这个
//初始化数据表
public class MyArrayList {
public int[] elem;
public int usedSize;//存储了多少个有效的数据
public static final int DEFAULT_SIZE = 5;
public MyArrayList() {
this.elem = new int[DEFAULT_SIZE];
}
}
就是简单的获取数组的长度
// 获取顺序表长度
public int size() {
return this.usedSize;
}
这里采取的方式还是,根据数组的长度去遍历数组,判断是否相等
public boolean contains(int toFind) {
for (int i = 0; i < this.usedSize; i++) {
if(this.elem[i] == toFind) {
return true;
}
}
return false;
}
public int indexOf(int toFind) {
for (int i = 0; i < this.usedSize; i++) {
if(this.elem[i] == toFind) {
return i;
}
}
return -1;//因为数组 没负数下标
}
新增元素不好理解,我这里举一个例子,举个例子,我们在春运时去买火车票,大家都排队排的好好的。这时来了一个美女,对着队伍中排在第三位的你说,“大哥,求求你帮帮忙,我家母亲有病,我得急着回去看她,这队伍这么长,你可否让我排在你的前面?”你心一软,就同意了。这时,你必须得退后一步,否则她是没法进到队伍来的。这可不得了,后面的人像恪虫一样,全部都得退一步。骂起四声。但后面的人也不清楚这加塞是怎么回事,没什么办法。
具体的一个方法,如下图所示:
具体的代码实现:
我们要往一个顺序表里增加东西,我们就必须,考虑扩容问题,万一装不下怎么办?于是下面我们提供了,扩容方法.
public void add(int data) {
if(this.isFull()) {
resize();
}
this.elem[this.usedSize] = data;
this.usedSize++;
}
/**
* 扩容
*/
private void resize() {
this.elem = Arrays.copyOf(this.elem,
2*this.elem.length);
}
/**
* 判断是否为满
* @return
*/
public boolean isFull() {
/*if(this.usedSize == this.elem.length) {
return true;
}
return false;*/
return this.usedSize == this.elem.length;
}
我们必须考虑的问题如下:
1.扩容问题
2.下标合法问题
代码如下:
// 在 pos 位置新增元素 O(N)
public void add(int pos, int data) {
checkIndex(pos);
if(isFull()) {
resize();
}
for (int i = usedSize-1; i >= pos ; i--) {
elem[i+1] = elem[i];
}
elem[pos] = data;
usedSize++;
}
/**
* 检查add数据的时候,pos是否是合法的
* @param pos
*/
private void checkIndex(int pos) {
if(pos < 0 || pos > usedSize) {
throw new IndexOutOfException
("位置不合法,请检查位置的合法性!");
}
}
public class IndexOutOfException extends RuntimeException{
public IndexOutOfException() {
}
public IndexOutOfException(String msg) {
super(msg);
}
}
这个就是很简单的,就是指定位置获取即可
public int get(int pos) {
checkGetIndex(pos);
return elem[pos];
}
private void checkGetIndex(int pos) {
if(pos < 0 || pos >= usedSize) {
throw new IndexOutOfException
("get获取元素的时候,位置不合法,请检查位置的合法性!");
}
}
接着刚才排队的例子,我们继续讲.此时后面排队的人群意见都很大,都说怎么可以这样,不管什么原因,插队就是不行,有本事,找火车站开后门去。就在这时,远处跑来一胖子,对着这美女喊,可找到你了,你这骗子,还我钱。只见这女子二话不说,突然就冲出了队伍,胖子追在其后,消失在人群中。哦,原来她是倒卖火车票的黄牛,刚才还装可怜。于是排队的人群,又像蠕虫一样,均向前移动了一步,骂声渐息,队伍又恢复了平静。
代码如下:
删除流程也很简单:
1.找到指定下标
2.根据指定下标删除
3.完成
public int indexOf(int toFind) {
for (int i = 0; i < this.usedSize; i++) {
if(this.elem[i] == toFind) {
return i;
}
}
return -1;//因为数组 没负数下标
}
public boolean remove(int toRemove) {
int index = indexOf(toRemove);
if(index == -1) {
System.out.println("没有这个数据");
return false;
}
for (int i = index;i < usedSize-1;i++) {
elem[i] = elem[i+1];
}
usedSize --;
//elem[usedSize] = null;
// 如果里面是引用类型 那么此时就需要手动置空
elem[usedSize] = 0;
return true;
}
自然就是把数组置为0
public void clear() {
//1.这种也可以
/*
for (int i = 0; i < usedSize; i++) {
elem[i] = null;
}
usedSize = 0;
*/
//2.这种也可以,因为我们使用构造方法初始化的,useSize为0,自然数组就为0了.
usedSize = 0;
}
public class MyArrayList {
public int[] elem;
public int usedSize;//存储了多少个有效的数据
public static final int DEFAULT_SIZE = 5;
public MyArrayList() {
this.elem = new int[DEFAULT_SIZE];
}
// 打印顺序表,注意:该方法并不是顺序表中的方法,为了方便看测试结果给出的
public void display() {
for (int i = 0; i < this.usedSize; i++) {
System.out.print(this.elem[i] +" ");
}
System.out.println();
}
// 获取顺序表长度
public int size() {
return this.usedSize;
}
// 判定是否包含某个元素
public boolean contains(int toFind) {
for (int i = 0; i < this.usedSize; i++) {
if(this.elem[i] == toFind) {
return true;
}
}
return false;
}
// 查找某个元素对应的位置
public int indexOf(int toFind) {
for (int i = 0; i < this.usedSize; i++) {
if(this.elem[i] == toFind) {
return i;
}
}
return -1;//因为数组 没负数下标
}
// 新增元素,默认在数组最后新增
public void add(int data) {
if(this.isFull()) {
resize();
}
this.elem[this.usedSize] = data;
this.usedSize++;
}
/**
* 扩容
*/
private void resize() {
this.elem = Arrays.copyOf(this.elem,
2*this.elem.length);
}
/**
* 判断是否为满
* @return
*/
public boolean isFull() {
/*if(this.usedSize == this.elem.length) {
return true;
}
return false;*/
return this.usedSize == this.elem.length;
}
// 在 pos 位置新增元素 O(N)
public void add(int pos, int data) {
checkIndex(pos);
if(isFull()) {
resize();
}
for (int i = usedSize-1; i >= pos ; i--) {
elem[i+1] = elem[i];
}
elem[pos] = data;
usedSize++;
}
/**
* 检查add数据的时候,pos是否是合法的
* @param pos
*/
private void checkIndex(int pos) {
if(pos < 0 || pos > usedSize) {
throw new IndexOutOfException
("位置不合法,请检查位置的合法性!");
}
}
// 获取 pos 位置的元素
public int get(int pos) {
checkGetIndex(pos);
return elem[pos];
}
private void checkGetIndex(int pos) {
if(pos < 0 || pos >= usedSize) {
throw new IndexOutOfException
("get获取元素的时候,位置不合法,请检查位置的合法性!");
}
}
// 给 pos 位置的元素设为 value 1
public void set(int pos, int value) {
checkIndex(pos);
elem[pos] = value;
}
//删除第一次出现的关键字key O(n)
public boolean remove(int toRemove) {
int index = indexOf(toRemove);
if(index == -1) {
System.out.println("没有这个数据");
return false;
}
for (int i = index;i < usedSize-1;i++) {
elem[i] = elem[i+1];
}
usedSize --;
//elem[usedSize] = null;
// 如果里面是引用类型 那么此时就需要手动置空
elem[usedSize] = 0;
return true;
}
// 清空顺序表
public void clear() {
/*
for (int i = 0; i < usedSize; i++) {
elem[i] = null;
}
usedSize = 0;
*/
usedSize = 0;
}
}
优点:
1.无须为表示表中元素之间的逻辑关系而增加额外的存储空间
2.可以快速地存取表中任一位置的元素
缺点:
1.插入和删除操作需要移动大量元素
2.·插入和删除操作需要移动大量元素,当线性表长度变化较大时,难以确定存储空间的容量造成存储空间的“碎片”.