数据结构基础1——动态数组

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

作为一名数据结构的初学小白,我从动态数组学起,先后学习了动态数组,队列,栈,链表,二叉搜索树等基本的数据结构,本博客用的是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.容量的变化

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值