提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
作为一名数据结构的初学小白,我从动态数组学起,先后学习了动态数组,队列,栈,链表,二叉搜索树等基本的数据结构,本博客用的是java语言,MyEclipse编译器,此系列几篇博客将依次展示所学内容及新的体会,恳请广大网友参考补充。日后正式学习时也会对此博客进行补充。
一、动态数组是什么
动态数组打破了数组是静态的这个观念。依靠动态数组数据结构,我们可以实现基本的添加,删除,查找元素,查找位置,以及动态数组的扩容缩容,动态数组的维护。
而在java中,实际上队列和栈都有相应的包来实现,但是也可以通过动态数组来写出队列和栈这两种基础结构的算法。
二、动态数组的基本成员变量
1.E[] data
动态数组的数组名data。由于我们的数组装下的不仅仅是最常见的整型,所以我们在这里引用泛型。根据java的语法我们应该这样定义数组
data=(E[])new Object[capacity];
2.size(数组长度)与数组容量(capacity)
size是数组的长度,我们在访问时可以引用getSize()这一方法,而capacity是数组的容积大小,是装数据的最大极限。这里的capacity存在是为了防止数据的溢出。
3.代码展示
public class Array {
private E[] data;
private int size;
public Array(int capacity){
data=(E[])new Object[capacity];
size=0;
}
//无参构造函=-
public Array(){
this(10);
}
二.基础函数
1.获取size,capacity,判断数组是否为空
代码
public int getSize(){
return size;
}
public int getCapacity(){
return data.length;
}
public boolean isEmpty(){
return size==0;
}
2.数组中添加元素
逻辑:给出索引以及要添加的元素。
再利用for循环。
基础逻辑是将从index开始的地方将各元素都往后推一个位置,然后把要添加的元素插入
不要忘了维护数组,size++;
代码如下
for(int i=size-1;i>=index;i--){
data[i+1]=data[i];
data[index]=e;
size++;
3.数组中删除元素
逻辑:与添加元素类似,把index后的每一个元素都往前挪一个。然后对数组进行维护,size–;
代码如下
for(int i=index+1;i<size;i++)
data[i-1]=data[i];
size--;
data[size]=null;
这里需要注意,java中有一个回收功能,但是为了让逻辑更清楚,所以让data[size]=null
4.知索引查元素,知元素查索引
前者是最基础的数组功能,及数组本身的查找功能
而后者需要遍历
后者代码如下
public int find(int e){
for(int i=0;i<size;i++){
if(data[i].equals(e))
return i;
}
return -1;
}
这里要注意的是第三行
对于字符串变量来说,使用“==”和“equals()”方法比较字符串时,其比较方法不同。
“==”比较两个变量本身的值,即两个对象在内存中的首地址。
“equals()”比较字符串中所包含的内容是否相同。
5.修改索引处的元素
对索引处的数组进行重新赋值
代码如下
void set(int index,E e){
if(index<0||index>=size)
throw new IllegalArgumentException("Get failed. Index is illegal.");
data[index]=e;
}
三.动态数组的扩容与缩容
1.为什么要扩容缩容
在动态数组中,我们会添加元素,当添加元素较多时size逐渐要与capacity相等甚至超过,我们则需要维护我们的数组,即改变数组的容量。
反之,删除元素时若删除较多会浪费内存空间,所以要对动态数组进行缩容
2.扩容思路
代码
public void add(int index,E e){
if(size==data.length)
throw new IllegalArgumentException(“Add failed. Array is full”);
if(index<0||index>size)
resize(2*data.length);
for(int i=size-1;i>=index;i--){
data[i+1]=data[i];
data[index]=e;
size++;
}
}
为什么要扩容2倍,并且是根据data的长度来扩,因为我们在不知道data具体的长度时无法给出一个具体的值来进行扩容,而二倍的容量能做到既不浪费成本也不会使得数组溢出。
3.缩容思路
代码
public E remove(int index){
if(index<0||index>=size)
throw new IllegalArgumentException(“Get failed.Index is illegal”);
E ret=data[index];
if(size==data.length/2)
resize(data.length/2);
for(int i=index+1;i<size;i++)
data[i-1]=data[i];
size--;
data[size]=null;
return ret;
}
与扩容类似,根据数组的长度来进行判断。
4.resize逻辑及代码
逻辑:建立一个新的数组,并将其容量变为数组长度的二倍或者一半,然后再利用for循环,使得新的数组的元素及位置都与之前的老数组相同
代码
private void resize(int newCapacity){
E[] newData=(E[])new Object[newCapacity];
for(int i=0;i<size;i++)
newData[i]=data[i];
data=newData;
}
四.动态数组的打印
res是一个StringBuilder类的对象,StringBuffer和其类似,可以运用append对其进行添加字符。
逻辑就是普通数组的遍历。
而format可以让编译器按所给格式打出相应字符。
五.整体代码
public class Array {
private E[] data;
private int size;
public Array(int capacity){
data=(E[])new Object[capacity];
size=0;
}
//无参构造函=-
public Array(){
this(10);
}
public int getSize(){
return size;
}
public int getCapacity(){
return data.length;
}
public boolean isEmpty(){
return size==0;
}
public void addLast(E e){
if(size==data.length)
throw new IllegalArgumentException("AddLast failed. Srray is full.");
data[size]=e;
size++;
}
public void addFirst(E e){
add(0,e);
}
public void add(int index,E e){
if(size==data.length)
throw new IllegalArgumentException("Add failed. Array is full");
if(index<0||index>size)
resize(2*data.length);
for(int i=size-1;i>=index;i--){
data[i+1]=data[i];
data[index]=e;
size++;
}
}
E get(int index){
if(index<0||index>=size)
throw new IllegalArgumentException("Get failed.Index is illegal");
return data[index];
}
public E getLast(){
return get(size-1);
}
public E getFirst(){
return get(0);
}
void set(int index,E e){
if(index<0||index>=size)
throw new IllegalArgumentException("Get failed. Index is illegal.");
data[index]=e;
}
public boolean contains(int e){
for(int i=0;i<size;i++){
if(data[i].equals(e))
return true;
}
return false;
}
public int find(int e){
for(int i=0;i<size;i++){
if(data[i].equals(e))
return i;
}
return -1;
}
public E remove(int index){
if(index<0||index>=size)
throw new IllegalArgumentException("Get failed.Index is illegal");
E ret=data[index];
if(size==data.length/2)
resize(data.length/2);
for(int i=index+1;i<size;i++)
data[i-1]=data[i];
size--;
data[size]=null;
return ret;
}
public E removeFirst(){
return remove(0);
}
public E removeLast(){
return remove(size-1);
}
public void removeElement(int e){
int index=find(e);
if(index!=-1)
remove(index);
}
@Override
public String toString(){
StringBuilder res=new StringBuilder();
res.append(String.format(“Array: size=%d,capacity=%d\n”,size,data.length));
res.append("[");
for(int i=0;i<size;i++){
res.append(data[i]);
if(i!=size-1)
res.append(",");
}
res.append("]");
return res.toString();
}
private void resize(int newCapacity){
E[] newData=(E[])new Object[newCapacity];
for(int i=0;i<size;i++)
newData[i]=data[i];
data=newData;
}
总结
1.动态数组为什么动态
1.动态数组相对于普通数组可以很灵活的插入,删除元素。
2.动态数组的容量(capacity)可以改变
3.动态数组的功能化十分强大。
2.维护
1.size的维护
2.index,size的判别。我们运用throw new IllegalArgumentException来抛出错误并终止程序
3.容量的变化