数据结构(Data Structure)是计算机存储、组织数据的方式,指相互之间存在一种或多种特定关系的数据元素的集合。
下面我们就开一个新坑,数据结构。数据结构,简单来说就是存放数据的方式,这些方式多种多样,我们来一点一点认识。
首先就是最简单的顺序表。
1.顺序表的认识
线性表:n个具有相同特性的数据元素的有限序列
而常见的线性表就包含我们今天要认识的顺序表,同时还有列表、栈等等。
首先,它是一个序列,元素之间是有顺序的,若元素存在多个,则第一个无前驱,最后一个无后继,每个元素都有且只有一个前驱和后继。
顺序表是使用数组来完成的一种数据结构,顺序表的底层结构就是一个数组。
我们java已经为我们写好了顺序表,并带了很多方法:
我们第一步要做的,就是自己实现这样一个数据结构,并将其中主要的方法,都一并实现。
2.顺序表的实现
显然,顺序表本身也是一个类,为了区分,我们可以给自己写的类取名MyArrayList。那么我们现在就可以创建这个类。
1)基础框架
我们刚才讲了,我们顺序表的底层逻辑是数组。因此我们这个类需要一个数组进行操作,用来存放数据元素。其次,我们需要获取数组当前已经使用的长度,这样方便我们操作,我们可以定义一个usedsize进行记录,初始值为0,最后,我们可以单独指定数组的容量,方便我们后续进行修改。综合以上,我们可以先写出这个类的框架:
public class MyArrayList {
private int[] elem;//存放数据元素
private int usedsize;
private static final int initCapacity=10;
public MyArrayList(){
this.elem=new int[initCapacity];
}
}
这样这个数据结构基本的结构就完成了,下面我们就要实现顺序表中自带的一些方法。具体有这些:
dispaly():打印所有数据
add():新增数据
contains():是否包含某个元素
indexof():查找某个元素位置
set():更新某位置的值
remove():删除第一次出现的关键字
size():获取顺序表的长度
clear():清空顺序表
那么我们可以先把这些方法构造出来,再谈具体实现:
public class MyArrayList {
private int[] elem;//存放数据元素
private int usedsize;
private static final int initCapacity=10;
public MyArrayList(){
this.elem=new int[initCapacity];
}
public void add(int data) { }
// 在 pos 位置新增元素
public void add(int pos, int data) { }
// 判定是否包含某个元素
public boolean contains(int toFind) { return true; }
// 查找某个元素对应的位置
public int indexOf(int toFind) { return -1; }
// 获取 pos 位置的元素
public int get(int pos) { return -1; }
// 给 pos 位置的元素设为 value
public void set(int pos, int value) { }
//删除第一次出现的关键字key
public void remove(int toRemove) { }
// 获取顺序表长度
public int size() { return 0; }
// 清空顺序表
public void clear() { }
//打印所有数据
public void display(){}
}
2)方法的具体实现
下面我们来对每个方法都进行具体的实现。
display():
实质上就是对数组进行遍历
public void display(){
//实质上就是遍历数组
for (int i = 0; i < usedsize; i++) {
System.out.println(this.elem[i]);
}
}
add():
要在数组最后进行新增
public void add(int data) {
this.elem[usedsize]=data;
this.usedsize++;
//考虑数组已满情况
}
同时,我们还要考虑数组已满的情况。我们可以设置一个方法对是否已满进行判断,如果是,我们就进行扩容。那么,现在我们来设置一个isfull方法。
同时,我们,还要相应改变add方法,一旦数组满了,要进行扩容。
public void add(int data) {
this.elem[usedsize]=data;
this.usedsize++;
//考虑数组已满情况
if(isfull()){
this.elem= Arrays.copyOf(this.elem,2*this.elem.length);
}
}
public boolean isfull(){
if (this.elem.length==this.usedsize){
return true;
}
return false;
}
这里,一旦数组已满,我们就扩容至两倍。当然,扩容我们也可以单独写成方法。
add(int pos,int data):
属于方法的重载,在pos位置新增元素。我们需要把pos后面的数据都往后挪一位,需要从最后一个开始挪,从后往前挪。要先考虑已满扩容问题,如果已满,要先扩容,再进行后续操作。同时我们也要注意pos的合法性,数据结构是一门严谨的学科。
public void add(int pos, int data) {
if(pos<0||pos>this.usedsize){
System.out.println("位置不合法");
return ;
}
if(isfull()){
this.elem= Arrays.copyOf(this.elem,2*this.elem.length);
}
for (int i = usedsize-1; i >=pos ; i++) {
this.elem[i+1]=this.elem[i];
}
this.elem[pos]=data;
this.usedsize++;
}
contains():
找是否包含,同样是遍历数组,这个也很好理解。
public boolean contains(int toFind) {
for (int i = 0; i < usedsize; i++) {
if(this.elem[i]==toFind){
return true;
}
}
return false;
}
注意这里是整形的比较因从我们使用==,如果是引用类型我们也不要忘了使用equals。
查找某个元素对应位置/查找某个位置对应元素
这个只需要注意pos的合法性就可以了,剩下的就是老生常谈的遍历数组。
public int indexOf(int toFind) {
for (int i = 0; i < usedsize; i++) {
if(this.elem[i]==toFind){
return i;
}
}
System.out.println("该元素不存在");
return -1;
}
public int get(int pos) {
if(pos<0||pos>this.usedsize){
System.out.println("位置不合法");
return -1;
}
return this.elem[pos];
}
set():
更新操作,同样,判断是否合法。
public void set(int pos, int value) {
if(pos<0||pos>this.usedsize){
System.out.println("pos不合法");
}
this.elem[pos]=value;
}
size():
获取顺序表长度,这个就很简单了,直接返回usedsize就可以了
public int size() { return usedsize; }
remove():
删除第一次出现的关键字。遍历数组,找不到直接返回,找到了从后往前进行覆盖。
public void remove(int toRemove) {
int pos=-1;
for (int i = 0; i < usedsize; i++) {
if(this.elem[i]==toRemove){
pos=i;
break;
}
}
if(pos==-1)
return;
for(int i=pos;i<this.usedsize-1;i++){
this.elem[i]=this.elem[i+1];
}
this.usedsize--;
}
clear():
清空数据,一般我们可以直接把usedsize置为0,这样就清空了。当我们表中存放的是地址时,我们就要一个个设置为null,防止内存泄漏。
public void clear() {
this.usedsize=0;
}
那么我们就完成了自己的顺序表的实现。
public class MyArrayList {
private int[] elem;//存放数据元素
private int usedsize;
private static final int initCapacity=10;
public MyArrayList(){
this.elem=new int[initCapacity];
}
public void add(int data) {
this.elem[usedsize]=data;
this.usedsize++;
//考虑数组已满情况
if(isfull()){
this.elem= Arrays.copyOf(this.elem,2*this.elem.length);
}
}
public boolean isfull(){
if (this.elem.length==this.usedsize){
return true;
}
return false;
}
// 在 pos 位置新增元素
public void add(int pos, int data) {
if(pos<0||pos>this.usedsize){
System.out.println("位置不合法");
return ;
}
if(isfull()){
this.elem= Arrays.copyOf(this.elem,2*this.elem.length);
}
for (int i = usedsize-1; i >=pos ; i++) {
this.elem[i+1]=this.elem[i];
}
this.elem[pos]=data;
this.usedsize++;
}
// 判定是否包含某个元素
public boolean contains(int toFind) {
for (int i = 0; i < usedsize; i++) {
if(this.elem[i]==toFind){
return true;
}
}
return false;
}
// 查找某个元素对应的位置
public int indexOf(int toFind) {
for (int i = 0; i < usedsize; i++) {
if(this.elem[i]==toFind){
return i;
}
}
System.out.println("该元素不存在");
return -1;
}
// 获取 pos 位置的元素
public int get(int pos) {
if(pos<0||pos>this.usedsize){
System.out.println("pos不合法");
return-1;
}
return this.elem[pos];
}
// 给 pos 位置的元素设为 value
public void set(int pos, int value) {
if(pos<0||pos>this.usedsize){
System.out.println("pos不合法");
}
this.elem[pos]=value;
}
//删除第一次出现的关键字key
public void remove(int toRemove) {
int pos=-1;
for (int i = 0; i < usedsize; i++) {
if(this.elem[i]==toRemove){
pos=i;
break;
}
}
if(pos==-1)
return;
for(int i=pos;i<this.usedsize-1;i++){
this.elem[i]=this.elem[i+1];
}
this.usedsize--;
}
// 获取顺序表长度
public int size() { return usedsize; }
// 清空顺序表
public void clear() {
this.usedsize=0;
}
//打印所有数据
public void display(){
//实质上就是遍历数组
for (int i = 0; i < usedsize; i++) {
System.out.println(this.elem[i]);
}
}
}
以后用到顺序表,我们并不用自己实现,可以直接使用ArrayList这个类。
同时,我们可以双击shift->classes->ArrayList,来进行源码的查看。
下面我们来学习这个类的使用。
3.顺序表的使用
public static void main(String[] args) {
ArrayList<Integer> arrayList=new ArrayList<>();
}
这就是我们最常用的无参构造方法,我们也可以在后面的小括号里添加容量。
当我们写出无参方法,并不分配内存,默认在第一次调用add时才会分配大小为10的内存,而扩容会按照1.5倍的方式进行扩容。
各种方法的使用,我们通过.进行调用,基本和我们自己写的没有区别。
我们要注意一个,就是remove。可以用来删除下标,也可以用来直接删除数据
public class Test {
public static void main(String[] args) {
ArrayList<Integer> arrayList=new ArrayList<>();
arrayList.remove(2);//删除的是下标
arrayList.remove(new Integer(2));//删除的是数据
}
}
4.小结
这篇文章我们主要讨论了顺序表,一种由数组构造的数据结构,比较简单,希望大家可以认真掌握。