一、什么是数据结构?
概念:
数据结构是一门基础的学科,是研究数据如何在计算机中进行组织和存储,使得我们可以高效的获取数据和修改数据的。
数据结构可以分为三类:
1.线性结构:数组、队列、栈、链表、哈希表…
2.树形结构:二叉树、二分搜索树、AVL树,红黑树、堆、Trie、线段树、并查集…
3.图结构:邻接矩阵、邻接表
为什么要学习数据结构?
好的程序是数据结构+算法来实现的:数据结构+算法=程序
在遇到不同的问题时,选择对应的数据结构方法,能更快更好的进行解决。
学习数据结构还可以锻炼我们的代码和思维能力。
二、数组
1.数组基础
- 数组是用来存储一组类型相同的数据的集合
- 在内存中,分配连续的空间,数组创建时要指定容量(大小)
- 数据类型[] 数组名 int[] arr = new int[10] int[] arr2 = {1,2,3,4}
- 索引---访问数组时通过索引进行操作 索引从0开始,最大为 arr.length -1
- 常见的错误: NullPointException ArrayIndexOutOfBoundsException
- 常见的数组: 字符串, 对象数组,哈希表
使用数组时,最重要的就是数组的索引,通过索引可以对数组进行改和查操作。
数组最大的优点:快速查询。
数组最好应用于索引有语义的情况。例如:通过学生学号进行查询等
2.向数组中添加元素
public void add(int item) {//this.size 指向的是待插入元素的位置this.data[this.size] = item;this.size++;}
3.向指定位置添加元素
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.sizethis.size++;}
有了向指定位置添加元素的方法后,就可以修改上面所写的尾部添加元素的方法:
public void add(int item) {//this.size 指向的是待插入元素的位置addInIndex(this.size, item);}
也可以写一个头部添加的方法:
public void addHead(int item) {addInIndex(0, item);}
4.获取指定位置的元素和修改指定位置的元素
//修改指定位置的值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];}
5.包含、搜索和删除元素
//查询指定的值在数组中是否存在,如查存在,获取索引,否则返回-1public 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;}
6.打印数组
打印数组前提是重写toString方法:
@Overridepublic 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);}
7.出现的问题和解决方法:
- 向数组中继续添加一个元素,就会出错(解决方法:扩容)
- 删除元素后,空间利用率低--(解决办法:缩容)
- 现在只能处理int类型,如何处理多种类型--(解决办法:泛型)
新增了一个改变容积的方法
//向数组中指定位置添加元素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.sizethis.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<T> {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.sizethis.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];}//查询指定的值在数组中是否存在,如查存在,获取索引,否则返回-1public 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;}@Overridepublic 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<Integer> 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.删除元素后,空间利用率低--(解决办法:缩容)*/}}