1

Java 八大数据结构之数组,栈,队列

一:介绍
数据结构是计算机存储,组织数据的方式,指相互之间存在一种或多种特定的数据元素的集合
image.png
二:数组(Array)
数组是用来存放同一种数据类型的集合,注意只能存放同一种数据类型(Object类型数组除外)
数组申明
方式一:
数据类型 [] 数组名称=new 数据类型[数组长度];

//声明一个数组长度为5,只能存放int类型的数据
 int[] num=new int[5];
 //给数组第二个元素赋值
        num[1]=12;
        //输出第一个元素和第二个元素,结果0,12默认没赋值的是0
        System.out.println(num[0]);
        System.out.println(num[1]);
        //结果
        System.out: 0
        System.out: 12
        
         String[] ss=new String[4];
        ss[0]="Rocky";
        System.out.println(ss[0]);
        System.out.println(ss[1]);
        //结果
        System.out: Rocky
        System.out: null
   //可以看到:int:默认是0,String:默认是null,boolean:默认是flase

方式二:
数据类型 [] 数组名称 = {数组元素1,数组元素2,......}

 int[] ss= {1,4,3,5,7,3};
        for (int i=0;i<ss.length;i++){
            System.out.println(ss[i]);
        }
        
    

自定义一个数组的实现,实现数组的增删改查

public class MyArray {
    //定义一个数组
    private int[] intArray;
    //定义数组的实际有效长度
    private int elems;
    //定义数组的最大长度
    private int length;

    //默认构造一个长度为50的数组
    public MyArray() {
        elems = 0;
        length = 50;
        intArray = new int[length];
    }

    //构造函数,初始化一个长度为length 的数组
    public MyArray(int length) {
        elems = 0;
        this.length = length;
        intArray = new int[length];
    }

    //获取数组的有效长度
    public int getSize() {
        return elems;
    }

    //添加数组
    //假设操作人是不会添加重复元素的,如果有重复元素对于后面的操作都会有影响
    public boolean add(int value) {
        //添加元素默认是从角标elems,0开始添加
        if (elems == length) {
            return false;
        } else {
            intArray[elems] = value;
            elems++;
        }
        return true;
    }

    //根据下标获取元素,查找下标值在数组下标有效范围内,返回下标所表示的元素,查找下标超出数组下标有效值,提示访问下标越界
    public int get(int i) {
        if (i < 0 || i > elems) {
            System.out.println("访问下标越界");
        }
        return intArray[i];
    }

    //查找元素,查找的元素如果存在则返回下标值,如果不存在,返回 -1
    public int find(int searchValue) {
        int i;
        for (i = 0; i < elems; i++) {
            if (intArray[i] == searchValue) {
                break;
            }
        }
        if (i == elems) {
            return -1;
        }
        return i;
    }

    //删除元素,果要删除的值不存在,直接返回 false;否则返回true,删除成功
    public boolean delete(int value) {
        int k = find(value);
        if (k == -1) {
            return false;
        } else {
            if (k == elems - 1) {
                elems--;
            } else {
                //删除元素最关键的是遍历元素,并替换删除元素之后坐标的数值
                for (int i = k; i < elems - 1; i++) {
                    intArray[i] = intArray[i + 1];

                }
                elems--;
            }
            return true;
        }
    }

    //修改数据,修改成功返回true,修改失败返回false
    public boolean modify(int oldValue, int newValue) {
        int i = find(oldValue);
        if (i == -1) {
            System.out.println("需要修改的数据不存在");
            return false;
        } else {
            intArray[i] = newValue;
            return true;
        }
    }

    /**
     * 遍历显示元素
     */
    public void display() {
        for (int i = 0; i < elems; i++) {
            System.out.print(intArray[i] + " ");
        }
        System.out.println();
    }
}

调用

 //创建自定义封装数组结构,数组大小为4
        MyArray array=new MyArray(4);
        //添加4个元素
        array.add(3);
        array.add(5);
        array.add(6);
        array.add(7);
        //显示数据
        array.display();
        //根据下标为0的元素
        int i = array.get(0);
        System.out.println(i);
        //删除7的元素
        array.delete(7);
        //将元素6修改为12
        array.modify(6, 12);
        array.display();
        
        //结果
        System.out: 3 5 6 7 
        System.out: 3
        System.out: 3 5 12 

数组一旦创建后,大小就固定了,不能动态扩展数组的元素个数。如果初始化你给一个很大的数组大小,那会白白浪费内存空间,如果给小了,后面数据个数增加了又添加不进去了。

二维数组
定义:二维数组其实就是一个元素为一维数组的数组;

//方式一
 int a[][] = new int[3][4];
 int[][] b = new int[3][4];//一般使用
 int[] c[] = new int[3][3];
  b[0][0]=1;
   b[0][1]=5;
  System.out.print(b[0][0]+",");
  System.out.print(b[0][1]+",");
 System.out.print(b[3][6]+","); //java.lang.ArrayIndexOutOfBoundsException: length=3; index=3
 //结果
 System.out: 1,5,

 //方式二
  int d[][] = {{1, 3, 5, 7}, {9, 11, 13, 15}, {17, 19, 21, 23}};
  //遍历结果
   for(int x=0;x<d.length;x++){
            for(int y=0;y<d[x].length;y++){
                System.out.print(d[x][y]+",");
            }
            System.out.println();
        }
        //结果
System.out: 1,3,5,7,
System.out: 9,11,13,15,
System.out: 17,19,21,23,
  //方式三
  int e[][] = new int[3][];
        e[0] = new int[2];
        e[1] = new int[2];
        e[2] = new int[2];

三:栈(Stack)
栈(英语:stack)又称为堆栈或堆叠,栈作为一种数据结构,是一种只能在一端进行插入和删除操作的特殊线性表。它按照先进后出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据(最后一个数据被第一个读出来)。栈具有记忆作用,对栈的插入与删除操作中,不需要改变栈底指针。
 栈是允许在同一端进行插入和删除操作的特殊线性表。允许进行插入和删除操作的一端称为栈顶(top),另一端为栈底(bottom);栈底固定,而栈顶浮动;栈中元素个数为零时称为空栈。插入一般称为进栈(PUSH),删除则称为退栈(POP)
  由于堆叠数据结构只允许在一端进行操作,因而按照后进先出(LIFO, Last In First Out)的原理运作。栈也称为后进先出表。

自定义实现一个栈,可以出栈,入栈,可以扩容,可以访问栈顶数据

public class MyStack {
    //存储元素的数组,声明为Object类型能存储任意类型的数据
    private Object[] elementData;//数组
    private int maxSize;//栈最大数量
    private int top;//栈顶的指针

    public MyStack() {
        this.elementData = new Object[10];
        this.top = -1;
        this.maxSize = 10;
    }

    public MyStack(int initialCapacity) {
        if (initialCapacity < 0) {
            throw new IllegalArgumentException("栈初始容量不能小于0: " + initialCapacity);
        }
        this.elementData = new Object[initialCapacity];
        this.maxSize = initialCapacity;
        top = -1;

    }

    //压入数据
    public Object push(Object value) {
        //是否需要扩容
        isGrow(top + 1);
        elementData[++top] = value;
        return value;

    }

    //弹出栈顶数据
    public Object pop() {
        Object obj = peek();
        remove(top);
        return obj;
    }

    //访问栈顶数据
    public Object peek() {
        if(top == -1){
            throw new EmptyStackException();
        }
        return elementData[top];
    }

    //判断栈是否为空
    public boolean isEmpty() {
        return (top == -1);
    }
    //删除栈顶元素
    public void remove(int top){
        //栈顶元素置为null
        elementData[top] = null;
        this.top--;
    }


    //判断栈是否满了
    public boolean isFull() {
        return (top == maxSize - 1);
    }

    /**
     * 是否需要扩容,如果需要,则扩大一倍并返回true,不需要则返回false
     *
     * @param minCapacity
     * @return
     */
    public boolean isGrow(int minCapacity) {
        int oldCapacity = maxSize;
        //如果当前元素压入栈之后总容量大于前面定义的容量,则需要扩容
        if (minCapacity >= oldCapacity) {
            //定义扩大之后栈的总容量
            int newCapacity = 0;
            //栈容量扩大两倍(左移一位)看是否超过int类型所表示的最大范围
            if ((oldCapacity << 1) - Integer.MAX_VALUE > 0) {
                newCapacity = Integer.MAX_VALUE;
            } else {
                newCapacity = (oldCapacity << 1);//左移一位,相当于*2
            }
            this.maxSize = newCapacity;
            int[] newArray = new int[maxSize];
            elementData = Arrays.copyOf(elementData, maxSize);
            return true;
        } else {
            return false;
        }
    }
}

调用

MyStack stack = new MyStack(3);
        stack.push(2);
        System.out.println(stack.peek());
        stack.push("Rocky");
        stack.push(true);
        stack.push("fight");//超过了3个自动扩容
       //访问栈顶数据
        System.out.println(stack.peek());
        stack.pop();
        stack.pop();
        stack.pop();
        System.out.println(stack.peek());
        //判断栈是否为空
        boolean aa=stack.isEmpty();
        System.out.println(aa);
        
        
        
        //结果
        System.out: 2
        System.out: fight
        System.out: 2
       System.out: false
       
      //利用栈实现字符翻转
       MyStack myStack = new MyStack();
                String str = "HelloWorld";
                char[] cha = str.toCharArray();
                for(char c : cha){
                    myStack.push(c);
                }
                while(!myStack.isEmpty()){
                    System.out.println(myStack.pop());
                }

四:队列(Queue)
队列(queue)是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。队列中没有元素时,称为空队列。
 队列的数据元素又称为队列元素。在队列中插入一个队列元素称为入队,从队列中删除一个队列元素称为出队。因为队列只允许在一端插入,在另一端删除,所以只有最早进入队列的元素才能最先从队列中删除,故队列又称为先进先出(FIFO—first in first out)线性表。

1.与栈不同的是,队列中的数据不总是从数组的0下标开始的,移除一些队头front的数据后,队头指针会指向一个较高的下标位置
image.png
2.我们再设计时,队列中新增一个数据时,队尾的指针rear 会向上移动,也就是向下标大的方向。移除数据项时,队头指针 front 向上移动。那么这样设计好像和现实情况相反,比如排队买电影票,队头的买完票就离开了,然后队伍整体向前移动。在计算机中也可以在队列中删除一个数之后,队列整体向前移动,但是这样做效率很差。我们选择的做法是移动队头和队尾的指针。
自定队列实现增删
3.如果向第②步这样移动指针,相信队尾指针很快就移动到数据的最末端了,这时候可能移除过数据,那么队头会有空着的位置,然后新来了一个数据项,由于队尾不能再向上移动了,那该怎么办呢?
image.png
4.为了避免队列不满却不能插入新的数据,我们可以让队尾指针绕回到数组开始的位置,这也称为“循环队列”。
image.png

弄懂原理之后实现:

public class MyQueue {
    private Object[] queArray;
    //队列总大小
    private int maxSize;
    //前端指针,队头指针
    private int front;
    //后端指针,队尾指针
    private int rear;
    //队列中元素的实际数量
    private int nItems;
    
    public MyQueue(int size){
        maxSize=size;
        queArray=new Object[maxSize];
        front=0;
        rear=-1;
        nItems=0;
        
    }
    
    //队列新增数据
    public void insert(int value){
        if (isFull()){
            System.out.println("队列已满!!!");
        }else {
            //如果队尾部指向顶了,那么循环回来,执行队列的第一个元素
            if (rear==maxSize-1){
                rear=-1;
            }
            //队尾指针加1,然后在队尾指针处插入新的数据
            queArray[++rear] = value;
            nItems++;
        }
    }

    //移除数据
    public Object remove(){
        Object removeValue = null ;
        if (!isEmpty()){
            removeValue=queArray[front];
            //把队头数据置为空
            queArray[front]=null;
            //队头指针+1
            front++;
            if (front==maxSize){
                //如果队头为总容量大小,置为0
                front=0;
            }
            nItems--;
            return removeValue;
        }
        return removeValue;
    }
    //返回队列的大小
    public int getSize(){
        return nItems;
    }
    //查看对头数据
    public Object peekFront(){
        return queArray[front];
    }
    //判断队列是否为空
    public boolean isEmpty(){
        return (nItems ==0);
    }
    //判断队列是否满了
    public boolean isFull(){
        return (nItems == maxSize);
    }
}

调用

      MyQueue queue = new MyQueue(3);
                queue.insert(1);
                queue.insert(2);
                queue.insert(3);//queArray数组数据为[1,2,3]

                System.out.println(queue.peekFront()); //1
                queue.remove();//queArray数组数据为[null,2,3]
                System.out.println(queue.peekFront()); //2

                queue.insert(4);//queArray数组数据为[4,2,3]
                //为什么是[4,2,3]不是[2,3,4]
                //应为队列队尾满了以后会循环到前面去
                /* 如果队尾部指向顶了,那么循环回来,执行队列的第一个元素
            if (rear==maxSize-1){
                rear=-1;
                //这个决定的,队尾的指针变了,到了最后了,就会循环回去,但是队头的指针是没有变动的
            }*/
                System.out.println(queue.peekFront()); //2,应为队头指针没有变动
                queue.remove();//queArray数组数据为[4,null,3]
                queue.remove();//queArray数组数据为[4,null,null]
                queue.insert(5);//queArray数组数据为[4,5,null]
                queue.insert(6);//queArray数组数据为[4,5,6]
                queue.insert(7);//队列已满,queArray数组数据为[4,5,6]
                System.out.println(queue.peekFront()); //4
                
                //结果
              System.out: 1
              System.out: 2
              System.out: 2
              System.out: 队列已满!!!
              System.out: 4

 请大家先看看什么是前缀表达式,中缀表达式,后缀表达式:这三种表达式其实就是算术表达式的三种写法,以 3+4-5为例
 前缀表达式:操作符在操作数的前面,比如 +-543(计算机从右向左扫描)
 中缀表达式:操作符在操作数的中间,这也是人类最容易识别的算术表达式 3+4-5
 后缀表达式:操作符在操作数的后面,比如 34+5-(计算机从左向右扫描)

END:我们都是阴沟里的虫子,但总还是得有人仰望星空。
 


Rocky_ruan
57 声望5 粉丝

不积跬步,无以至千里