LeetCode刷题总结(1)--栈、队列、堆

队列:先进先出

栈:先进后出

堆(优先队列 ): 逻辑结构上是完全二叉树结构,其中每个字数的最大值(最小值)节点是头节点。实际结构常用数组实现。 建立一个大根堆 时间复杂度O(n)


基础题

1.数组实现栈、队列 ; 实现堆排序

class ArrayStack{
    int maxSize;
    int top;
    int []stack;

    public ArrayStack(int maxSize) {
        this.maxSize = maxSize;
        top=-1;
        stack=new int[maxSize];
    }
    //栈空
    public boolean isEmpty(){
        return top==-1;
    }
    //栈满
    public boolean isFull(){
        return top==maxSize-1;
    }
    //入栈
    public void push(int i){
        if(isFull()) {
            System.out.println("栈已满");
            return;
        }
        stack[++top]=i;
    }
    //出栈
    public int pop(){
        if(isEmpty()){
           throw new RuntimeException("栈空");
        }
        int val=stack[top];
        top--;
        return val;
    }
    //显示栈顶元素,不出栈
    public int showPop(){
        if(isEmpty()){
            throw new RuntimeException("栈空");
        }
        return stack[top];
    }
    //遍历
    public void show(){
        if(isEmpty()){
            System.out.println("栈空");
            return;
        }
        for (int i = 0; i <=top; i++) {
            System.out.printf("stack[%d]=%d   \n",i,stack[i]);
        }

    }

  • 队列
class ArrayCircleQueue {
    private int maxSize;
    private int front; //指向队头
    private int rear; //指向队尾的后一个位置,且rear后预留一个位置,方便判断队空
    private int []circleArr;

    public ArrayCircleQueue(int maxSize) {
        this.maxSize = maxSize;    //因为预留一个位置,实际上只有maxSize-1个数有效
        front=0;
        rear=0;
        circleArr=new int[maxSize];
    }

    public int getMaxSize() {
        return maxSize;
    }

    public int getFront() {
        return front;
    }

    public int getRear() {
        return rear;
    }

    //判断队空
    public boolean isEmpty(){
        return rear==front;
    }
    //判断队满
    public boolean isFull(){
        return (rear+1)%maxSize==front;
    }
    //入队
    public void enqueue(int i){
        if(isFull()){
            System.out.println("队列已满,不能添加数据");
            return;
        }
        circleArr[rear]=i;
        rear=(rear+1)%maxSize;
    }
    //出队
    public int dequeue(){
        if(isEmpty()){
            return -1;
        }
        int temp=front;
        front=(front+1)%maxSize;
        return circleArr[temp];
    }
}

//给定数组  建立大根堆
public static void main(String[] args){
   int[] a={.......};
   int len=a.length;
    //建立大根堆
   for(int i=len/2-1 ; i>=0; i--){
       heapSort(a,i,len);
   }
   for(int i=len-1;i>0;i--){
       int temp=a[i];
       a[i]=a[0];
       a[0]=a[i];
       heapSort(a,0,i);
   }
}
public int[] heapSort(int[] a,int i, int len){
    int temp=a[i];
    for(int j=2*i+1 ; jtemp){
            a[i]=a[j];
            i=j;
        }else break;
    }
    a[i]=temp;
}

2.用栈实现队列 ; 用队列实现栈

//栈实现队列
class MyQueue {

Stack res = new Stack<>();
public MyQueue() {}
   
public void push(int x) {
    Stack temp = new Stack<>();
    while(!res.isEmpty()) temp.push(res.pop());
    temp.push(x);
    while(!temp.isEmpty()) res.push(temp.pop());
}

public int pop() {
    return res.pop();
}

public int peek() {
    return res.peek();
}

public boolean empty() {
    return res.isEmpty();
}

//队列实现栈
同样准备两个队列
class MyStack {
    Queue res = new LinkedList<>();
    }   
   
    public void push(int x) {
        Queue temp=new LinkedList<>();
        temp.add(x);
        while(res.peek()!=null) temp.add(res.poll());
        while(temp.peek()!=null) res.add(temp.poll());
    }
    
    
    public int pop() {
        return res.poll();
    }
    
    public int top() {
        return res.peek();
    }
    
    public boolean empty() {
        return res.peek()==null;
    }
}

3.实现一个特殊的栈,在实现栈的基本功能的基础上,再实现返回栈中最小元素的操作

//另外定义一个最小栈  min_stack
class GetMinStack{
    public GetMinStack{
        
    }
    public void push(int x){
        if(min_stack.isEmpty() || x

4.验证栈序列

输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如,序列 {1,2,3,4,5} 是某栈的压栈序列,序列 {4,5,3,2,1} 是该压栈序列对应的一个弹出序列,但 {4,3,5,1,2} 就不可能是该压栈序列的弹出序列。 ==栈中元素不重复==

public boolean validateStackSequences(int[] pushed, int[] popped) {
    Stack stack = new Stack<>();
    int r = 0;
    for(int i = 0 ; i < pushed.length ; i++){
        stack.push(pushed[i]);
        while(!stack.isEmpty() && stack.peek() == popped[r]){
              stack.pop();
              r++;
          }    
     } 
    return stack.isEmpty();
}

5.有一个整数数组,找出数组中第K大的数。
要找第K大的数,需要维持一个大小为K的小根堆 , 遍历结束结果就是小根堆的堆顶。
对于为什么是小根堆 ,如果遍历的数比堆顶小 ,那肯定其他数都小 ,如果比堆顶大,就替换堆顶,重新维持小根堆

public class Finder {
    public int findKth(int[] a, int n, int K) {
        // write code here
        int[] heap = new int[K];
        for(int i = 0 ; i < K ; i++) heap[i] = a[i];
        for(int i = K/2 - 1 ; i >= 0 ; i--) heapSort(heap,i);
        for(int i = K ; i < a.length ; i++){
            if(a[i] > heap[0]) heap[0] = a[i];
            heapSort(heap,0);
        }
        return heap[0];
    }
    public void heapSort(int[] heap , int i){
        int temp = heap[i];
        for(int j = 2*i + 1 ; j < heap.length ; j = 2*j + 1){
            if(j+1 < heap.length && heap[j] > heap[j+1]) j++;
            if(heap[j] < temp){
                heap[i] = heap[j];
                i = j;
            }else break;
        }
        heap[i] = temp;
    }
}


进阶

1.一个栈一次压入1,2,3,4,5, 将这个栈中元素逆序,即输出1、2、3、4、5,仅用递归实现,不用其他数据结构
! 递归的本质就是栈

public int getStackBottom(Stack stack){
    int res=stack.pop();
    if(stack.isEmpty()) return res;
    int last=getStackBottom(stack);
    stack.push(res);
    return last;
}
public void reverse(Stack stack){
    if(stack.isEmpty()) return;
    int bot=getStackBottom(stack);
    reverse(stack);
    stack.push(bot);
}

2.更新滑动窗口 最大值 最小值问题

! 双端队列 队列存放数组元素的下标

以更新最大值为例

1.对于从右侧窗口新进入的数a ,不断从双端队列的队尾释放小于等于 a 的数,直到遇到一个大于a的数或者队列为空时 , 把 a 从双端队列的队尾放入


2.对于左侧窗口的数字b ,判断下标是否属于滑动窗口内 , 如果不是则弹出


3.这样就保证队头是该滑动窗口的最大值

给定一个数组nums ,有一个大小为k的滑动窗口从数组的最左侧移动到数组的最右侧。返回滑动窗口最大值

    public int[] maxSlidingWindow(int[] nums, int k) {
        LinkedList list = new LinkedList<>();
        int[] res = new int[nums.length - k + 1];
        int j = 0;
        for(int i = 0 ; i < nums.length ; i++){
            while(!list.isEmpty() && nums[list.getLast()] < nums[i]){
                list.removeLast();
            }
            list.addLast(i);
            if(list.getFirst() == i-k) list.removeFirst();
            if(i >= k-1) res[j++] = nums[list.getFirst()];
        }
        return res;
    }

3.单调栈

单调栈可以用来找 一个数组中 左边离他最近比他大(小)的数 和 右边离他最近比他大(小)的数

如果找 大数 就是单调递减栈 遍历数组 遇到比栈顶小的元素 直接入栈, 遇到比栈顶大的元素,栈顶出栈

新栈顶就是左边离他最近比他大的数 ,要 push的数就是右边离他最近比他大的数

3.a给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。求在该柱状图中,能够勾勒出来的矩形的最大面积

LeetCode84.png
// 对于每个柱子   往两边扩散 直到遇到比它小的柱子  就求出以这个柱子为中心的矩形面积
// 以某个柱子 i 为中心 ,也就是生成的矩形高为 heights[i]
// 利用单调递增栈 求出 每个柱子 左边最近比它小的柱子  和 右边 最近比它小的柱子
// 每次某个元素出栈的时候 ,要入栈的元素就是它右边最近比它小的元素 , 出栈后的栈顶(新元素还没入栈)就是它左边最近比它小的元素 ,如果出栈后栈为空, 那么就是说这个元素左边没有比它小的元素,可以定义左边比它小的元素下标为-1
// 对于 相等的元素,一样进行出栈 ,因为即使前一个相等的元素提前出栈,以它为中心生成的最大矩形 一定 被后一个相等元素生成的矩形囊括
public int getMaxRect(int[] heights){
    Stack stack = new Stack stack;  //栈中存放的是柱子的下标
    int maxSize=0;
    for(int i=0;i

3.b 进阶 最大矩形

LeetCode85.png

思路

  1. 遍历每一行, 以每一行中的几个元素为底 , 求出每一行的最大矩形
  2. 对于每一行,都可以看成一个柱状图,如上图中的第三行 对应的柱状图 就是 [3,1,3,2,2] 这个数组生成的柱状图
  3. 每一行的柱状图 利用3.a求出最大矩形
public int maximalRectangle(char[][] matrix) {
    int m = matrix.length;
    if(m == 0) return 0;
    int n = matrix[0].length;
    int[] heights = new int[n];
    int maxArea = 0;

    for(int i = 0 ; i < m ; i++ ){
        for(int j = 0 ; j < n ; j++){
            if(matrix[i][j] == '0') heights[j] = 0;   
            else heights[j] += 1;
        }
        maxArea = Math.max(maxArea , getMaxArea(heights) );
    }
    return maxArea;
}
public int getMaxArea(int[] heights){
   //参考上题
}

  1. 给定一个非空的整数数组,返回其中出现频率前 k 高的元素

输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]

结合HashMap 和 堆 实现

class Solution {
    //记录数组中元素及其对应出现的次数
    Map map = new HashMap<>();
    
    public int[] topKFrequent(int[] nums, int k) {    
        int[] heap = new int[k];
        for(int i : nums){
            map.put(i , map.getOrDefault(i,0) + 1);
        }
        Iterator it = map.keySet().iterator();
        int i = 0;
        while(it.hasNext()){
            // 先初始化一个大小为k的堆,当堆中元素个数为k时,建立小根堆
            if( i < k){
                heap[i] = (Integer)it.next();
                i++;
                if(i == k){
                    for(int j = k/2 -1 ; j >= 0 ; j--){
                        heapSort(heap,j);
                    }
                }
            }else{   //小根堆建好后 ,对于每个新遍历的元素与堆顶比较,如果比堆顶大,替换
                     //堆顶,重新维持小根堆
                int key = (Integer)it.next(); 
                if(map.get(key) > map.get(heap[0])) heap[0] = key;
                heapSort(heap , 0);
            }
            
        }
        return heap;
    }
    
    //维持小根堆
    //注意,堆中元素比较是比较它们出现的次数,即该元素在map中对应的value
    public void heapSort(int[] heap , int i){
        int temp = heap[i];
        for(int j = 2*i + 1 ; j < heap.length ; j = 2*j + 1){
            if(j+1 < heap.length && map.get(heap[j+1]) < map.get(heap[j])) j++;
            if(map.get(heap[j]) < map.get(temp)){
                heap[i] = heap[j];
                i = j;
            }else break;
        }
        heap[i] = temp;
    }
}

你可能感兴趣的:(LeetCode刷题总结(1)--栈、队列、堆)