数据结构_栈和队列

栈和队列:

         栈Stack:

         1.线性数据结构

         2.相比于数组,栈对应的操作是数组的子集.

         3.只能从一端添加元素,也只能从一端取出元素,这一端我们成为栈顶.

 

        

栈是一种后进先出的数据结构(Last In First Out(LIFO))

在计算机世界里,栈拥有着不可思议的作用.

栈的应用:

撤销应用,数据压入栈.

程序调用的系统栈:

编程子过程调用,

栈的实现:

         Stack<E>

        Void push(E);

         E pop();

         E peek();

         Int getSize();

         Boolean isEmpty();

从用户角度来看,支持这些操作就好.

具体底层实现,用户不关心

实际底层有多种实现方式.

package com.stack;

import com.array.Array;

public class ArrayStack<E> implements Stack<E> {
   
//创建一个动态数组成员变量
   
Array<E> array;

   
public ArrayStack() {
       
array = new Array<>();
    }

   
public ArrayStack(int capacity) {
       
array = new Array<>(capacity);
    }

   
@Override
   
public int getSize() {
       
return array.getSize();
    }

   
@Override
   
public void push(E e) {
       
array.addLast(e);
    }

   
@Override
   
public E pop() {
       
return array.removeLast();
    }

   
@Override
   
public E peek() {
       
return array.getLast();
    }
   
   
@Override
   
public boolean isEmpty() {
       
return array.isEmpty();
    }

   
public int getCapacity() {
       
return array.getCapacity();
    }
}

实现后的方法,不会对数据索引的合法性进行判断,因为在动态数组中都已经进行了判断.

 

3-3站的实际应用

括号匹配—编译器:有效的括号.

 

判断字符,如果是左括号,那么压入栈,如果是右括号,对比,如果是左括号,那么就弹出,代码如下:

package com.stack;



class Solution {

    public boolean isValid(String s) {

        Stack<Character> stack = new ArrayStack<>();

        //将传进来的s转化成

        for (int i = 0; i < s.length(); i++) {

            char c = s.charAt(i);

            if (c == '(' || c == '[' || c == '{') {

                stack.push(c);

            } else {

                if (stack.isEmpty())

                    return false;



                Character pop = stack.pop();

                if (pop != '(' && c == ')')

                    return false;

                if (pop != '[' && c == ']')

                    return false;

                if (pop != '{' && c == '}')

                    return false;

            }



        }

        return stack.isEmpty();

    }

}

队列Queue

1.队列也是一种线性结构

2.相比于数组,队列对应的操作是数组的子集

3.只能从一端(队尾)添加元素,只能从一端(队首)取出元素

 

队列是一种先进先出的数据结构(先到先得)

First In First Out(FIFO)

 

队列实现

import com.array.Array;





public class ArrayQueue<E> implements Queue<E> {

    //添加一个数组元素

    private Array<E> array;



    public ArrayQueue() {

        array = new Array<>();

    }



    public ArrayQueue(int capacity) {

        array = new Array<>(capacity);

    }



    @Override

    public int getSize() {

        return array.getSize();

    }



    @Override

    public boolean isEmpty() {

        return array.isEmpty();

    }



    @Override

    public void enqueue(E e) {

        array.addLast(e);

    }





    @Override

    public E dequeue() {

        return array.removeFrist();

    }



    @Override

    public E getFront() {

        return array.getFirst();

    }



    //遍历

    @Override

    public String toString() {

        StringBuilder res = new StringBuilder();

        res.append("ArryQueue ");

        res.append("Front [");

        for (int i = 0; i < array.getSize(); i++) {

            res.append(array.get(i));

            if (i != array.getSize() - 1) {

                res.append(",");

            }

        }

        res.append("] tail");

        return res.toString();

    }

}

 

怎么保证数组队列出栈和入栈的时间复杂度都为O(1)呢.

3-6循环队列该如何实现?

数组队列问题:

         删除队首元素:数组队列是删除头一个元素,然后再将所有的其他元素向前移动一个单位,时间复杂度特别高.

         我们可以不移动其他元素么?

         我们可以记录队首中的位置,定义一个front,指向队首元素.

         Front==tail那么队列为空,添加元素,维护tail,出队维护front.

         Tail+1=front表示队列满了,准确来说是(tail+1)%c==front表示数组满了

可以用钟表来表示,队尾元素怎么利用队首空间,

                 

 

实现循环队列:

         首先要有一个数组,data

         还有front,和tail;

         定义两个size.

在学完循环队列之后,可以去实现一下,不写size,只使用front,和tail实现逻辑.

构造函数,传入容积capacity,因为我们要浪费一个空间,所以变成capacity+1

         1.构造函数中,front,tail,size初始化为0,空参构造LoopQueue

         2.获取容量,数组长度减一

         3.判空即front==tail

         4.获取数据数量getSize();

package com.loopQueue;


public class LoopQueue<E> implements Queue<E> {
   
private E[] data;
   
private int front, tail;
   
private int size;

   
public LoopQueue() {
       
this(10);
    }


   
public LoopQueue(int capacity) {
       
data = (E[]) new Object[capacity + 1];

       
front = 0;
       
tail = 0;
       
size = 0;
    }


   
public int getCapacity() {
        
return data.length - 1;
    }

   
@Override
   
public int getSize() {
       
return size;
    }

   
@Override
   
public boolean isEmpty() {
       
return front == tail;
    }

   
@Override
   
public void enqueue(E e) {
       
//判断队列是否满了.
        //
进队操作,首先判断队列是否满元素,判断条件是尾元素不等于头元素
       
//元素满了,进行扩容
       
//判断条件为,尾指针加一等于首指针
       
if ((tail + 1) % data.length == front) {
            resize(getCapacity() *
2);
        }
       
data[tail] = e;
       
//扩容之后,将元素赋值给尾元素
       
//改变一下尾坐标指针
       
//
       
tail = (tail + 1) % data.length;
       
//长度加一
       
size++;

    }

   
private void resize(int newCapacity) {
       
//创建一个newData
       
E[] newData = (E[]) new Object[newCapacity + 1];
       
//循环队列,将旧数组中的元素复赋值给新元素.
        //
关于i+front,首先,偏移量front=2
        //
那么i=0 i+front=2 2%8=2
        //    i=3 3+2      5%8=5
        //    i=10 10+2    12%8= 4
       
for (int i = 0; i < size; i++) {
            newData[i] =
data[(i + front) % data.length];
        }
       
data = newData;
       
front = 0;
       
tail = size;
    }

   
@Override
   
public E dequeue() {
       
//对于出队操作
       
//先判空
       
if (isEmpty()) {
           
throw new IllegalArgumentException("Cannot dequeue from an empty queue.");
        }
       
E ret = data[front];
       
//然后将元素赋给ret
        //
data赋值为空
       
data[front] = null;
       
//改变头元素的值
       
front = (front + 1) % data.length;
       
//长度减一
       
size--;
       
//缩容,缩容要保证容量不能为0
       
if (size == getCapacity() / 4 && getCapacity() / 2 != 0)
            resize(getCapacity() /
2);

       
return ret;
    }

   
@Override
   
public E getFront() {
       
if (isEmpty()) {
           
throw new IllegalArgumentException("Cannot dequeue from an empty queue.");
        }
       
return data[front];
    }

   
@Override
   
public String toString() {
        StringBuilder res =
new StringBuilder();
        res.append(String.format(
"Queue: size=%d,capacity=%d\n", size, getCapacity()));
        res.append(
"front [");
       
//该方法中,遍历数组,从头开始遍历,front,然后改变i的值,i不等于tail作为结束条件.
       
for (int i = front; i != tail; i = (i + 1) % data.length) {
            res.append(
data[i]);
           
if ((i + 1) % data.length != tail) {
                res.append(
", ");
            }
        }
        res.append(
"] tail");
       
return res.toString();
    }

   
public static void main(String[] args) {
        LoopQueue<Integer> queue =
new LoopQueue<>();
       
for (int i = 0; i < 10; i++) {
            queue.enqueue(i);
            System.
out.println(queue);

           
if (i % 3 == 2) {
                queue.dequeue();
                System.
out.println(queue);
            }

        }
    }
}

 

 

 

 

时间主要浪费在出队上,ArrayQueue是O(n^2)级别的函数

因为电脑和操作系统的影响,会有时间的差别.

建议运行多次,比较平均值.

JVM也会影响测试结果.

队列应用:广度优先遍历.

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值