day 11 第五章 栈与队列

779 阅读1分钟

今日任务:

  • 理论基础
  • 用栈实现队列
  • 用队列实现栈
  • 有效的括号
  • 删除字符串中的所有相邻重复项

今天的题目都比较 easy,简单记录一下

用栈实现队列

栈实现队列的核心是,利用栈的先进后出的特性来实现,队列的先进先出,我们就需要定义两个栈,stackA 和 stackB 然后,如果是添加元素的话栈和队列是一样的,都是从后面进行添加,因此,添加我们直接放到 stackA 当中即可,如果是删除的话,栈是从后面删除,而队列要删除前面的元素,所以我们要借助另一个栈来帮助它删除,从 stackA 中后往前弹出元素,比如说 stackA 里面是 [1, 2, 3] 弹出,之后到 stackB 里面是 [3, 2, 1] 然后我们将 stackB.pop() 弹出的就是 1 也就是之前 stackA 中的队首元素,然后再把 stackB 里面中的元素弹出回到 stackA 当中即可,之后 stackA 就是 [2, 3]

(我没写 peek 方法,但是大致思路有了其它就是一些修改了)

Stack<E> stackA = new ArrayStack<>();
Stack<E> stackB = new ArrayStack<>();

public void offer(E e) {
    stackA.push(e);
}

public E poll() {
    //弹出需要借助stackB来执行
    while (!stackA.isEmpty()) {
        stackB.push(stackA.pop());
    }
    E ret = stackB.pop();
    while (!stackB.isEmpty()) {
        stackA.push(stackB.pop());
    }
    return ret;
}

public void clear() {
    stackA.clear();
}

public void size() {
    stackA.size();
}

public void isEmpty() {
    stackA.isEmpty();
}

用队列实现栈

队列实现栈和栈实现队列是有一些不一样的,这是根据它们自身的特性来的,如果添加就直接在后面进行添加,这个没有争议,但是如果删除的话,队列是要删除前面的元素,但是栈希望实现的是删除后面的元素,因此也要借助另一个队列来实现删除操作 queueA [1, 2, 3] 删除后面的元素 3 我们将 queueB 进行弹出,从前往后,如果全部弹出的话 queueB 就是 [1, 2, 3] 还是无法删除 3 因此我们就将 queueA 弹出 size - 1 个元素,剩下的 3 作为队首元素,删除,如果添加在 queueB 中直接添加,如果删除将 queueB 中的元素移动到 queueA 中,也是只保留一个元素即可,所以我们可以看到栈实现队列和队列实现栈的一处不同在于,栈实现队列每次操作都需要移动元素从 stackA 到 stackB,但是队列实现栈的话就是两个队列随机,一个慢一个空这样,如果删除元素的话才移动,并不固定,添加元素比较 random

Queue<E> queueA = new ArrayQueue<>();
Queue<E> queueB = new ArrayQueue<>();

public void push(E element) {
    if (!queueA.isEmpty()) {
        queueA.offer(element);
    } else if (!queueB.isEmpty()) {
        queueB.offer(element);
    } else {//如果都为空就添加到queueA里面
        queueA.offer(element);
    }
}

public E pop() {
    if (size() == 0) {
        throw new IllegalArgumentException("queue is empty");
    }
    if (!queueA.isEmpty()) {
        while (queueA.size() != 1) {
            queueB.offer(queueA.poll());
        }
        return queueA.poll();
    } else {
        while (queueB.size() != 1) {
            queueA.offer(queueB.poll());
        }
        return queueB.poll();
    }
    //然后删除
}

public int size() {
    return queueA.size() + queueB.size();
}

public boolean isEmpty() {
    return queueA.isEmpty() && queueB.isEmpty();
}

public void clear() {
    queueA.clear();
    queueB.clear();
}

有效括号

有效括号是栈的一个应用,这类题目,如果是用其它解法会很复杂,我也想不到,但是这种括号匹配问题用栈来做最为方便,思路也比较简单,这里我们借助了一个 hashMap,遍历提供的字符串,如果不是 ) } ] 这种的话就直接添加到栈中,如果是的话,在判断栈不为空的情况下(如果不判断的话,弹出栈顶元素可能会导致空指针),我们就需要弹出栈顶元素,如果比如我们弹出 ( 如果和 hashMap.get(')') 的值 ( 不相等的话,就说明他这个不是闭合的,直接返回 false 即可,当然我们给的这个肯定是闭合的,我是说,如果 字符串遍历到比如说 } 然后,我们弹出的栈顶元素是这个 ( 那么 (} 肯定是不闭合的,因此就返回 false,当然如果给的字符串只有一个元素,比如说 ) 这时候它也不会进入到 stack 中,如果不做空判断的话,直接 stack.pop() 就报空指针异常了!

public boolean isValid(String s) {
    //定义一个栈,和一个 HashMap
    //栈中存放 ( [ { 而 HashMap 当中存放 )( ][ }{ 键值对
    Map<Character, Character> hashMap = new HashMap();
    hashMap.put(')', '(');
    hashMap.put(']', '[');
    hashMap.put('}', '{');
    Stack<Character> stack = new Stack();
    for (int i = 0; i < s.length(); i++) {
        char c = s.charAt(i);
        if (hashMap.containsKey(c)) {
            if (!stack.isEmpty()) {
                Character ch = stack.pop();
                if (!ch.equals(hashMap.get(c))) {
                    return false; 
                } 
            } else  {
                return false;
            }
        } else {
            stack.push(c);
        }
    }
    if (stack.isEmpty()) {
        return true;
    } else {
        return false;
    }
}

删除字符串中的所有相邻重复项

这个和上面的类似,注意 !stack.isEmpty() 判断即可!

public String removeDuplicates(String s) {
    Stack<Character> stack = new Stack();
    for (int i = 0; i < s.length(); i++) {
        if (!stack.isEmpty() && stack.peek().equals(s.charAt(i))) {
            stack.pop();
        } else {
            stack.push(s.charAt(i));
        }
    }
    StringBuilder sb = new StringBuilder();
    while(!stack.isEmpty()) {
        sb.append(stack.pop());
    }
    return sb.reverse().toString();
}

总结:

栈的应用可以很方便的解决这类删除相邻重复项,括号匹配问题,如果遇到这类问题,想一想可不可以利用栈这种数据结构的特性来解决,栈转队列和队列转栈比较考察对它们两个数据结构特性的掌握。