数据结构是一门基础的学科,是研究数据如何在计算机中进行组织和存储,使得我们可以高效的获取数据和修改数据的。
1.线性结构:数组、队列、栈、链表、哈希表…
2.树形结构:二叉树、二分搜索树、AVL树,红黑树、堆、Trie、线段树、并查集…
3.图结构:邻接矩阵、邻接表
好的程序是数据结构+算法来实现的:数据结构+算法=程序
在遇到不同的问题时,选择对应的数据结构方法,能更快更好的进行解决。
学习数据结构还可以锻炼我们的代码和思维能力。
使用数组时,最重要的就是数组的索引,通过索引可以对数组进行改和查操作。
数组最大的优点:快速查询。
数组最好应用于索引有语义的情况。例如:通过学生学号进行查询等
public void add(int item) {
//this.size 指向的是待插入元素的位置
this.data[this.size] = item;
this.size++;
}
public void addInIndex(int index, int val) {
if (index < 0 || index > this.size) {
throw new IllegalArgumentException("index is invalid.");
}
//从index位置开始元素需要进行后移
for (int i = this.size - 1; i >= index; i--) {
this.data[i + 1] = this.data[i];
}
this.data[index] = val;
//更新this.size
this.size++;
}
有了向指定位置添加元素的方法后,就可以修改上面所写的尾部添加元素的方法:
public void add(int item) {
//this.size 指向的是待插入元素的位置
addInIndex(this.size, item);
}
也可以写一个头部添加的方法:
public void addHead(int item) {
addInIndex(0, item);
}
//修改指定位置的值
public void modifyValueByIndex(int index, int value) {
//入参一定要判断
if (index < 0 || index >= this.size) {
throw new IllegalArgumentException("index is invalid.");
}
this.data[index] = value;
}
//获取指定位置的值
public int getValueByIndex(int index) {
if (index < 0 || index >= this.size) {
throw new IllegalArgumentException("index is invalid.");
}
return this.data[index];
}
//查询指定的值在数组中是否存在,如查存在,获取索引,否则返回-1
public int containsValue(int val) {
//遍历数组
for (int i = 0; i < this.size; i++) {
if (val.equals(this.data[i])) {
return i;
}
}
return -1;
}
//删除操作
public int removeByIndex(int index) {
if (index < 0 || index >= this.size) {
throw new IllegalArgumentException("index is invalid.");
}
//删除操作的核心
/*
1.找到删除的位置
2.删除位置之后的元素要前移 arr[j-1]=arr[j]
*/
int delValue = this.data[index];
for (int i = index + 1; i < this.size; i++) {
this.data[i - 1] = this.data[i];
}
this.size--;
return delValue;
}
打印数组前提是重写toString方法:
@Override
public String toString() {
StringBuilder sb = new StringBuilder("[");
for (int i = 0; i < this.size; i++) {
sb.append(this.data[i]);
if (i != this.size - 1) {
sb.append(",");
}
}
sb.append("]");
return sb.toString();
}
然后写主函数,对所写的数组的方法进行调用和输出:
public static void main(String[] args) {
Random random = new Random();
//向数组中添加元素
for (int i = 0; i < 10; i++) {
myArr.add(random.nextInt(100));
}
//遍历数组
System.out.println(myArr.toString());
//查询
//1.索引为2的位置值是多少
int result = myArr.getValueByIndex(2);
System.out.println("index=2,value=" + result);
//2.获取值为57的元素的索引
int index = myArr.containsValue(57);
System.out.println("57元素的索引是" + index);
//删除值为57的元素
if (index != -1) {
result = myArr.removeByIndex(index);
System.out.println(result);
}
System.out.println(myArr);
//向数组中指定位置添加元素(3,99)
myArr.addInIndex(3, 99);
System.out.println(myArr);
}
新增了一个改变容积的方法
//向数组中指定位置添加元素
public void addInIndex(int index, int val) {
if (index < 0 || index > this.size) {
throw new IllegalArgumentException("index is invalid.");
}
//判断数组是否满
if (this.size == this.capacity) {
//扩容
resize(this.capacity * 2);
}
//从index位置开始元素需要进行后移
for (int i = this.size - 1; i >= index; i--) {
this.data[i + 1] = this.data[i];
}
this.data[index] = val;
//更新this.size
this.size++;
}
private void resize(int newCapacity) {
System.out.println("resize:" + newCapacity);
T[] newData = (T[]) (new Object[newCapacity]);
//将原数组驾到新数组里
for (int i = 0; i < this.size; i++) {
newData[i] = this.data[i];
}
//改变容器
this.data = newData;
this.capacity = newCapacity;
}
既然扩容可以,当然也可以进行缩容:
//删除操作
public int removeByIndex(int index) {
if (index < 0 || index >= this.size) {
throw new IllegalArgumentException("index is invalid.");
}
//删除操作的核心
/*
1.找到删除的位置
2.删除位置之后的元素要前移 arr[j-1]=arr[j]
*/
T delValue = this.data[index];
for (int i = index + 1; i < this.size; i++) {
this.data[i - 1] = this.data[i];
}
this.size--;
//判断是否缩容
if (this.size < this.capacity / 2 && this.capacity / 2 > 0) {
resize(this.capacity / 2);
}
return delValue;
}
注意:这样缩容会出现一个问题:复杂度的震荡
以本次的数组为例,在删除一定的元素,打到缩容的条件,进行缩容,但是下一步如果添加元素的话,又要进行扩容,时间复杂度就会增加,这就是复杂度的震荡
如何解决:
//判断是否缩容
if (this.size < this.capacity / 4 && this.capacity / 2 > 0) {
resize(this.capacity / 2);
}
return delValue;
}
缩容条件改为容积的1/4,这样留有空间进行添加元素,实在是不需要的情况下,再进行缩容。
处理多种类型的方法就是添加泛型:泛型 把类型当做参数
package com.algo.lesson.lesson01;
import java.util.Random;
/*
基于Java中的数组进行二次封装,制作一个可变数组
*/
//泛型:就是类型作为参数
public class MyArr {
private T[] data;//保存数据
private int size;//数组中实际存放元素的个数
int capacity;//容积
//构造函数
public MyArr(int capacity) {
if (capacity <= 0) {
this.capacity = 10;
} else {
this.capacity = capacity;
}
this.size = 0;
this.data = (T[]) (new Object[this.capacity]);
}
//获取数组中实际存放元素的个数
public int getSize() {
return this.size;
}
//获取数组的容积
public int getCapacity() {
return this.capacity;
}
//判断数组是否为空
public boolean isEmpty() {
return this.size == 0;
}
//向数组中添加元素(尾部)
public void add(T item) {
//this.size 指向的是待插入元素的位置
addInIndex(this.size, item);
}
//向数组中添加元素(头部)
public void addHead(T item) {
addInIndex(0, item);
}
//向数组中指定位置添加元素
public void addInIndex(int index, T val) {
if (index < 0 || index > this.size) {
throw new IllegalArgumentException("index is invalid.");
}
//判断数组是否满
if (this.size == this.capacity) {
//扩容
resize(this.capacity * 2);
}
//从index位置开始元素需要进行后移
for (int i = this.size - 1; i >= index; i--) {
this.data[i + 1] = this.data[i];
}
this.data[index] = val;
//更新this.size
this.size++;
}
private void resize(int newCapacity) {
System.out.println("resize:" + newCapacity);
T[] newData = (T[]) (new Object[newCapacity]);
//将原数组驾到新数组里
for (int i = 0; i < this.size; i++) {
newData[i] = this.data[i];
}
//改变容器
this.data = newData;
this.capacity = newCapacity;
}
//修改指定位置的值
public void modifyValueByIndex(int index, T value) {
//入参一定要判断
if (index < 0 || index >= this.size) {
throw new IllegalArgumentException("index is invalid.");
}
this.data[index] = value;
}
//获取指定位置的值
public T getValueByIndex(int index) {
if (index < 0 || index >= this.size) {
throw new IllegalArgumentException("index is invalid.");
}
return this.data[index];
}
//查询指定的值在数组中是否存在,如查存在,获取索引,否则返回-1
public int containsValue(T val) {
//遍历数组
for (int i = 0; i < this.size; i++) {
if (val.equals(this.data[i])) {
return i;
}
}
return -1;
}
//删除操作
public T removeByIndex(int index) {
if (index < 0 || index >= this.size) {
throw new IllegalArgumentException("index is invalid.");
}
//删除操作的核心
/*
1.找到删除的位置
2.删除位置之后的元素要前移 arr[j-1]=arr[j]
*/
T delValue = this.data[index];
for (int i = index + 1; i < this.size; i++) {
this.data[i - 1] = this.data[i];
}
this.size--;
//判断是否缩容
if (this.size < this.capacity / 4 && this.capacity / 2 > 0) {
resize(this.capacity / 2);
}
return delValue;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("[");
for (int i = 0; i < this.size; i++) {
sb.append(this.data[i]);
if (i != this.size - 1) {
sb.append(",");
}
}
sb.append("]");
return sb.toString();
}
public static void main(String[] args) {
MyArr myArr = new MyArr<>(10);
Random random = new Random();
//向数组中添加元素
for (int i = 0; i < 10; i++) {
myArr.add(random.nextInt(100));
}
//遍历数组
System.out.println(myArr.toString());
//查询
//1.索引为2的位置值是多少
int result = myArr.getValueByIndex(2);
System.out.println("index=2,value=" + result);
//2.获取值为57的元素的索引
int index = myArr.containsValue(57);
System.out.println("57元素的索引是" + index);
//删除值为57的元素
if (index != -1) {
result = myArr.removeByIndex(index);
System.out.println(result);
}
System.out.println(myArr);
//向数组中指定位置添加元素(3,99)
myArr.addInIndex(3, 99);
System.out.println(myArr);
/*
1.向数组中继续添加一个元素,就会出错(解决方法:扩容)
2.现在只能处理int类型,如何处理多种类型--(解决办法:泛型)
3.删除元素后,空间利用率低--(解决办法:缩容)
*/
}
}