数组
我们对数组的增删改查操作的时间复杂度为O(1)或者O(n)
- 即使我们在数组的删除操作的时候是删除某个元素,这个时候只是告诉我们元素值,然后让我们删除数组中第一个遇到数值和指定元素值大小相等的元素,这个时候我们先要找到位置,然后进行删除,这个时候是要进行一个双层的for循环,但是这个删除操作的时间复杂度还是O(n) —> 因为这个时候我们的内层for循环只有在外层for循环执行最后一次的时候才会执行 ( 因为我们的内层for循环在一个if语句中,这个if语句只有当我们的外层for循环执行最后一次的时候才满足 ) —> 这个时候时间复杂度就是一个O(n)
我们的数组扩容一般都是扩容为原来的两倍
- 我们的数组扩容的方法时在数组增加元素的方法中调用的, 并且我们也知道我们的数组扩容的时候的时间复杂度为O(n)
- (因为我们的数组扩容就是创建一个新的数组,然后将我们的原本数组中的内容复制到我们的新数组中来,这个时候数组内容复制的操作就要使用到for循环,时间复杂度就是O(n) , 最后我们将数组内容复制到新数组之后不要忘记让我们的arr 引用指向我们的新数组 )
- 但是我们可以发现我们的在执行增加元素的方法的时候并不是每次都执行扩容的操作,这个时候我们要如何计算我们的增加元素的方法的时间复杂度?
- 这个时候就要使用到" 均摊复杂度 "
- 那么什么是均摊复杂度? 我们这个时候就以上面的问题为例(假设这个时候我们是往数组的末尾添加元素):
- 我们的数组容量如果为n那么我们每当执行n+1次的时候才会使用到一次扩容方法,这个时候扩容方法中执行的次数是n次,我们的原本的向末尾添加元素的方法的每次是执行一次,而我们每当执行n+1次向默认添加元素的操作之后就要执行一次数组扩容,那么将一次数组扩容中执行的n次操作"均摊"到我们的n+1次操作中,这个时候就是我们每次执行向末尾添加元素的操作的时间复杂度就是 2n+1/n , 也就是2 + 1/n,那么时间频度就是T(2 + 1/n),但其实时间复杂度还是O(1)
我们的数组一般都是当数组内元素实际只占用了数组容量的四分之一的时候才进行缩容,缩容为原来的数组容量的二分之一
- 那么为什么明明是缩容为原来数组的二分之一,但是我们却是在占据容量只有四分之一的时候才缩容?
- 这个时候我们要避免复杂度震荡的问题出现:
- 那么什么是复杂度震荡的问题?
- 就是如果我们是每次当我们的数组中实际元素占用了二分之一的时候就进行缩容,这个时候没有一个预留的缓冲空间, 这个时候就有可能是当我们刚刚数组中的实际存储元素占了数组容量二分之一的时候我们就进行了缩容,这个时候一旦一缩容,数组就是一个满的状态了,假如这个时候我们再向数组中添加元素,这个时候立马又会发生数组扩容,然后我们扩容之后数组的容量刚刚又是数组容量的二分之一了,这个时候如果我们又删除一个,这个时候我们又会进行一次缩容的操作,如果一直这样循环下去,这种情况就是一种时间复杂度震荡的问题:
- 我们通过均摊时间复杂度的方式计算出我们的删除操作(删除末尾元素)和添加元素(向末尾添加)的操作的时间复杂度其实都是n(1), 但是如果是遇到了上面的情况,这个时候增加元素和删除元素的操作中每次都执行了扩容操作,这个时候时间复杂度就从O(1)变为了O(n) ----> 我们将这种现象就称之为 “时间复杂度震荡”
注意: 让我们的arr引用指向我们的新数组的时候如果形参位置也是arr,这个时候我们一定要使用this表名是给属性arr赋值,而不是给形参arr赋值
这里我们给出我们的自定义的一个数组类(这个数组类中使用了泛型,并且这个数组类中提供了很多操作自定义数组的方法):
package com.ffyc.util;
import org.junit.Test;
import java.util.Arrays;
public class MyArray<T> {
private T [] arr;
private int size;
private int capacity;
public MyArray(){
this(100);
}
public MyArray(int capacity) {
this.arr = (T [])new Object[capacity];
this.size = 0;
this.capacity = capacity;
}
public void addHead(T val){
add(0,val);
}
public void addLast(T val){
add(size,val);
}
public void add(int index, T val){
if(index < 0 || index > size){
throw new IllegalArgumentException("插入位置不合法!");
}
if(size == capacity){
resize(arr,capacity*2);
}
for(int i = this.size; i>index; i--){
arr[i] = arr[i-1];
}
arr[index] = val;
this.size+=1;
}
public void delete(T val){
for(int i = 0; i<this.size; i++){
if(arr[i].equals(val)){
for (int j = i; j<this.size - 1; j++){
arr[j] = arr[j+1];
}
this.size-=1;
if (size < capacity/4 && capacity/2 > 1){
suorong(arr,capacity/2);
}
}
}
}
public int getSize(){
return this.size;
}
public int getCapacity(){
return this.capacity;
}
public int getIndex(T val){
for (int i = 0; i < arr.length; i++) {
if (val.equals(arr[i])){
return i;
}
}
return -1;
}
public T getElement(int index){
if (index < 0 || index > arr.length - 1){
throw new IllegalArgumentException("查询位置不合法!");
}
return arr[index];
}
private void resize(T [] arr,int newCapacity){
T [] newArr = (T [])new Object[newCapacity];
for (int i = 0; i < size; i++) {
newArr[i] = arr[i];
}
this.arr = newArr;
capacity = newCapacity;
}
private void suorong(T [] arr,int newCapacity){
T [] newArr = (T[])new Object[newCapacity];
for (int i = 0; i < size; i++) {
newArr[i] = arr[i];
}
this.arr = newArr;
capacity = newCapacity;
}
public String toString(){
return Arrays.toString(arr);
}
}
class Test2{
public static void main(String[] args) {
MyArray myArray = new MyArray(3);
System.out.println(myArray.getCapacity());
System.out.println(myArray.toString());
myArray.addLast(1);
myArray.addLast(2);
myArray.addLast(3);
myArray.addLast(3);
myArray.addLast(3);
System.out.println(myArray.getCapacity());
System.out.println(myArray.toString());
myArray.delete(1);
myArray.delete(2);
myArray.delete(3);
myArray.delete(3);
myArray.delete(3);
myArray.delete(3);
System.out.println(myArray.getCapacity());
System.out.println(myArray.toString());
}
}