算法-栈 队列

7 篇文章 1 订阅

目录

一、栈和队列概念

二、链表实现栈和队列

1、思路

2、链表实现进出

三、数组实现栈和队列

1、数组实现栈

2、数组实现队列

四、实现一个栈方法,返回最小元素

1、思路

2、实现

五、栈和队列的相互实现

1、如何用栈实现队列

2、如何用队列实现栈


一、栈和队列概念

和链表这种“能看见”的结构不同,栈和队列属于逻辑概念。

栈:犹如弹夹,先进后出;

队列:犹如排队,先进先出。

二、链表实现栈和队列

无论是实现栈还是队列,因为它们是逻辑概念,实现它们无非也是逻辑上如何实现,也就是它们的进出顺序,所以用链表实现进出就可以了。

1、思路

给用户提供一个我们自己封装的栈,用户拿到这个栈实例化对象a,用户对a这个对象进行加数据,弹数据操作。

||

我们内部如何对a进行操作呢?给a连个指针,一个记录head,一个记录tail,加数据、弹数据都是对这两个指针的操作。

||

我们可不可以只提供一个指针呢?毕竟通过一个指针就可以看到这个数据的全貌,当然可以,只不过,比如你只记录了head指针,那么你从尾部加数据时,要不断循环到尾部,定位到尾部之后,再再尾部加数据。这样不如直接用两个指针。那么用三个指针可以吗?可以,当三个指针干嘛用?是没有必要的。

||

想明白要实现什么效果:

用户调用我们的栈类.add() -- >从尾部加数据-->调用另一个类的addFromBottom();

调用栈类.pop() --> 另一个类的popFromBottom();

调用队列.pop() --> 另一个类的popFromHead();

即栈加数据是从尾部加,弹出数据从尾部弹;

队列加数据从尾部加,弹出数据从头部弹。

2、链表实现进出

(1)首先建一个双向链表,我们不像上一章一样是int型的双向链表,我们提供泛型

public class DoubleNode<T>{
        public T value;
        public DoubleNode next;
        public DoubleNode last;
        public DoubleNode(T value){
            this.value = value;
        }
    }

(2)建一个类,这个类内部专门提供addFromBottom() 、popFromBottom() 、popFromHead()方法:

 public static class AddAndPop<T>{
        DoubleNode head;
        DoubleNode tail;
        //从尾部加数据
        //1。旧tail的next指向新加的数据;2.新数据的last指向旧tail;3.cur做新tail
        public void addFromBottom(T value){
            DoubleNode<T> cur = new DoubleNode<>(value); //先把在这个value封装成node类型,这样就有了指针
            if(tail == null){
                head = cur;
                tail = cur;
            }else{
                tail.next = cur;
                cur.last = tail;
                tail = cur;
            }
        }
        //从尾部弹数据
        public T popFromBottom(){
            if(tail == null){
                return null;
            }
            DoubleNode<T> cur = tail; //这里是把当前tail的内存给cur
            if(head == tail){
                head = null;
                tail = null;
            }else{
                tail = tail.last;
                tail.next = null;
                cur.last = null;
            }
            return cur.value;
        }

        //从头部弹数据
        public T popFromHead(){
            if(head == null){
                return null;
            }
            DoubleNode<T> cur = head; //先把旧head内存做一个备份
            if(head == tail){
                head = null;
                tail = null;
            }else{
                head = head.next;
                head.last = null;
                cur.next = null;
            }
            return cur.value;
        }
        //判断是否为空
        public boolean isEmpty(){
            return head == null;
        }
    }

3、链表实现栈和队列

有了进出,那么顺序组合一下就可以实现自己的栈和队列:

实现栈:

public class MyStack<T>{
        private AddAndPop<T> addAndPop;
        //栈加数据,是从尾部加
        public void add(T value){
            addAndPop.addFromBottom(value);
        }
        //栈弹数据,是从尾部弹
        public T delete(){
            return addAndPop.popFromBottom();
        }
        //判断栈是否为空
        public boolean isEmpty(){
            return addAndPop.isEmpty();
        }
    }

实现队列:

public class MyQueue<T>{
        private AddAndPop<T> addAndPop;
        //队列加数据,是从尾部加
        public void add(T value){
            addAndPop.addFromBottom(value);
        }
        //队列弹数据,是从头部弹
        public T delete(){
            return addAndPop.popFromHead();
        }
        //判断队列是否为空
        public boolean isEmpty(){
            return addAndPop.isEmpty();
        }
    }

三、数组实现栈和队列

1、数组实现栈

数组实现栈是比较简单的,因为栈的出入都是在尾部,那么我们定位一个指针,指向尾部后一位,这样加数据时,加到此指针位置,此指针++,减数据时,此指针--,定位到尾部,再弹出数据。

 //数组实现栈
    public class ArrayForStack{
        private Object[] myArray;
        private int limit;
        private int index;
        public ArrayForStack(int limit){
            this.limit = limit;
            myArray = new Object[this.limit];
        }
        //加数据
        public void add(Object value){
            if(index <= limit-1){
                myArray[index] = value;
                index++;
            }
        }
        //弹数据
        public Object delete(){
            Object cur = null;
            if(index != 0){
                index --;
                cur = myArray[index];
                myArray[index] = null;
            }
            return cur;
        }
    }

2、数组实现队列

队列进是从尾部进,出是从头部出,所以要准备两个指针,一个指向头部,进数据,一个指向尾部,出数据。

有数据的部分是两个指针之间的,无数据的部分是两个指针之外。我们可以用一个size表示有数据部分大小

 public class ArrayForQueue{
        private Object[] queue;
        private int pullIndex;
        private int pushIndex;
        private int size;
        private int limit;
        public ArrayForQueue(int limit){
            this.limit = limit;
            queue = new Object[limit];
            pullIndex = 0;
            pushIndex = 0;
            size = 0;
        }
        //添加数据,size增加,pullindex移动,判断size是否为limit,是的话不能再加
        public void add(Object value){
            if(size!=limit){
                queue[pullIndex] = value;
                pullIndex = pullIndex++ == limit ? 0:pullIndex++ ; //pullIndex移动到下个“空格”
                size++;
            }else{
                throw new RuntimeException("栈满了,不能再加");
            }
        }
        //减数据,size减,pushindex移动,判断size是否为0,是则不能取数据
        public Object delete(){
            Object cur = null;
            if(size!=0){
                cur = queue[pushIndex];
                queue[pushIndex]=null;  //这里其实无所谓写,因为再加数据,加到这个位置会覆盖原来的数据,原来的数据存在也好,不存在(null)也好,都不影响加新数据
                pushIndex = pushIndex++ == limit? 0: pushIndex++;
                size--;
            }else{
                throw new RuntimeException("栈空了,不能再取");
            }
            return cur;
        }
    }

四、实现一个栈方法,返回最小元素

要清楚,栈是逻辑概念,它只是因为进出的顺序让它被称为栈。我们要利用它的逻辑意义---即进出取得它的最小值。

1、思路

既然只能通过进出获取最小值,那么我们希望当我要最小值时,这个栈就给我弹一个值,这个值恰好是最小值。

||

那么就要在进数据的时候动心思,进数据是不区分大小的。我们可以自己设计加一个栈,专门记录大小,这个栈栈顶永远是最小值。

||

进数据的时候,最小栈判断是不是比上一个值小,小的话加新进的数据,大的话加上一个数据。

||

出数据的时候,最小栈直接弹栈顶就可以了。

2、实现

我们利用系统栈,不用自己实现的栈:

public class MyStack2{
        private Stack<Integer> data;
        private Stack<Integer> min;

        public MyStack2(){
            data = new Stack<Integer>();
            min = new Stack<Integer>();
        }
        public void add(Integer i){
            if(min.isEmpty()){
                min.push(i);
            }else if(i < getMin()){
                min.push(i);
            }else{
                min.push(getMin());
            }
            data.add(i);
        }
        public Integer pop(){
            if(data.isEmpty()){
                throw new RuntimeException("栈空,无法弹出");
            }
            min.pop();
            return data.pop();
        }

        public Integer getMin(){
            if(min.isEmpty()){
                throw new RuntimeException("栈空,无最小值");
            }
            return min.peek();//返回栈顶的元素,但不弹出该栈顶元素。
        }
    }

五、栈和队列的相互实现

1、如何用栈实现队列

进入的时候进入栈,顺序12345,出的时候也是从栈出,54321,但是给用户展现的是12345

需要明确,进数据的时候用push栈,出的时候用先把push栈的数据弹出,进入pop栈,再从pop栈出数据。

但是为了防止:pop栈还有数据--push栈再进数据--需要出数据时--push栈倒数据给pop栈 -- pop栈弹数据--弹出最新数据  这种情况发生,所以应保证pop栈数据全部弹光,push栈才可以给pop栈新加数据

public class StackForQueue<T>{
        private Stack<T> push;
        private Stack<T> pop;

        public void add(T value){
            push.add(value);
        }

        public T pop(){
            //push栈出数据,pop栈加数据,而且1要加光,push栈不能留有数据;2pop栈要弹光,push栈才能加
            if(pop.isEmpty()){
                while(!push.isEmpty()){
                    pop.add(push.pop());
                }
            }
            if(pop.isEmpty()){
                throw new RuntimeException("栈空了,无法弹出数据");
            }
            return pop.pop();
        }
    }

2、如何用队列实现栈

比如,进数据是进入队列,依次进12345,出的时候内部是从队列出,顺序是12345,但是给用户展现的是54321,此时我们可以用两个队列实现倒序出

用系统提供的数组队列,data是操作的队列,无论加数据还是弹数据都用它,help是辅助队列,承担倒数据的作用

public class QueueForStack<T>{
        private ArrayBlockingQueue<T> data;
        private ArrayBlockingQueue<T> help;
        private int limit;

        public QueueForStack(int limit){
            data = new ArrayBlockingQueue<T>(limit);
            help = new ArrayBlockingQueue<T>(limit);
            this.limit = limit;
        }

        public void add(T value){
            if(data.size() < limit){
                data.add(value);
            }
        }

        /*
        * 1.data开始搬数据,只留下一个元素
        * 2.这个元素做为返回值弹出
        * 3.data help互换
        * */
        public T pop(){
            //data出数据,help加数据
            while(help.size()<limit && data.size()>1){
                help.add(data.poll());
            }
            T value = data.poll();
            //data help互换
            ArrayBlockingQueue<T> tmp;
            tmp = data;
            data = help;
            help = tmp;
            return value;
        }
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值