数据结构——用Java实现数组

一、什么是数据结构?

概念:

数据结构是一门基础的学科,是研究数据如何在计算机中进行组织和存储,使得我们可以高效的获取数据和修改数据的。

数据结构可以分为三类:

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.size
        this.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.包含、搜索和删除元素

//查询指定的值在数组中是否存在,如查存在,获取索引,否则返回-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;
    }

6.打印数组

打印数组前提是重写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);

       

    }

7.出现的问题和解决方法:

  1. 向数组中继续添加一个元素,就会出错(解决方法:扩容)
  2. 删除元素后,空间利用率低--(解决办法:缩容)
  3. 现在只能处理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.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.删除元素后,空间利用率低--(解决办法:缩容)
         */

    }

}

你可能感兴趣的:(数据结构)