1.什么是线性表?
基于数组的顺序表就叫线性表,线性表属于动态数组,可以根据数据的大小调整长度grow,元素不仅逻辑连续,物理上也连续。(基于链表的线性表,只是数字之间逻辑连续)
2.为什么我们不直接用数组吗
因为数组的长度是固定的,假设定义了int[] data= new int[3],声明后就只能存放固定长度的数值。而我们用MyAaay的话,就可以扩展数组容量了。
3.以下定义
因为存储元素在数组中,因此需要声明数组,来存放具体的数值4
import java.util.Arrays;
public class MyArray {
private int[]data;
//存放当前实际用了几个空间
private int size;
public MyArray() {
data = new int[10];//默认开辟10个大小
}
4.一些插入方法add,包括头插、尾插、按下标插入
我们先写按下标插入,因为头插和尾插也属于下标插入的一种。头插是index=0,
尾插是Index =size。
按照下标插入时,插入到index位置,首先判断是否越界然后才能插入。如何插入呢?将index以后的每一个元素向后移一个位置,空出index然后插入值value。这个过程中,要先判断数组容量是否足够,若越界需要扩容,扩容后可以插入。
<1>index插入
public void addIndex(int index,int value) {
//判断数组是否满了
if (size == data.length) {
grow();
}
//index <0 ||index>size判断Index合法性
//判断index和size的大小关系不能使用data.length,length表示当前数组的最多保存个数,有可能数组的后半部分为空,
//要使用size属性保证连续性
if (index < 0 || index > size) {
System.out.println("add index illeagal");
return;
}
else {
//将index位置空出来,方便元素插入
for (int i = size-1; i >=index; i--) {
data[i+1] = data[i];
}
data[index] = value;
size++;
}
}
<2>我将扩容方法写在这里,因为插入要用
private void grow() {
//扩容方法设置私有,外界不需要知道
//copyOf方法传入数组名称,以及新数组的长度
int[] newData = Arrays.copyOf(data,data.length << 1);
this.data = newData;
//扩容后,原来的空间变为垃圾空间被释放
}
<3>头插法addFirst
public void addFirst(int value){
//判断是否越界,如果超过定义的大小,需要扩容
if(size == data.length){
grow();
}
// //在数组的头部插入
// //当数组不为空时,第一个位置有元素,需要将第一个位置空出来,元素必须从最后一个位置开始向后
//移动一个单位,否则就会覆盖原先位置元素
// for (int i = size -1; i >= 0; i--) {
// //i=0因为必须把第一个位置空出来
// data[i+1] = data[i];
//}
//此时data[0]空出来了
// data [0]= value;
//size++;
//以上是自己写的方法,利用addIndex可以实现优化
addIndex(0,value);
}
<4>尾插法addLast
public MyArray(int capacity){
data = new int[capacity];
}
public void addLast(int value){
//先判断当前数组是否越界。
// 假设我们定义的数组长度是3,此时size已经是3了,若要再尾插一个,就会越界
if(size == data.length){
//当前数组已满
//数组要扩容
grow();
}
// //在数组的头部插入
// //当数组不为空时,第一个位置有元素,需要将第一个位置空出来,元素必须从最后一个位置开始
// 向后移动一个单位,否则就会覆盖原先位置元素
// for (int i = size -1; i >= 0; i--) {
// //i=0因为必须把第一个位置空出来
// data[i+1] = data[i];
// }
// //此时data[0]空出来了
// data [0]= value;
// size++;
//上面是自己写的方法,通过addIndex达到优化的目的,仍然需要判断是否越界
addIndex(0,value);
}
5.一些查询方法get
查询可以分为两种,按照值查询,另一个是按照下标查询
<1>值查询,返回这个值得下标
public int getByValue(int value){
//遍历数组
for (int i = 0; i <size; i++) {
if(data[i]== value){
return i;
}
}
//此时循环走完,还没找到元素,说明value不存在
return -1;
}
<2>索引查询,返回这个下标对应的值
public int get(int index){
//牵扯到用户输入,需要判断合法性
if(index <0 || index >size){
System.err.println("get index illagal!");
return -1;
}
//否则返回查询到的值
return data[index];
}
<3>contain判断是否存在
由于上面已经写了下值查询,在这里我们可以直接应用,若这个值存在就返回true
public boolean contains(int value){
int index = getByValue(value);
if(index == -1){
return false;
}
return true;
}
6.一些修改方法set
根据索引修改原先元素,将指定位置元素修改为newValue,返回修改前的元素值(原先这个下标下的值)
public int set (int index,int newValue){
//判断合法性
if(index <0 || index>= size){
System.err.println("set index illegal!");
}
//否则返回这个下标下原先的元素
int oldValue = data[index];
data[index] = newValue;
return oldValue;
}
7.一些删除方法remove
删除方法分为两种,一种是下标删除,另外一种是值删除。值删除的情况比较特殊,一个数组中,可能出现同一个值连续出现,或者多次出现同一个值,需要分两个方法写。
<1>按照下标删除
找到对应的下标index,将index后的元素全部向前挪一个位置,直接覆盖Index,从index+1开始。
循环移动以后,size位置的元素值还在,徐亚将它删除,我们使用data[size]=0
public void removeIndex(int index){
if(index<0 || index >= size){
System.err.println("remove index illegal!");
return;
}
//当size== data.length为了防止越界,保证程序健壮性,i< size-1
for (int i = index; i <size -1; i++) {
//i =size-1,最后一个位置
data[i] = data[i+1];
}
size--;
data[size] = 0;//删除原先数组的最后一个位置的元素
}
<2>按照值删除
<a>这个值出现一次的删除
public void removeValueOnce(int value){
for (int i = 0; i <size; i++) {
if(data[i] == value){
//此时i对应的索引是第一个值为value的结点
removeIndex(i);
return;
}
//若这个分支走完,还没找到值相同的结点,说明这个值不存在
}
}
<b>这个值多次出现的删除。
个人觉得这个方法真的牛,它可以删除连续出现的一个值,也可以删除一个多次出现的不连续的值
我有点不明白这里是怎么体现的,但是它可以实现。
例如一个这样的数组1,2,2,2,2
出现这种连续的重复元素时,需要使用while(data[i] == value),同时i!= size
为什么用while而不是用for呢,因为for每此出现这个值都需要判断,如果使用while就可以让它自己进行判断,不需要我们写了,这是一个精妙的写法
public void removeAllValue(int value){
for (int i = 0; i <size; i++) {
while(i!= size && data[i] == value){
removeIndex(i);
}
}
}
<3>删除头结点
这里使用我们已经写好的方法
public void removeFirst(){
removeIndex(0);
}
<4>删除尾结点
这里使用我们已经写好的方法
我还写了一个测试类,如果要练习的话,可以全部依次拷贝,感受一下它的精妙。由于要测试,我们需要写一个打印方法,方便打印。
<1>这是打印方法toString
public String toString(){
String ret = "[";
//遍历data数组
for (int i = 0; i <size; i++) {
ret +=data[i];
if(i!= size-1){
ret += ",";
}
}
ret += "]";
return ret;
}
<2>这是测试类,要写在同一个包里哦,直接拷贝就好了。
public class Test {
public static void main(String[] args) {
MyArray myArray = new MyArray(3);
// myArray.addLast(1);
// myArray.addLast(3);
// myArray.addLast(5);
// //存了3个数据,此时我们设定的大小已满
// myArray.addLast(7);
// //[1,3,5,7]
// //自动扩容后,检测结果
// System.out.println(myArray);
// myArray.addFirst(10);
// //[10,1,3,5,7]
// System.out.println(myArray);
// myArray.addIndex(1,22);
// //[10,22,1,3,5,7]
// System.out.println(myArray);
// myArray.addIndex(0,33);//头插
// myArray.addIndex(7,44);//尾插
// //插入一个不合法下标,不连续了
// myArray.addIndex(10,55);
// // [33,10,22,1,3,5,7,44]
// System.out.println(myArray);
// System.out.println(myArray.contains(7));
// //true
// System.out.println(myArray.getByValue(22));
// // 2
// System.out.println(myArray.get(3));
// //1
// System.out.println(myArray.set(1,100));
// //将下标为1位置的元素值改为100
// System.out.println(myArray);
// //[33,100,22,1,3,5,7,44]
// myArray.removeFirst();
// myArray.removeLast();
// // 100,22,1,3,5,7
// myArray.removeIndex(1);
// // 100,1,3,5,7
// System.out.println(myArray);
//测试按照值删除元素
myArray.addLast(1);
myArray.addLast(2);
myArray.addLast(3);
myArray.addLast(2);
// myArray.removeValueOnce(2);
// // 1,2,2
myArray.removeAllValue(2);
System.out.println(myArray);
}
}