栈和队列

一.栈

1.栈(stack)又名堆栈,仅允许在表的一端(栈顶)进行插入和删除。向一个栈插入新元素称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。由于栈的这种特性,所以栈内元素是先进后出,即先进栈的元素,后出栈。

2.栈的操作。
1)Push(入栈)
a.向栈中插入一个元素,插入前判断栈是否已满,满了则抛异常。
b.如果栈未满,栈顶指针top先+1,然后再插入元素,否则将覆盖之前的栈顶。
2)Pop(出栈)
a.移除栈顶元素,移除前先判断栈是否为空,空了则抛异常。
b.移除栈顶指针top所指元素,再top-1,它与入栈操作刚好相反。
3)Peek(查看)
a.只能查看栈顶元素,查看前先判断栈是否为空,空了给相应提示信息。

3.栈的实现(数组)

public class MyStack {
    //栈的最多容量
    private int maxSize;
    //栈顶指针
    private int top;
    //栈
    private char[] stackArray;

    public MyStack(int size) {
        maxSize = size;
        stackArray = new char[size];
        top = -1;
    }

    public void push(char value) {
        //栈在不满的情况下才能插入
        if(!this.isFull()) {
        //先++,再赋值
        stackArray[++top] = value;
        }
    }

    public char pop() {
        //栈在不空的情况下才能移除,否则返回空
        if(!this.isEmpty()) {
        //先清除栈顶元素,再--
        return stackArray[top--];
        }
        return 0;
    }

    public char peek() {
        //栈在不空的情况下才能查询栈顶元素,否则返回空
        if(!this.isEmpty()) {
        return stackArray[top];
        }
        return 0;
    }

    public boolean isEmpty() {
        return (top == -1);
    }

    public boolean isFull() {
        return (top == maxSize -1);
    }

    @Override
    public String toString() {
        return "MyStack [maxSize=" + maxSize + ", top=" + top + ", stackArray=" + Arrays.toString(stackArray) + "]";
    }

}

4.栈的实例1——单词逆序
由于栈先进后出的特性,单词逆序正是一个很应景的实例。

public class TestReverse {
    @org.junit.Test
    public void testMyStack() {
        String target = "你好,我叫黄国攀";

        MyStack stack = new MyStack(target.length());
        for (int i = 0; i < target.length(); i++) {
            stack.push(target.charAt(i));
        }
        System.out.println(target);
        for (int i = 0; i < target.length(); i++) {
            System.out.print(stack.pop());
        }
    }
}

结果:

5.栈的实例2——匹配括号
从字符串中不断读取字符,一旦匹配到左括号如{ [(,就将该字符压入栈中,当读到右括号)] },则弹出栈顶括号,判断括号是否匹配,不匹配则报错。一直没被匹配的括号也会报错。

public class TestMatch {

    @Test
    public void main() {
        String target = "ab{[]}]";
        System.out.println("Target string: " + target);
        MyStack stack = new MyStack(target.length());
        for (int i = 0; i < target.length(); i++) {
            char ch = target.charAt(i);
            switch (ch) {
            case '{':
            case '[':
            case '(':
            stack.push(ch);
            break;
            case '}':
            case ']':
            case ')':
            char chx = stack.pop();
            if ((chx == '{' && ch == '}') ||
                (chx == '[' && ch == ']') ||
                (chx == '(' && ch == ')')) {
                System.out.println(chx + "and" + ch +  " Match success!");
            } else {
                System.out.println("Error:" + ch + " don't match " + chx + " location: " + i);
            }
            break;
            default:
                break;
            }
        }
    }

}

结果:

二.队列

1.队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,进行插入操作的端称为队尾,进行删除操作的端称为队头。顾名思义,队列中的元素先进先出,就像排队一样。

2.队列的操作。
1)Insert(插入)
a.向队尾中插入元素,插入前判断队列是否已满,满了报异常 。
b.向队尾插入新元素,队尾指针rear+1,再插入元素。
2)Remove(移除)
a.移除队头元素,移除前判断队列是否为空,空了报异常 。
b. 移除队头元素,同时队头指针front+1。
注意:由于生活中队列都是往前移,所以这里不够直观,因为如果保持头指针不动的话,就需求移动后面的元素,这样效率太差,所以这里头指针向上移,后面的元素就可以保持原来位置。
3)Peek(查看)
a.查看队头元素,查看前判断队列是否为空,空了返回提示信息 。
循环队列
按照队列的操作方式,很快队列就满了,rear到达最大位置,但是可能之前有移除元素,队列前已经空出来了,我们仍然无法插入元素,所以这时候需要循环队列,环绕式处理,这就需要把rear指针绕回队列的起始位置。

3.循环队列的实现

public class MyQueue {
    private int maxSize;
    //头指针
    private int front;
    //尾指针
    private int rear;
    //队列中的元素个数
    private int nItems;
    private long[] queueArray;

    public MyQueue(int size) {
        this.maxSize = size;
        this.front = 0;
        this.rear = -1;
        this.nItems = 0;
        queueArray = new long[size];
    }

    public void insert(long value) {
        //队列不满才能添加元素
        if(!this.isFull()) {
            //为了实现循环队列
            if(rear == maxSize-1) {
                rear = -1;
            }
            queueArray[++rear] = value;
            nItems++;
        }
    }

    public long remove() {
        //取出当前元素并且上移元素坐标
        long temp = queueArray[front++];
        if(!this.isEmpty()) {
            //为实现循环队列
            if(front == maxSize) {
                front = 0;
            }
            nItems--;
            return temp;
        }
        return 0;
    }

    public long peek() {
        if(!this.isEmpty()) {
        return queueArray[front];
        }
        return 0;
    }

    public boolean isEmpty() {
        return (nItems == 0);
    }

    public boolean isFull() {
        return (nItems == maxSize);
    }

    @Override
    public String toString() {
        return "MyQueue [maxSize=" + maxSize + ", front=" + front + ", rear=" + rear + ", nItems=" + nItems
                + ", queueArray=" + Arrays.toString(queueArray) + "]";
    }

}

测试类:

public class TestQueue {

    @Test
    public void test() {
        MyQueue queue = new MyQueue(5);
        queue.insert(1);
        queue.insert(2);
        queue.insert(3);
        queue.insert(4);
        queue.insert(5);
        System.out.println(queue.toString());
        //移除2次队头元素
        queue.remove();
        queue.remove();
        //查看队头元素
        System.out.println(queue.peek());
        System.out.println(queue.toString());
    }
}

结果:

4.优先级队列。
基本属性和基本操作和普通一样,不过在优先级队列中,元素按 关键字的值有序, 这种关键字最小或最大的元素总在队头。
元素再插入的时候会按照顺序插入到合适的位置,以保证队列有序。
1)实现:
采用插入排序实现队列中元素的排序,详情可以查看我之前的博客请点击这里

public class MyPriorityQueue {
    private int maxSize;
    private long[] priorityQueue;
    private int numItems;

    public MyPriorityQueue(int size) {
        this.maxSize = size;
        priorityQueue = new long[size];
        numItems = 0;
    }

    public void insert(long value) {
        if(!this.isFull()) {
            insertByDesc(value);
        }
    }

    //按降序插入,队头总是最大元素
    public void insertByDesc(long value) {
        long temp = 0;
        int index = 0;
        //如果没有元素,直接插入
        if(numItems == 0) {
            priorityQueue[numItems++] = value;
        } else {、
            //这里采用插入排序,是因为之前的队列已基本有序
            for (int i = numItems; i <= numItems; i++) {
                index = i;
                while (index > 0 && value <= priorityQueue[index-1]) {
                    priorityQueue[index] = priorityQueue[index-1];
                    index--;
                }
                priorityQueue[index] = value;
            }
            numItems++;
        }
    }

    public long remove() {
        if(!this.isEmpty()) {
            return priorityQueue[--numItems];
        }
        return 0;
    }

    public long peek() {
        if(!this.isEmpty()) {
            return priorityQueue[numItems-1];
        }
        return maxSize;

    }
    public boolean isFull() {
        return (numItems == maxSize);
    }

    public boolean isEmpty() {
        return (numItems == 0);
    }

    @Override
    public String toString() {
        return "MyPriorityQueue [maxSize=" + maxSize + ", priorityQueue=" + Arrays.toString(priorityQueue)
                + ", numItems=" + numItems + "]";
    }
}

测试类:

public class TestPriorityQueue {

    @Test
    public void test() {
        MyPriorityQueue priorityQueue = new MyPriorityQueue(8);
        priorityQueue.insert(2);
        priorityQueue.insert(4);
        priorityQueue.insert(7);
        priorityQueue.insert(6);
        priorityQueue.insert(1);
        priorityQueue.insert(3);
        priorityQueue.insert(5);
        System.out.println(priorityQueue.peek());
        System.out.println(priorityQueue.toString());
        //移除2次
        priorityQueue.remove();
        priorityQueue.remove();
        System.out.println(priorityQueue.peek());
        System.out.println(priorityQueue.toString());
    }
}

结果:

总结:栈和队列的学习暂告一段落,现在是基于数组的实现,以后会增加使用其他数据结构实现的实例。虽然现在可能用不到这些知识,是因为前人已经帮我们封装好了,我们不需要重复造轮子,但是以后的路还长着呢,说不定我们需要改善某个工具,或者在前人基础上DIY,所以这些基础知识也是必不可少,本着学习的态度,我们不说知其然而知其所以然,至少也应该对这些基础知识有一定的认识。

参考文献:Java数据结构和算法.(第二版)

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值