LeetCode面试热题十一

227. 基本计算器 II

给你一个字符串表达式 s ,请你实现一个基本计算器来计算并返回它的值。
整数除法仅保留整数部分。

  • 1 <= s.length <= 3 * 105
  • s 由整数和算符 (’+’, ‘-’, ‘*’, ‘/’) 组成,中间由一些空格隔开
  • s 表示一个有效表达式
  • 表达式中的所有整数都是非负整数,且在范围 [0, 231 - 1] 内
  • 题目数据保证答案是一个 32-bit 整数

示例:

输入:s = " 3+5 / 2 "
输出:5

输入:s = "3+2*2"
输出:7

解题思路:使用栈,将数字前面的算符记录下来,如果数字前面的算符是+,则直接将当前数字入栈,如果数字前面的算符是-,则将当前数的相反数入栈,如果数字前面的算符是 *,则将当前数字与栈顶元素做乘,并将结果入栈,如果数字前面的算符是 /,则将当前数字与栈顶元素左除,并将结果入栈。遍历完字符串后,此时将栈中的所有元素相加,即为表达式的值。

public int calculate(String s) {
    Stack<Integer> stack = new Stack<>();
    int num = 0;
    char preSign = '+';
    int len = s.length();
    for(int i = 0;i < len; i++){
        if(Character.isDigit(s.charAt(i))){
            num = num * 10 + s.charAt(i)-'0';
        }
        if(i == len - 1 || s.charAt(i) != ' ' && !Character.isDigit(s.charAt(i))){
            switch (preSign){
                case '+':
                    stack.push(num);
                    break;
                case '-':
                    stack.push(-num);
                    break;
                case '*':
                    stack.push(stack.pop() * num);
                    break; 
                default:
                    stack.push(stack.pop() / num);
            }
            preSign = s.charAt(i);
            num = 0;
        }
    }
    int ans = 0;
    while(!stack.isEmpty()){
        ans += stack.pop();
    }
    return ans;
}

230. 二叉搜索树中第K小的元素

给定一个二叉搜索树的根节点 root ,和一个整数 k ,请你设计一个算法查找其中第 k 个最小元素(从 1 开始计数)。
示例:
请添加图片描述

输入:root = [5,3,6,2,4,null,null,1], k = 3
输出:3
  • 树中的节点数为 n 。
  • 1 <= k <= n <= 104
  • 0 <= Node.val <= 104

方法一:中序遍历
解题思路:使用中序遍历树,并记录遍历的个数,当为k个最小值,则返回。

// 记录遍历的个数
int index = 0;
// 记录结果
int res = 0;
public int kthSmallest(TreeNode root, int k) {
    // 中序遍历找打第 K 个数
    dfs(root,k);
    return res;
}
public void dfs(TreeNode root,int k){
    if(root == null){
        return;
    }
    // 左
    dfs(root.left,k);
    index++;
    // 中
    if(index == k){
        res = root.val;
        return;
    }
    // 右
    dfs(root.right,k);
}

方法二:二分法,对树的子树求个数,判断第 k个最小的元素在左子树还是右子树。进行递归查找

public int kthSmallest(TreeNode root, int k) {
    // 得到右边子树的个数
    int leftNum = findChild(root.left);
    if(k <= leftNum){
    	// 第k个最小的元素在左边
        return kthSmallest(root.left,k);
    } else if(leftNum + 1 == k){
    	// 当前元素是第k个最小的元素
        return root.val;
    }else{
    	// 第k个最小的元素在右边
        return kthSmallest(root.right,k-leftNum-1);
    }
}
public int findChild(TreeNode root){
    if(root == null){
        return 0;
    }
    return findChild(root.left)+findChild(root.right)+1;
}

234. 回文链表

给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。
示例:
请添加图片描述

输入:head = [1,2,2,1]
输出:true

提示:

  • 链表中节点数目在范围[1, 105] 内
  • 0 <= Node.val <= 9

进阶:你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?

解题思路:快慢指针,使用快指针和慢指针,快指针走两步,慢指针走一步,等快指针到结尾,慢指针到中间位置。然后将后半部分链表反转,再判断是否为回文串。

public boolean isPalindrome(ListNode head) {
    ListNode fast,slow;
    fast = slow = head;
    if(fast.next == null){
        return true;
    }
    while(fast.next != null && fast.next.next != null){
        fast = fast.next.next;
        slow = slow.next;
    }
    // 此时 slow为链表的中间节点,将 slow.next置为null,将链表分割为两个子链表。
    ListNode tmp,reversalHead = slow.next;
    slow.next = null;
    slow = reversalHead.next;
    reversalHead.next = null;
    // 将后半部分链表反转。
    while(slow != null){
        tmp = slow.next;
        slow.next = reversalHead;
        reversalHead = slow;
        slow = tmp;
    }
    // 比较两个链表
    while(reversalHead != null){
        if(reversalHead.val != head.val){
            return false;
        }
        reversalHead = reversalHead.next;
        head = head.next;
    }
    return true;
}

236. 二叉树的最近公共祖先

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
示例:
请添加图片描述

输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出:3
解释:节点 5 和节点 1 的最近公共祖先是节点 3

示例:
请添加图片描述

输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出:5
解释:节点 5 和节点 4 的最近公共祖先是节点 5 。因为根据定义最近公共祖先节点可以为节点本身。

解题思路:对二叉树做递归遍历。先从最左边的节点考虑,如果该节点的左子树,或右子树分别包含其中一个元素,则该元素为最近公共祖先。或者该节点是其中一个节点,并且子树包含另一个节点,则该元素为最近公共祖先。

// ans 保存最近公共祖先。
TreeNode ans = null;
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
    dfs(root,p,q);
    return ans;
}
public boolean dfs(TreeNode root,TreeNode p,TreeNode q){
    if (root == null) return false;
    if( ans != null ){
        return false;
    }
    boolean lson = dfs(root.left, p, q);
    boolean rson = dfs(root.right, p, q);
    // 子树包含两个元素或本身为其中一个元素,子树包含另一个。
    if ((lson && rson) || ((root.val == p.val || root.val == q.val) && (lson || rson))) {
        ans = root;
    }
    // 返回是否包含其中一个元素,包含其中一个,或本身是另一个。
    return lson || rson || (root.val == p.val || root.val == q.val);
}

237. 删除链表中的节点

请编写一个函数,用于删除单链表中某个特定节点 。在设计函数时需要注意,你无法访问链表的头节点 head ,只能直接访问要被删除的节点 。题目数据保证需要删除的节点不是末尾节点 。

示例:
请添加图片描述

输入:head = [4,5,1,9], node = 5
输出:[4,1,9]
解释:指定链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9

解题思路:将下一个节点的值,赋值给当前节点的值,将链表的最后一个节点删除。

public void deleteNode(ListNode node) {
    ListNode next = node.next;
    while(next.next != null){
    	// 将下一个节点的值赋给当前节点。
        node.val = next.val;
        node = node.next;
        next = next.next;
    }
    node.val = next.val;
    // 将最后一个节点删除
    node.next = null;
}

238. 除自身以外数组的乘积

给你一个长度为 n 的整数数组 nums,其中 n > 1,返回输出数组 output ,其中 output[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积。

示例:

输入: [1,2,3,4]
输出: [24,12,8,6]

提示:题目数据保证数组之中任意元素的全部前缀元素和后缀(甚至是整个数组)的乘积都在 32 位整数范围内。
说明: 请不要使用除法,且在 O(n) 时间复杂度内完成此题。

解题思路:new一个结果数组,对求解数组进行两次遍历求解,先求出每个元素的右边的乘积的和,然后再求出每个元素左边的所有元素的乘积的和,相加即可。返回结果数组。

public int[] productExceptSelf(int[] nums) {     
    int[] output = new int[nums.length];
    Arrays.fill(output,1);
    // 计算每个元素所有右边的和
    for(int n = nums.length - 1;n > 0;n--){
        output[n - 1] = nums[n] * output[n];
    }
    // 计算每个元素所有左边的和,并加上所有右边的和,即为,除了自身整个数组的和。
    int tmp = 1;
    for(int n = 1;n < nums.length; n++){
        tmp *= nums[n - 1];
        output[n] *= tmp;
    }
    return output;
}

239. 滑动窗口最大值

给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

返回滑动窗口中的最大值。
示例 :

输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]
解释:
滑动窗口的位置                		最大值
-----------------------------------------
[1  3  -1] -3  5  3  6  7       	 3
 1 [3  -1  -3] 5  3  6  7            3
 1  3 [-1  -3  5] 3  6  7       	 5
 1  3  -1 [-3  5  3] 6  7       	 5
 1  3  -1  -3 [5  3  6] 7       	 6
 1  3  -1  -3  5 [3  6  7]      	 7

解题思路:使用单调队列,在队列中维护单调递减的属性,队列的头节点值最大,队列的尾节点值最小。

public int[] maxSlidingWindow(int[] nums, int k) {
    // 单调队列
    Deque<Integer> deque = new LinkedList<>();
    int[] ans = new int[nums.length -  k + 1];
    int right,left = 0;
    // 先将滑动窗口初始化,考虑数组中的前k个元素。
    for(right = 0;right < k;right++){
    	// 维护一个单调队列,将元素按从大到小排序。
    	// 将比当前元素小的删除,因为不可能是滑动窗户的最大值。
        while (!deque.isEmpty() && nums[right] >= nums[deque.peekLast()]) {
            deque.removeLast();
        }
        // 将元素的下标放入队列
        deque.addLast(right);
    }
    // 队列首元素就是滑动窗口的最大值。
    ans[left] = nums[deque.peekFirst()];
    for(;right < nums.length;right++){
        while (!deque.isEmpty() && nums[right] >= nums[deque.peekLast()]) {
            deque.removeLast();
        }
        deque.addLast(right);
        // 滑动窗口的左边界,每次加一
        left++;
        // 如果当前队列的最大值不在滑动窗口内,出队列
        if(left > deque.peekFirst()){
            deque.removeFirst();
        }
        // 将滑动窗口的最大元素放入结果集。
        ans[left] = nums[deque.peekFirst()];
    }
    return ans;
}

240. 搜索二维矩阵 II

编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性:

  • 每行的元素从左到右升序排列。
  • 每列的元素从上到下升序排列。

请添加图片描述

输入:matrix = [[1,4,7,11,15],
			   [2,5,8,12,19],
			   [3,6,9,16,22],
			   [10,13,14,17,24],
			   [18,21,23,26,30]], target = 5
输出:true

解题思路:从右上角开始搜索,若值大于目标值,则向左搜索,若值小于目标值,则向下搜索。若数组越界,则说明矩阵中没有目标值。

public boolean searchMatrix(int[][] matrix, int target) {
    int m = matrix.length;
    int n = matrix[0].length;
    int i,j;
    i = 0;
    j = n-1;
    // 从 matrix[0][n-1]开始搜索,目标值大于该值,则向下搜索,小于则向左搜索。
    while(i < m && j >= 0){
        if(matrix[i][j] == target){
            return true;
        }else if(matrix[i][j] > target){
            j--;
        }else{
            i++;
        }
    }
    return false;
}

242. 有效的字母异位词

给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。
注意:若 s 和 t 中每个字符出现的次数都相同,则称 s 和 t 互为字母异位词。

示例 :

输入: s = "anagram", t = "nagaram"
输出: true

提示:

  • 1 <= s.length, t.length <= 5 * 104
  • s 和 t 仅包含小写字母

进阶: 如果输入字符串包含 unicode 字符怎么办?你能否调整你的解法来应对这种情况?

解题思路:哈希表,遍历字符串s,统计s中的每一个字符出现的次数,将字符出现的次数存储到哈希表中,然后遍历字符串 t,统计 t 中的每一个字符出现的次数。对比是否一致。

public boolean isAnagram(String s, String t) {
    int[] num = new int[26];
    int i;
    for(i = 0;i<s.length();i++){
        num[s.charAt(i)-'a']++;
    }
    for(i = 0;i<t.length();i++){
        num[t.charAt(i)-'a']--;
    }
    for(i = 0;i<26;i++){
        if(num[i]!=0){
            return false;
        }
    }
    return true;
}

268. 丢失的数字

给定一个包含 [0, n] 中 n 个数的数组 nums ,找出 [0, n] 这个范围内没有出现在数组中的那个数。

示例:

输入:nums = [9,6,4,2,3,5,7,0,1]
输出:8
解释:n = 9,因为有 9 个数字,所以所有的数字都在范围 [0,9] 内。
8 是丢失的数字,因为它没有出现在 nums 中。

提示:

  • n == nums.length
  • 1 <= n <= 104
  • 0 <= nums[i] <= n
  • nums 中的所有数字都 独一无二

进阶:你能否实现线性时间复杂度、仅使用额外常数空间的算法解决此问题?

方法一:使用高斯公式求和,然后使用和减去数组中的每一个元素,最后的结果就是缺失的元素。

public int missingNumber(int[] nums) {
    int n = nums.length;
    // 数学,高斯公式
    int missSum = n*(n+1)/2;
    for(int i = 0;i<n;i++){
        missSum = missSum-nums[i];
    }
    return missSum;
}

方法二:位运算,使用异或符号,对同一个数异或两次结果为0,分别对 0 - n进行异或,再对数组中的元素异或,最后的值就是缺失的元素。

public int missingNumber(int[] nums) {
    int xor = 0;
    int n = nums.length;
    for (int i = 0; i <= n; i++) {
        xor ^= i;
    }
    for (int i = 0; i < n; i++) {
        xor ^= nums[i];
    }
    return xor;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值