剑指offer刷题宝典--第2节

三、队列 & 栈

JZ31 栈的压入、弹出序列

考虑借用一个辅助栈 stack,模拟 压入 / 弹出操作的排列。根据是否模拟成功,即可得到结果。

入栈操作: 按照压栈序列的顺序执行。
出栈操作: 每次入栈后,循环判断 “栈顶元素 == 弹出序列的当前元素” 是否成立,将符合弹出序列顺序的栈顶元素全部弹出。

import java.util.*;
public class Solution {
    public boolean IsPopOrder(int [] pushA, int [] popA) {
        Stack<Integer> stack = new Stack<>();
        int i = 0;
        for (int num : pushA) {
            stack.push(num);
            while (!stack.isEmpty() && popA[i] == stack.peek()) {
                stack.pop();
                i++;
            }
        }
        return stack.isEmpty();
    }
}

JZ59 滑动窗口的最大值 【难!!】

思路:双端队列

队列中的元素按照从大到小排序,如果新增加的元素值>队尾元素,则依次删除,直到保证队列元素的从大到小顺序。

如果队列中元素个数超过size,则从队首删除;

如果元素个数达到size,则将队列中队首元素作为最大值加到res中

import java.util.*;
public class Solution {
    public ArrayList<Integer> maxInWindows(int [] num, int size) {
        ArrayList<Integer> res = new ArrayList<>();
        Deque<Integer> deque = new LinkedList<>();
        for (int i = 0; i < num.length; i++) {
            while (!deque.isEmpty() && num[deque.peekLast()] < num[i]) {
                deque.pollLast();
            }
            deque.addLast(i);
            // 计算窗口左侧边界
            // 当队首元素的下标小于滑动窗口左侧边界left时
            // 表示队首元素已经不再滑动窗口内,因此将其从队首移除
            if (i - deque.peekFirst() >= size)
                deque.pollFirst();
            if (i + 1 >= size)
                res.add(num[deque.peekFirst()]);
        }
        return res;
    }
}

面试题59 - II. 队列的最大值

面试题59 - II. 队列的最大值

Deque存储最大值,Queue存储数据

class MaxQueue {
    Queue<Integer> queue;
    Deque<Integer> deque;
    public MaxQueue() {
        queue = new LinkedList<>();
        deque = new LinkedList<>();
    }

    public int max_value() {
        return deque.isEmpty() ? -1 : deque.peekFirst();
    }

    public void push_back(int value) {
        queue.offer(value);
        while (!deque.isEmpty() && deque.peekLast() < value) {
            deque.pollLast();
        }
        deque.offerLast(value);
    }

    public int pop_front() {
        if (queue.isEmpty()) return -1;
        if (queue.peek().equals(deque.peekFirst())) {
            deque.pollFirst();
        }
        return queue.poll();
    }
}

四、搜索算法

JZ53 数字在升序数组中出现的次数

要求:空间复杂度 O(1),时间复杂度 O(logn)

解题思路:

排序数组中的搜索问题,首先想到 二分法 解决。

本题要求统计数字 target的出现次数,可转化为:使用二分法分别找到 左边界 left 和 右边界 rightright ,易得数字 target 的数量为 right - left - 1

在这里插入图片描述

//二分查找
public class Solution {
    public int GetNumberOfK(int [] array, int k) {
        int low = 0, high = array.length-1;
        //查找右边界
        while (low <= high) {
            int mid = low + (high - low) / 2;
            if (array[mid] <= k)
                low = mid + 1;
            else
                high = mid - 1;
        }
        int right = low; //right=4位置!!

        //查找左边界
        low=0;high=array.length-1;
        while (low <= high) {
            int mid = low + (high - low) / 2;
            if (array[mid] < k)
                low = mid + 1;
            else
                high = mid - 1;
        }
        int left = high;  // left=2位置
        return right-left-1;
    }
}

JZ4 二维数组中的查找

题解三: 线性搜索

复杂度分析:
时间复杂度:O(M+N)
空间复杂度:O(1)

public class Solution {
    public boolean Find(int target, int [][] array) {
        int rows = array.length, cols = array[0].length;
        int i = 0, j = cols-1;
        while (i < rows && j >= 0) {
            if (target < array[i][j]) {
                j--;
            } else if (target > array[i][j]) {
                i++;
            } else {
                return true;
            }
        }
        return false;
    }
}

JZ11 旋转数组的最小数字

主要思路:
1.数组可以分为两个有序的子数组。其中,左排序的数组的值大于右排序数组中的值。

2.声明left,right 分别指向数组的左右两端;

3.mid = (left+right) / 2 为二分的中间位置。
4.mid,left, right分为三种情况:
a. nums[mid] > nums[right]时, 那么 最小值一定在 [mid+1,right]区间中;
b.nums[mid] < nums[right]时,那么最小值一定在[left,mid]区间内。
c. nums[mid] = nums[right]时,无法判断最小值在哪个区间,所以此时只能缩小right的值。

微信图片_20201226204725
class Solution {
    public int minArray(int[] nums) {
        int n = nums.length;
        int left = 0;
        int right = n - 1;
        while (left < right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] > nums[right]) {
                left = mid + 1;
            } else if (nums[mid] < nums[right]) {
                //注意下标!!
                right = mid ;
            } else {
                right--;
            }
        }
        return nums[left];
    }
}

JZ44 数字序列中某一位的数字 【× 难】

在这里插入图片描述

时间复杂度O(logn) :
空间复杂度 O(logn) :

import java.util.*;
public class Solution {
    public int findNthDigit (int n) {
        int digit = 1;   //位数
        long start = 1;   //起始范围
        long count = 9;   //每个范围中的数字位数
        //1. 确定所求数字的起始范围和位数
        while (n - count > 0) {
            n -= count;
            digit += 1;
            start *= 10;
            count = 9 * start * digit;
        }
        //2. 确定所求数位所在的数字
        long num = start + (n - 1) / digit;

        //3. 确定所求数位在 num的哪一数位
        int inx = (n - 1) % digit;
        return String.valueOf(num).charAt(inx) - '0';
    }
}

剑指 Offer 57. 和为s的两个数字

剑指 Offer 57. 和为s的两个数字

注意有序数组的双指针处理方式,体悟!!

class Solution {
    public int[] twoSum(int[] nums, int target) {
        int left = 0;
        int right = nums.length - 1;
        while (left < right) {
            int sum = nums[left] + nums[right];
            if (sum == target) {
                return new int[]{nums[left], nums[right]};
            } else if (sum < target) {
                left++;
            } else {
                right--;
            }
        }
        return new int[0];
    }
}

剑指 Offer 57 - II. 和为s的连续正数序列

剑指 Offer 57 - II. 和为s的连续正数序列

滑动窗口:

class Solution {
    public int[][] findContinuousSequence(int target) {
        //左右和总和   i是左边界值 j是右边界值 s是和
        int i = 1, j = 2, s = 3;
        //结果集
        List<int[]> res = new ArrayList<>();
        //窗口左右
        while(i < j) {
            //如果和为目标值 添加到结果集
            if(s == target) {
                //数组长度为右边界-左边界+1
                int[] ans = new int[j - i + 1];
                //从左边界循环到右边界 循环添加 左值到右值
                for(int k = i; k <= j; k++)
                    ans[k - i] = k;
                res.add(ans);
            }
            //如果窗口和大于等于目标值说明 窗口溢出或者窗口到达 
            //缩减 去掉最左边的值
            if(s >= target) {
                s -= i;
                i++;
            } else {
                //如果小于目标值说明窗口未到达 扩张添加右值
                j++;
                s += j;
            }
        }
        return res.toArray(new int[0][]);

整理不易,关注和收藏后拿走!
欢迎专注我的公众号:AdaCoding 和 Github:AdaCoding123
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值