lintcode算法周竞赛
------------------------------------------------------------
第七周:Follow up question
1,寻找峰值
寻找峰值 描述 笔记 数据 评测 你给出一个整数数组(size为n),其具有以下特点: 相邻位置的数字是不同的 A[0] < A[1] 并且 A[n - 2] > A[n - 1] 假定P是峰值的位置则满足A[P] > A[P-1]且A[P] > A[P+1],返回数组中任意一个峰值的位置。 注意事项 数组可能包含多个峰值,只需找到其中的任何一个即可 您在真实的面试中是否遇到过这个题? Yes 样例 给出数组[1, 2, 1, 3, 4, 5, 7, 6]返回1, 即数值 2 所在位置, 或者6, 即数值 7 所在位置.
答案和思路:利用二分来做。如果mid刚好是峰值,那么就return出来,不然的话就往比他大的一遍走。这个做法一定正确是因为峰值是一定存在的。而且左右边界是下降的。注意从1开始,length - 2结束。因为头尾都不可能是所求。
class Solution { /** * @param A: An integers array. * @return: return any of peek positions. */ public int findPeak(int[] A) { // write your code here if (null == A || A.length < 3) { return -1; } int left = 1; int right = A.length - 2; // 注意上面是从1开始,length - 2 结束。因为第一个最后一个一定不是值。 while (left <= right) { int mid = left + (right - left) / 2; if (A[mid - 1] < A[mid] && A[mid] > A[mid + 1]) { return mid; } else if (A[mid] > A[mid + 1]) { right = mid - 1; } else { left = mid + 1; } } return -1; } }
2,寻找峰值II
找峰值 II 描述 笔记 数据 评测 一个整数矩阵有如下一些特性: 相邻的整数都是不同的 矩阵有 n 行 m 列。 对于所有的 i < m, 都有 A[0][i] < A[1][i] && A[n - 2][i] > A[n - 1][i]. 对于所有的 j < n, 都有 A[j][0] < A[j][1] && A[j][m - 2] > A[j][m - 1]. 我们定义一个位置 P 是一个峰,如果有 A[j][i] > A[j+1][i] && A[j][i] > A[j-1][i] && A[j][i] > A[j][i+1] && A[j][i] > A[j][i-1]。 找出该矩阵的一个峰值元素,返回他的坐标。 注意事项 可能会存在多个峰值,返回任意一个即可。 您在真实的面试中是否遇到过这个题? Yes 样例 给一个矩阵: [ [1 ,2 ,3 ,4 ,5], [16,41,23,22,6], [15,17,24,21,7], [14,18,19,20,8], [13,12,11,10,9] ] 返回 41 的坐标[1,1], 或者 24 的坐标[2,2]。
答案和思路:
对row进行二分,到了mid这一行,对这一行的列进行二分找。
class Solution { /** * @param A: An integer matrix * @return: The index of the peak */ public List<Integer> findPeakII(int[][] A) { // write your code here // binary search to row int low = 1; int high = A.length - 2; List<Integer> ans = new ArrayList(); while (low <= high) { int mid = low + (high - low) / 2; // find the peek of the row int col = findPeak(A[mid]); if (A[mid - 1][col] < A[mid][col] && A[mid][col] > A[mid + 1][col]) { ans.add(mid); ans.add(col); break; } else if (A[mid][col] > A[mid + 1][col]) { high = mid - 1; } else { low = mid + 1; } } return ans; } public static int findPeak(int[] A) { // write your code here if (null == A || A.length < 3) { return -1; } int left = 1; int right = A.length - 2; // 注意上面是从1开始,length - 2 结束。因为第一个最后一个一定不是值。 while (left <= right) { int mid = left + (right - left) / 2; if (A[mid - 1] < A[mid] && A[mid] > A[mid + 1]) { return mid; } else if (A[mid] > A[mid + 1]) { right = mid - 1; } else { left = mid + 1; } } return -1; } }
3,找数组第k大
第k大元素 描述 笔记 数据 评测 在数组中找到第k大的元素 注意事项 你可以交换数组中的元素的位置 您在真实的面试中是否遇到过这个题? Yes 样例 给出数组 [9,3,2,4,8],第三大的元素是 4 给出数组 [1,2,3,4,5],第一大的元素是 5,第二大的元素是 4,第三大的元素是 3,以此类推
答案和思路:小顶堆
class Solution { /* * @param k : description of k * @param nums : array of nums * @return: description of return */ public int kthLargestElement(int k, int[] nums) { // write your code here if (k <= 0 || null == nums || nums.length == 0) { return -1; } PriorityQueue<Integer> minHeap = new PriorityQueue(); for (int i = 0; i < nums.length; ++i) { minHeap.add(nums[i]); if (minHeap.size() > k) { minHeap.poll(); } } return minHeap.peek(); } };
4, 子数组和
子数组之和
给定一个整数数组,找到和为零的子数组。你的代码应该返回满足要求的子数组的起始位置和结束位置
答案和思路:利用hashmap来存sum。也就是说如果当前为sum,过了一段时间又加到了sum。说明从sum开始的这一段加起来为0。
public class Solution { /** * @param nums: A list of integers * @return: A list of integers includes the index of the first number * and the index of the last number */ public ArrayList<Integer> subarraySum(int[] nums) { // write your code here ArrayList<Integer> ans = new ArrayList(); if (nums.length == 0 || nums == null) { return ans; } // 思路:用一个hashmap来存。从头到尾的sum值。只要出现sum相等,就说明从上一个sum到这里的这一段加起来是0 HashMap<Integer, Integer> map = new HashMap(); map.put(0, -1); // 结果是从sum的下一个坐标开始,所以这里是-1 int sum = 0; for (int i = 0; i < nums.length; ++i) { sum += nums[i]; if (map.containsKey(sum)) { ans.add(map.get(sum) + 1); ans.add(i); return ans; } map.put(sum, i); } return ans; } }
5,和为0的子矩阵
和为零的子矩阵 描述 笔记 数据 评测 给定一个整数矩阵,请找出一个子矩阵,使得其数字之和等于0.输出答案时,请返回左上数字和右下数字的坐标。 您在真实的面试中是否遇到过这个题? Yes 样例 给定矩阵 [ [1 ,5 ,7], [3 ,7 ,-8], [4 ,-8 ,9], ] 返回 [(1,1), (2,2)]
答案和思路:循环第low行到第high行之间。high行column列 - low行column列的差值,如果再次相等那么最右下角的这个正方形是0.
public class Solution { /** * @param matrix an integer matrix * @return the coordinate of the left-up and right-down number */ public int[][] submatrixSum(int[][] matrix) { // Write your code here int[][] res = new int[2][2]; if (matrix == null || matrix.length == 0) { return res; } int m = matrix.length; int n = matrix[0].length; int[][] sum = new int[m + 1][n + 1]; for (int i = 0; i < m; ++i) { for (int j = 0; j < n; ++j) { sum[i + 1][j + 1] = matrix[i][j] + sum[i + 1][j] + sum[i][j + 1] - sum[i][j]; } } for (int low = 0; low < m; ++low) { for (int high = low + 1; hight <= m; ++h) { HashMap<Integer, Integer> map = new HashMap<Integer, Integer>(); for (int column = 0; column <= n; ++j) { int diff = sum[high][column] - sum[low][column]; if (map.containsKey(diff)) { result[0][0] = low; result[1][0] = high - 1; result[0][1] = map.get(diff); result[1][1] = j - 1; return result; } map.put(diff, j); } } } return result; } }
------------------------------------------------------------
第六周:动态规划 I
1,石头归并
石子归并 描述 笔记 数据 评测 有一个石子归并的游戏。最开始的时候,有n堆石子排成一列,目标是要将所有的石子合并成一堆。合并规则如下: 每一次可以合并相邻位置的两堆石子 每次合并的代价为所合并的两堆石子的重量之和 求出最小的合并代价。 您在真实的面试中是否遇到过这个题? Yes 样例 对于石子序列:[4, 1, 1, 4](每个数代表这堆石子的重量),最优合并方案下,合并代价为 18: 1. 合并第2堆和第3堆 => [4, 2, 4], 代价 +2 2. 合并前两堆 => [6, 4],代价 +6 3. 合并剩下的两堆 => [10],代价 +10 其他例子: [1, 1, 1, 1] 代价为 8 [4, 4, 5, 9] 代价为 43 标签
答案和分析:
public class Solution { /** * @param A an integer array * @return an integer */ public int stoneGame(int[] a) { // Write your code here if (a == null || a.length == 0) { return 0; } int n = a.length; int[][] dp = new int[n][n]; boolean[][] visit = new boolean[n][n]; int[][] sum = new int[n][n]; for (int i = 0; i < n; ++i) { sum[i][i] = a[i]; for (int j = i + 1; j < n; ++j) { sum[i][j] = sum[i][j - 1] + a[j]; } } return search(0, n - 1, dp, visit, sum); } public static int search(int left, int right, int[][] dp, boolean[][] visit, int[][] sum) { if (visit[left][right]) { return dp[left][right]; } if (left == right) { visit[left][right] = true; return dp[left][right]; } dp[left][right] = Integer.MAX_VALUE; for (int k = left; k < right; ++k) { dp[left][right] = Math.min(dp[left][right], search(left, k, dp, visit, sum) + search(k + 1, right, dp, visit, sum) + sum[left][right]); } visit[left][right] = true; return dp[left][right]; } }
2,爆炸气球
Burst Balloons Description Notes Testcase Judge Given n balloons, indexed from 0 to n-1. Each balloon is painted with a number on it represented by array nums. You are asked to burst all the balloons. If the you burst balloon i you will get nums[left] * nums[i] * nums[right] coins. Here left and right are adjacent indices of i. After the burst, the left and right then becomes adjacent. Find the maximum coins you can collect by bursting the balloons wisely. - You may imagine nums[-1] = nums[n] = 1. They are not real therefore you can not burst them. - 0 ≤ n ≤ 500, 0 ≤ nums[i] ≤ 100 Have you met this question in a real interview? Yes Example Given [4, 1, 5, 10] Return 270 nums = [4, 1, 5, 10] burst 1, get coins 4 * 1 * 5 = 20 nums = [4, 5, 10] burst 5, get coins 4 * 5 * 10 = 200 nums = [4, 10] burst 4, get coins 1 * 4 * 10 = 40 nums = [10] burst 10, get coins 1 * 10 * 1 = 10 Total coins 20 + 200 + 40 + 10 = 270
答案和分析:dp[i][j] = Math.min(dp[i][j], search(left, k - 1) + search(k + 1, right) + value)
public class Solution { /** * @param nums a list of integer * @return an integer, maximum coins */ public int maxCoins(int[] nums) { // Write your code here int n = nums.length; int[][] dp = new int[n + 2][n + 2]; boolean[][] visited = new boolean[n + 2][n + 2]; int[] arr = new int[n + 2]; for (int i = 1; i <= n; i++) { arr[i] = nums[i - 1]; } arr[0] = 1; arr[n + 1] = 1; return search(arr, dp, visited, 1, n); } public static int search(int[] arr, int[][] dp, boolean[][] visited, int left, int right) { if (visited[left][right]) { return dp[left][right]; } int res = 0; for (int k = left; k <= right; ++k) { int midValue = arr[left - 1] * arr[k] * arr[right + 1]; int leftValue = search(arr, dp, visited, left, k - 1); int rightValue = search(arr, dp, visited, k + 1, right); res = Math.max(res, leftValue + midValue + rightValue); } visited[left][right] = true; dp[left][right] = res; return res; } }
3,背包问题
背包问题 描述 笔记 数据 评测 在n个物品中挑选若干物品装入背包,最多能装多满?假设背包的大小为m,每个物品的大小为A[i] 注意事项 你不可以将物品进行切割。 您在真实的面试中是否遇到过这个题? Yes 样例 如果有4个物品[2, 3, 5, 7] 如果背包的大小为11,可以选择[2, 3, 5]装入背包,最多可以装满10的空间。 如果背包的大小为12,可以选择[2, 3, 7]装入背包,最多可以装满12的空间。 函数需要返回最多能装满的空间大小。
答案和思路:dp[i][j] 表示前i个物品取出一些是否能够构成j价值。
dp[i + 1][j] = true:<=> dp[i][j - A[i]] == true;
dp[i + 1][j] = dp[i][j];
public class Solution { /** * @param m: An integer m denotes the size of a backpack * @param A: Given n items with size A[i] * @return: The maximum size */ public int backPack(int m, int[] A) { // write your code here if (A == null || A.length == 0) { return 0; } // dp[i][j] : 前i个物品是否能够组成j价值 boolean[][] dp = new boolean[A.length + 1][m + 1]; dp[0][0] = true; for (int i = 0; i < A.length; i++) { for (int j = 0; j < m + 1; j++) { dp[i + 1][j] = dp[i][j]; if (j >= A[i] && dp[i][j - A[i]]) { dp[i + 1][j] = true; } } } for (int j = m; j >= 0; j--) { if (dp[A.length][j]) { return j; } } return 0; } }
滚动数组优化:
public class Solution { /** * @param m: An integer m denotes the size of a backpack * @param A: Given n items with size A[i] * @return: The maximum size */ public int backPack(int m, int[] A) { // write your code here if (A == null || A.length == 0) { return 0; } // dp[i][j] : 前i个物品是否能够组成j价值 boolean[][] dp = new boolean[2][m + 1]; dp[0][0] = true; for (int i = 0; i < A.length; i++) { for (int j = 0; j < m + 1; j++) { dp[(i + 1) % 2][j] = dp[i % 2][j]; if (j >= A[i] && dp[i % 2][j - A[i]]) { dp[(i + 1) % 2][j] = true; } } } for (int j = m; j >= 0; j--) { if (dp[(A.length) % 2][j]) { return j; } } return 0; } }
4, 背包2
背包问题 II
描述
笔记
数据
评测
给出n个物品的体积A[i]和其价值V[i],将他们装入一个大小为m的背包,最多能装入的总价值有多大?
注意事项
A[i], V[i], n, m均为整数。你不能将物品进行切分。你所挑选的物品总体积需要小于等于给定的m。
您在真实的面试中是否遇到过这个题? Yes
样例
对于物品体积[2, 3, 5, 7]和对应的价值[1, 5, 2, 4], 假设背包大小为10的话,最大能够装入的价值为9。
答案和思路:dp[j] 表示占用j的体积的时候的最大的价值。dp[j] = dp[j - A[i]] + V[i].一直把dp[j] 最大值存下来。对于j来说,对于每一个进来的A[i]他要看dp[j - A[i]]那个地方能够偶安多少然后加上V[I]。加上v[i] 就是把V[I] 加进来。
public class Solution { /** * @param m: An integer m denotes the size of a backpack * @param A & V: Given n items with size A[i] and value V[i] * @return: The maximum value */ public int backPackII(int m, int[] A, int V[]) { // write your code here if (m == 0 || A.length == 0 || V.length == 0) { return 0; } int[] dp = new int[m + 1]; for (int i = 0; i < A.length; ++i) { for (int j = m; j >= A[i]; --j) { dp[j] = Math.max(dp[j], dp[j - A[i]] + V[i]); } } return dp[m]; } }
5,最大子数组III
最大子数组 III
描述
笔记
数据
评测
给定一个整数数组和一个整数 k,找出 k 个不重叠子数组使得它们的和最大。每个子数组的数字在数组中的位置应该是连续的。
返回最大的和。
注意事项
子数组最少包含一个数
答案和思路:
localMax[i][j] : 第j个元素必须取。前j个元素里面取出i个的max。
gloabMax
public class Solution { /** * @param nums: A list of integers * @param k: An integer denote to find k non-overlapping subarrays * @return: An integer denote the sum of max k non-overlapping subarrays */ public int maxSubArray(int[] nums, int k) { // write your code here if (nums.length < k) { return 0; } int len = nums.length; // 前j个元素的i个子数组的max int[][] globalMax = new int[k + 1][len + 1]; int[][] localMax = new int[k + 1][len + 1]; for (int i = 1; i <= k; ++i) { localMax[i][i - 1] = Integer.MIN_VALUE; for (int j = i; j <= len; ++j) { localMax[i][j] = Math.max(localMax[i][j - 1], globalMax[i - 1][j - 1]) + nums[j - 1]; if (j == i) { globalMax[i][j] = localMax[i][j]; } else { globalMax[i][j] = Math.max(globalMax[i][j - 1], localMax[i][j]); } } } return globalMax[k][len]; } }
------------------------------------------------------------
第五周:动态规划 I
1,房子抢劫
打劫房屋
描述
笔记
数据
评测
假设你是一个专业的窃贼,准备沿着一条街打劫房屋。每个房子都存放着特定金额的钱。你面临的唯一约束条件是:相邻的房子装着相互联系的防盗系统,且 当相邻的两个房子同一天被打劫时,该系统会自动报警。
给定一个非负整数列表,表示每个房子中存放的钱, 算一算,如果今晚去打劫,你最多可以得到多少钱 在不触动报警装置的情况下。
您在真实的面试中是否遇到过这个题? Yes
样例
给定 [3, 8, 4], 返回 8.
答案和思路:首先他这一家是否抢劫取决于,他如果抢这一家加上前前一家的钱和他采取抢前一家的钱谁多。这里优化用到滚动指针。因为他与他的前两个数字有关,可以通过%2来做。因为整个数组只需要一直有两个空位就够了。
注意:既然要用滚动指针来优化,那么开得空间的只需要2就行了。
public class Solution { /** * @param A: An array of non-negative integers. * return: The maximum amount of money you can rob tonight */ public long houseRobber(int[] A) { // write your code here if (null == A || A.length == 0) { return 0; } if (A.length == 1) { return A[0]; } if (A.length == 2) { return Math.max(A[0], A[1]); } long res = 0; long[] dp = new long[2]; dp[0] = A[0]; dp[1] = Math.max(A[0], A[1]); for (int i = 2; i < A.length; ++i) { // dp[i] = Math.max(dp[i - 1], dp[i - 2] + A[i]); // 优化 dp[i % 2] = Math.max(dp[(i - 1) % 2], dp[(i - 2) % 2] + A[i]); } return dp[(A.length - 1) % 2]; } }
2,房子抢劫II
打劫房屋 II
描述
笔记
数据
评测
在上次打劫完一条街道之后,窃贼又发现了一个新的可以打劫的地方,但这次所有的房子围成了一个圈,这就意味着第一间房子和最后一间房子是挨着的。每个房子都存放着特定金额的钱。你面临的唯一约束条件是:相邻的房子装着相互联系的防盗系统,且 当相邻的两个房子同一天被打劫时,该系统会自动报警。
给定一个非负整数列表,表示每个房子中存放的钱, 算一算,如果今晚去打劫,你最多可以得到多少钱 在不触动报警装置的情况下。
注意事项
这题是House Robber的扩展,只不过是由直线变成了圈
您在真实的面试中是否遇到过这个题? Yes
样例
给出nums = [3,6,4], 返回 6, 你不能打劫3和4所在的房间,因为它们围成一个圈,是相邻的.
答案和思路:在第一题的基础上,只不过是相对于抹去第一个和最后一个元素。
public class Solution { /** * @param nums: An array of non-negative integers. * return: The maximum amount of money you can rob tonight */ public int houseRobber2(int[] A) { // write your code here if (null == A || A.length == 0) { return 0; } if (A.length == 1) { return A[0]; } if (A.length == 2) { return Math.max(A[0], A[1]); } int[] a1 = new int[A.length - 1]; int[] a2 = new int[A.length - 1]; for (int i = 0; i < A.length - 1; ++i) { a1[i] = A[i]; } for (int i = 1; i < A.length; ++i) { a2[i - 1] = A[i]; } return Math.max(houseRobber(a1), houseRobber(a2)); } public int houseRobber(int[] A) { int[] dp = new int[2]; dp[0] = A[0]; dp[1] = Math.max(A[0], A[1]); for (int i = 2; i < A.length; ++i) { // dp[i] = Math.max(dp[i - 1], dp[i - 2] + A[i]); // 优化 dp[i % 2] = Math.max(dp[(i - 1) % 2], dp[(i - 2) % 2] + A[i]); } return dp[(A.length - 1) % 2]; } }
3,爬楼梯
爬楼梯 描述 笔记 数据 评测 假设你正在爬楼梯,需要n步你才能到达顶部。但每次你只能爬一步或者两步,你能有多少种不同的方法爬到楼顶部? 您在真实的面试中是否遇到过这个题? Yes 样例 比如n=3,1+1+1=1+2=2+1=3,共有3中不同的方法 返回 3
答案和思路:滚动指针来优化。dp数组大小为2
public class Solution { /** * @param n: An integer * @return: An integer */ public int climbStairs(int n) { // write your code here if (n <= 0) { return 1; } if (n <= 2) { return n; } int[] dp = new int[2]; dp[0] = 1; dp[1] = 2; for (int i = 2; i < n; i++) { dp[i % 2] = dp[(i - 1) % 2] + dp[(i - 2) % 2]; } return dp[(n - 1) % 2]; } }
4,求二维矩阵里面1的最大正方形
最大正方形 描述 笔记 数据 评测 在一个二维01矩阵中找到全为1的最大正方形 您在真实的面试中是否遇到过这个题? Yes 样例 1 0 1 0 0 1 0 1 1 1 1 1 1 1 1 1 0 0 1 0 返回 4
答案和思路:dp[i][j]表示:以第i,j个点为右下角能够带来的最大边长。他等于上面相邻三个的最小值+1;注意这里求出的是边长,而题目需要的是面积。所以return的时候max * max;然后用滚动数组来优化。
public class Solution { /** * @param matrix: a matrix of 0 and 1 * @return: an integer */ public int maxSquare(int[][] matrix) { // write your code here if (null == matrix || matrix.length == 0) { return 0; } int m = matrix.length; int n = matrix[0].length; int max = 0; int[][] dp = new int[m][n]; // dp[i][j]表示以i,j结尾能达到的最大的正方形的边长 for (int i = 0; i < m; ++i) { if (matrix[i][0] == 1) { max = 1; } dp[i][0] = matrix[i][0]; } for (int j = 0; j < n; ++j) { if (dp[0][j] == 1) { max = 1; } dp[0][j] = matrix[0][j]; } for (int i = 1; i < m; ++i) { for (int j = 1; j < n; ++j) { if (matrix[i][j] == 1) { dp[i % 2][j] = Math.min(dp[(i - 1) % 2][j - 1], Math.min(dp[(i - 1) % 2][j], dp[i % 2][j - 1])) + 1; } else { dp[i % 2][j] = 0; } max = Math.max(max, dp[i % 2][j]); } } return max * max; } }
5,最大上升子序列
最长上升子序列 描述 笔记 数据 评测 给定一个整数序列,找到最长上升子序列(LIS),返回LIS的长度。 您在真实的面试中是否遇到过这个题? Yes 说明 最长上升子序列的定义: 最长上升子序列问题是在一个无序的给定序列中找到一个尽可能长的由低到高排列的子序列,这种子序列不一定是连续的或者唯一的。 https://en.wikipedia.org/wiki/Longest_increasing_subsequence 样例 给出 [5,4,1,2,3],LIS 是 [1,2,3],返回 3 给出 [4,2,4,5,3,7],LIS 是 [4,4,5,7],返回 4
答案和思路:这叫做记忆化搜索。因为到了第i个只需要看一下比他小的那些哪个比他小。不用再次去从头扫一遍。dp[i] = dp[j] + 1;
public class Solution { /** * @param nums: The integer array * @return: The length of LIS (longest increasing subsequence) */ public int longestIncreasingSubsequence(int[] nums) { // write your code here if (null == nums || nums.length == 0) { return 0; } int[] dp = new int[nums.length]; // dp[i] 表示以i为结尾能够构成的最长上升序列 int max = 0; for (int i = 0; i < nums.length; ++i) { dp[i] = 1; for (int j = 0; j < i; ++j) { if (nums[j] <= nums[i]) { dp[i] = Math.max(dp[i], dp[j] + 1); } max = Math.max(max, dp[i]); } } return max; } }
6,最长上升子序列
最长上升连续子序列 II 描述 笔记 数据 评测 给定一个整数矩阵(其中,有 n 行, m 列),请找出矩阵中的最长上升连续子序列。(最长上升连续子序列可从任意行或任意列开始,向上/下/左/右任意方向移动)。 您在真实的面试中是否遇到过这个题? Yes 样例 给定一个矩阵 [ [1 ,2 ,3 ,4 ,5], [16,17,24,23,6], [15,18,25,22,7], [14,19,20,21,8], [13,12,11,10,9] ] 返回 25
答案和思路:也是一样的利用记忆化搜索。但是因为是二维的,而且题目说的是任意开始结束。所以,四个方向查询,只要是比他小的那么就+1.问题来了,怎么退出,利用flag来做标记。
public class Solution { /** * @param A an integer matrix * @return an integer */ static int n = 0; static int m = 0; public int longestIncreasingContinuousSubsequenceII(int[][] a) { // Write your code here if (null == a || a.length == 0) { return 0; } n = a.length; m = a[0].length; int[][] dp = new int[n][m]; int[][] flag = new int[n][m]; int ans = 0; for (int i = 0; i < n; ++i) { for (int j = 0; j < m; ++j) { dp[i][j] = search(i, j, a, dp, flag); ans = Math.max(ans, dp[i][j]); } } return ans; } static int[] dx = {1, 0, -1, 0}; static int[] dy = {0, 1, 0, -1}; public static int search(int x, int y, int[][] a, int[][] dp, int[][] flag) { if (flag[x][y] != 0) { return dp[x][y]; } int ans = 1; for (int i = 0; i < 4; ++i) { int nx = x + dx[i]; int ny = y + dy[i]; if (0 <= nx && nx < n && 0 <= ny && ny < m) { if (a[x][y] > a[nx][ny]) { ans = Math.max(ans, search(nx, ny, a, dp, flag) + 1); } } } flag[x][y] = 1; dp[x][y] = ans; return ans; } }
7 ,硬币排成线I
硬币排成线 描述 笔记 数据 评测 有 n 个硬币排成一条线。两个参赛者轮流从右边依次拿走 1 或 2 个硬币,直到没有硬币为止。拿到最后一枚硬币的人获胜。 请判定 第一个玩家 是输还是赢? 您在真实的面试中是否遇到过这个题? Yes 样例 n = 1, 返回 true. n = 2, 返回 true. n = 3, 返回 false. n = 4, 返回 true. n = 5, 返回 true.
两种解法,1种是用dp来做。这属于博弈dp。用滚动指针来优化。
思路:所谓的赢就是谁拿了这个硬币。那么dp[i - 1] 或者dp[i - 2] 为false,就以为这对手在这里拿起了。那么他就会一定能够拿到最后一个。所以,只要前两个中人以为为false他就为真。!dp[i - 1] || !dp[i - 2];
public class Solution { /** * @param n: an integer * @return: a boolean which equals to true if the first player will win */ public boolean firstWillWin(int n) { // write your code here if (n <= 0) { return false; } if (n <= 2) { return true; } boolean[] dp = new boolean[2]; dp[0] = true; dp[1] = true; for (int i = 2; i < n; ++i) { dp[i % 2] = !dp[(i - 1) % 2] || !dp[(i - 2) % 2]; } return dp[(n - 1) % 2]; } }
第二种解法就是推出了公式,%3 == 0的都是false;
public class Solution { /** * @param n: an integer * @return: a boolean which equals to true if the first player will win */ public boolean firstWillWin(int n) { // write your code here if (n % 3 == 0) { return false; } return true; } }
8,硬币排成线II
硬币排成线 II 描述 笔记 数据 评测 有 n 个不同价值的硬币排成一条线。两个参赛者轮流从左边依次拿走 1 或 2 个硬币,直到没有硬币为止。计算两个人分别拿到的硬币总价值,价值高的人获胜。 请判定 第一个玩家 是输还是赢? 您在真实的面试中是否遇到过这个题? Yes 样例 给定数组 A = [1,2,2], 返回 true. 给定数组 A = [1,2,4], 返回 false.
Analyse:dp[i] 表示的是还剩下i个硬币的时候它能够拿到的最大的价值。那么对于这一次来说,先手保证最大,就是保证下一步另一个人拿到的最小。这一步一共有sum[i], 那么sum[i]减去下一步能拿到的小的那个就保证了最大。dp[i] = sum[i] - Math.min(dp[i + 1], dp[i + 2]);
public class Solution { /** * @param values: an array of integers * @return: a boolean which equals to true if the first player will win */ public boolean firstWillWin(int[] values) { // write your code here int n = values.length; int[] dp = new int[n + 1]; boolean[] flag = new boolean[n + 1]; int[] sum = new int[n + 1]; sum[n- 1] = values[n - 1]; int allSum = values[n - 1]; for (int i = n - 2; i >= 0; --i) { sum[i] = sum[i + 1] + values[i]; allSum += values[i]; } return allSum / 2 < memorySearch(0, n, dp, flag, values, sum); } // dp[i] 还剩下i个的时候能拿多少钱 public static int memorySearch(int i, int n, int[] dp, boolean[] flag, int[] values, int[] sum) { if (flag[i]) { return dp[i]; } flag[i] = true; if (i == n) { dp[i] = 0; } else if (i == n - 1) { dp[i] = values[n - 1]; } else if (i == n - 2) { dp[i] = values[n - 1] + values[n - 2]; } else { dp[i] = sum[i] - Math.min(memorySearch(i + 1, n, dp, flag, values, sum), memorySearch(i + 2, n, dp, flag, values, sum)); } return dp[i]; } }
9,硬币排成线III
硬币排成线 III 描述 笔记 数据 评测 有 n 个硬币排成一条线,每一枚硬币有不同的价值。两个参赛者轮流从任意一边取一枚硬币,知道没有硬币为止。计算拿到的硬币总价值,价值最高的获胜。 请判定 第一个玩家 是输还是赢? 您在真实的面试中是否遇到过这个题? Yes 样例 给定数组 A = [3,2,2], 返回 true. 给定数组 A = [1,2,4], 返回 true. 给定数组 A = [1,20,4], 返回 false.
Analyse:
dp[i][j] 表示的是:还剩下第i到第j的时候先手能够最大拿多少。
public class Solution { /** * @param values: an array of integers * @return: a boolean which equals to true if the first player will win */ public boolean firstWillWin(int[] values) { // write your code here int n = values.length; int[][] dp = new int[n + 1][n + 1]; //现在还有i到j个硬币的时候先手能到的最大分数。 boolean[][] flag = new boolean[n + 1][n + 1]; int[][] sum = new int[n + 1][n + 1]; for (int i = 0; i < n; ++i) { for (int j = i; j < n; j++) { sum[i][j] = i == j ? values[j] : sum[i][j - 1] + values[j]; } } int allSum = 0; for (int now : values) { allSum += now; } return allSum < 2 * memorySearch(0, values.length - 1, dp, flag, values, sum); } public static int memorySearch(int left, int right, int[][] dp, boolean[][] flag, int[] values, int[][] sum) { if (flag[left][right]) { return dp[left][right]; } flag[left][right] = true; if (left > right) { dp[left][right] = 0; } else if (left == right) { dp[left][right] = values[left]; } else if (left + 1 == right) { dp[left][right] = Math.max(values[left], values[right]); // 因为只取一枚 } else { dp[left][right] = sum[left][right] - Math.min(memorySearch(left + 1, right, dp, flag, values, sum), memorySearch(left, right - 1, dp, flag, values, sum)); } return dp[left][right]; } }
3,最大子数组
最大子数组
描述
笔记
数据
评测
给定一个整数数组,找到一个具有最大和的子数组,返回其最大和。
注意事项
子数组最少包含一个数
您在真实的面试中是否遇到过这个题? Yes
样例
给出数组[−2,2,−3,4,−1,2,1,−5,3],符合要求的子数组为[4,−1,2,1],其最大和为6
答案和思路:dp[i]:以i结尾能够带来的最大的区间值。
public class Solution { /** * @param nums: A list of integers * @return: A integer indicate the sum of max subarray */ public int maxSubArray(int[] nums) { // write your code if (null == nums || nums.length == 0) { return 0; } int[] dp = new int[nums.length]; dp[0] = nums[0]; int max = dp[0]; for (int i = 1; i < nums.length; ++i) { dp[i] = Math.max(nums[i], nums[i] + dp[i - 1]); max = Math.max(dp[i], max); } return max; } }
4,最大的连乘值
乘积最大子序列
描述
笔记
数据
评测
找出一个序列中乘积最大的连续子序列(至少包含一个数)。
您在真实的面试中是否遇到过这个题? Yes
样例
比如, 序列 [2,3,-2,4] 中乘积最大的子序列为 [2,3] ,其乘积为6。
答案和思路:能够使他达到最大成绩的无非就是绝对值最大的哪些值。那么只需要存下来min,max就行。
public class Solution { /** * @param nums: an array of integers * @return: an integer */ public int maxProduct(int[] nums) { // write your code here if (null == nums || nums.length == 0) { return 0; } int len = nums.length; int[] max = new int[len]; int[] min = new int[len]; min[0] = max[0] = nums[0]; int result = nums[0]; for (int i = 1; i < nums.length; ++i) { min[i] = Math.min(nums[i], Math.min(min[i - 1] * nums[i], max[i - 1] * nums[i])); max[i] = Math.max(nums[i], Math.max(min[i - 1] * nums[i], max[i - 1] * nums[i])); result = Math.max(result, max[i]); } return result; } }
------------------------------------------------------------
第四周:两根指针
1, Two sum I
两数之和 描述 笔记 数据 评测 给一个整数数组,找到两个数使得他们的和等于一个给定的数 target。 你需要实现的函数twoSum需要返回这两个数的下标, 并且第一个下标小于第二个下标。注意这里下标的范围是 1 到 n,不是以 0 开头。 注意事项 你可以假设只有一组答案。 您在真实的面试中是否遇到过这个题? Yes 样例 给出 numbers = [2, 7, 11, 15], target = 9, 返回 [1, 2].
答案和思路:利用HashMap来辅助做题。关键点是:hash里面存的不是他,而是存target - 它的值。那么只要遍历到hashmap包含的值就说明可以构成一对了。
public class Solution { /* * @param numbers : An array of Integer * @param target : target = numbers[index1] + numbers[index2] * @return : [index1 + 1, index2 + 1] (index1 < index2) */ public int[] twoSum(int[] numbers, int target) { // write your code here if (null == numbers || numbers.length == 0) { return null; } HashMap<Integer, Integer> map = new HashMap(); for (int i = 0; i < numbers.length; ++i) { if (map.get(numbers[i]) != null) { // 注意这里如果是!= -1 就是错的。 int[] result = {map.get(numbers[i]), i + 1}; return result; } else { map.put(target - numbers[i], i + 1); } } return null; } }
2,Two Sum II
两数之和 II 描述 笔记 数据 评测 给一组整数,问能找出多少对整数,他们的和大于一个给定的目标值。 注意事项 使用 O(1) 的额外空间和 O(nlogn) 的时间。 您在真实的面试中是否遇到过这个题? Yes 样例 对于 numbers = [2, 7, 11, 15], target = 24 的情况,返回 1。因为只有11 + 15可以大于24。
答案和思路:注意一定要排序才可以用双指针。对撞型双指针。
public class Solution { /** * @param nums: an array of integer * @param target: an integer * @return: an integer */ public int twoSum2(int[] nums, int target) { // Write your code here if (null == nums || nums.length == 0) { return 0; } // Error : forget sort Arrays.sort(nums); int result = 0; int left = 0; int right = nums.length - 1; while (left < right) { if (nums[left] + nums[right] > target) { result += right - left; right--; } else { left++; } } return result; } }
3,数组构成三角形的对数计算
三角形计数 描述 笔记 数据 评测 给定一个整数数组,在该数组中,寻找三个数,分别代表三角形三条边的长度,问,可以寻找到多少组这样的三个数来组成三角形? 您在真实的面试中是否遇到过这个题? Yes 样例 例如,给定数组 S = {3,4,6,7},返回 3 其中我们可以找到的三个三角形为: {3,4,6} {3,6,7} {4,6,7} 给定数组 S = {4,4,4,4}, 返回 3 其中我们可以找到的三个三角形为: {4(1),4(2),4(3)} {4(1),4(2),4(4)} {4(1),4(3),4(4)} {4(2),4(3),4(4)}
答案和思路:以第i个为最大边的情况下有多少对。对于每一个i来说就是two sum II的类似解法。注意:一定要先排序。
public class Solution { /** * @param S: A list of integers * @return: An integer */ public int triangleCount(int S[]) { // write your code here if (null == S || S.length == 0) { return 0; } int result = 0; Arrays.sort(S); // 每次都忘记sort。一定要记得呀。 for (int i = 0; i < S.length; ++i) { int left = 0; int right = i - 1; while (left < right) { if (S[left] + S[right] > S[i]) { result += right - left; right--; } else { left++; } } } return result; } }
4,灌水I
接雨水 描述 笔记 数据 评测 给出 n 个非负整数,代表一张X轴上每个区域宽度为 1 的海拔图, 计算这个海拔图最多能接住多少(面积)雨水。 接雨水 您在真实的面试中是否遇到过这个题? Yes 样例 如上图所示,海拔分别为 [0,1,0,2,1,0,1,3,2,1,2,1], 返回 6.
答案和思路:利用双指针。灌水问题思想就是从外围开始往里走。而最低的最优先处理。
public class Solution { /** * @param heights: an array of integers * @return: a integer */ public int trapRainWater(int[] heights) { // write your code here if (heights == null || heights.length == 0) { return 0; } int left = 0; int right = heights.length - 1; int result = 0; while (left < right) { if (heights[left] < heights[right]) { int smaller = heights[left]; while (left < right && heights[left] <= smaller) { result += smaller - heights[left]; left++; } } else { int smaller = heights[right]; while (left < right && heights[right] <= smaller) { result += smaller - heights[right]; right--; } } } return result; } }
5, 装最多水的容器。
装最多水的容器 描述 笔记 数据 评测 给定 n 个非负整数 a1, a2, ..., an, 每个数代表了坐标中的一个点 (i, ai)。画 n 条垂直线,使得 i 垂直线的两个端点分别为(i, ai)和(i, 0)。找到两条线,使得其与 x 轴共同构成一个容器,以容纳最多水。 注意事项 容器不可倾斜。 您在真实的面试中是否遇到过这个题? Yes 样例 给出[1,3,2], 最大的储水面积是2.
答案和思路:利用碰撞型指针扫描。那么谁是低的就把谁往中间移动,以为对于他来说,这已经是它能够带来的最大的area了。
public class Solution { /** * @param heights: an array of integers * @return: an integer */ public int maxArea(int[] heights) { // write your code here if (null == heights || heights.length == 0) { return 0; } int left = 0; int right = heights.length - 1; int max = 0; while (left < right) { max = Math.max(max, (right - left) * Math.min(heights[left], heights[right])); if (heights[left] < heights[right]) { left++; } else { right--; } } return max; } }
6,找数组第K大。
第k大元素 描述 笔记 数据 评测 在数组中找到第k大的元素 注意事项 你可以交换数组中的元素的位置 您在真实的面试中是否遇到过这个题? Yes 样例 给出数组 [9,3,2,4,8],第三大的元素是 4 给出数组 [1,2,3,4,5],第一大的元素是 5,第二大的元素是 4,第三大的元素是 3,以此类推
一种解法就是用PriorityQueue。时间复杂度是> O(n).而且还有空间复杂度。答案如下:
class Solution { /* * @param k : description of k * @param nums : array of nums * @return: description of return */ public int kthLargestElement(int k, int[] nums) { // write your code here PriorityQueue<Integer> p = new PriorityQueue(); for (int i = 0; i < nums.length; ++i) { p.add(nums[i]); if (p.size() > k) { p.poll(); } } return p.peek(); } };
那么优化成O(n)才是最优解。
7,螺母和螺栓的问题
Nuts & Bolts Problem Description Notes Testcase Judge Given a set of n nuts of different sizes and n bolts of different sizes. There is a one-one mapping between nuts and bolts. Comparison of a nut to another nut or a bolt to another bolt is not allowed. It means nut can only be compared with bolt and bolt can only be compared with nut to see which one is bigger/smaller. We will give you a compare function to compare nut with bolt. Have you met this question in a real interview? Yes Example Given nuts = ['ab','bc','dd','gg'], bolts = ['AB','GG', 'DD', 'BC']. Your code should find the matching bolts and nuts. one of the possible return: nuts = ['ab','bc','dd','gg'], bolts = ['AB','BC','DD','GG']. we will tell you the match compare function. If we give you another compare function. the possible return is the following: nuts = ['ab','bc','dd','gg'], bolts = ['BC','AA','DD','GG']. So you must use the compare function that we give to do the sorting. The order of the nuts or bolts does not matter. You just need to find the matching bolt for each nut. Tags
答案和思路:利用快排来做。那么划分的时候要划分两次。分别用螺母划分一次螺栓,螺栓在划分一次螺母。
public class Solution { /** * @param nuts: an array of integers * @param bolts: an array of integers * @param compare: a instance of Comparator * @return: nothing */ public void sortNutsAndBolts(String[] nuts, String[] bolts, NBComparator compare) { if (nuts == null || bolts == null) return; if (nuts.length != bolts.length) return; qsort(nuts, bolts, compare, 0, nuts.length - 1); } private void qsort(String[] nuts, String[] bolts, NBComparator compare, int l, int u) { if (l >= u) return; // find the partition index for nuts with bolts[l] int part_inx = partition(nuts, bolts[l], compare, l, u); // partition bolts with nuts[part_inx] partition(bolts, nuts[part_inx], compare, l, u); // qsort recursively qsort(nuts, bolts, compare, l, part_inx - 1); qsort(nuts, bolts, compare, part_inx + 1, u); } private int partition(String[] str, String pivot, NBComparator compare, int l, int u) { for (int i = l; i <= u; i++) { if (compare.cmp(str[i], pivot) == 0 || compare.cmp(pivot, str[i]) == 0) { swap(str, i, l); break; } } String now = str[l]; int left = l; int right = u; while (left < right) { while (left < right && (compare.cmp(str[right], pivot) == -1 || compare.cmp(pivot, str[right]) == 1)) { right--; } str[left] = str[right]; while (left < right && (compare.cmp(str[left], pivot) == 1 || compare.cmp(pivot, str[left]) == -1)) { left++; } str[right] = str[left]; } str[left] = now; return left; } private void swap(String[] str, int l, int r) { String temp = str[l]; str[l] = str[r]; str[r] = temp; } }
8,和大于S的最小子数组
和大于S的最小子数组 描述 笔记 数据 评测 给定一个由 n 个整数组成的数组和一个正整数 s ,请找出该数组中满足其和 ≥ s 的最小长度子数组。如果无解,则返回 -1。 您在真实的面试中是否遇到过这个题? Yes 样例 给定数组 [2,3,1,2,4,3] 和 s = 7, 子数组 [4,3] 是该条件下的最小长度子数组。
答案和思路:利用前向型双指针。他的思想就是j指针不需要往回退。
注意点:这道题要是没答案要返回-1.所以一定要注意最后判断一下result是否有。
public class Solution { /** * @param nums: an array of integers * @param s: an integer * @return: an integer representing the minimum size of subarray */ public int minimumSize(int[] nums, int s) { // write your code here if (null == nums || nums.length == 0 || s <= 0) { return -1; } int j = 0; int sum = 0; int result = Integer.MAX_VALUE; for (int i = 0; i < nums.length; ++i) { while (j < nums.length && sum < s) { sum += nums[j]; j++; } if (sum >= s) { result = Math.min(result, j - i); } sum -= nums[i]; } if (result == Integer.MAX_VALUE) { return -1; } return result; } }
9,找无重复元素的最大子串
最长无重复字符的子串 描述 笔记 数据 评测 给定一个字符串,请找出其中无重复字符的最长子字符串。 您在真实的面试中是否遇到过这个题? Yes 样例 例如,在"abcabcbb"中,其无重复字符的最长子字符串是"abc",其长度为 3。 对于,"bbbbb",其无重复字符的最长子字符串为"b",长度为1。
答案和思路:首先不断地往前走,直到重复了停下来。那么说明最后这个导致了重复。那么从前开始依次查询,直到找到和他相等的那个字符,这里开始就会不重复。整天思想就是前向型的两个指针。
import java.util.HashSet; public class Solution { /** * @param s: a string * @return: an integer */ public int lengthOfLongestSubstring(String s) { // write your code here if (null == s || s.length() == 0) { return 0; } int start = 0; int max = 0; HashSet<Character> set = new HashSet(); for (int i = 0; i < s.length(); ++i) { char c = s.charAt(i); if (!set.contains(c)) { set.add(c); max = Math.max(max, i - start + 1); } else { // 上一次i都还没有重复的,这一次就有了,那么说明重复的是现在进来的这个元素 while (s.charAt(start) != s.charAt(i)) { set.remove(s.charAt(start)); start++; } start++; } } return max; } }
10,最小子串覆盖
最小子串覆盖 描述 笔记 数据 评测 给定一个字符串source和一个目标字符串target,在字符串source中找到包括所有目标字符串字母的子串。 注意事项 如果在source中没有这样的子串,返回"",如果有多个这样的子串,返回起始位置最小的子串。 您在真实的面试中是否遇到过这个题? Yes 说明 在答案的子串中的字母在目标字符串中是否需要具有相同的顺序? ——不需要。 样例 给出source = "ADOBECODEBANC",target = "ABC" 满足要求的解 "BANC"
答案和思路:从i这个位置起,j一直往前跑,直到包含了后面的所有。那么这时候j不用回退。i前进一个。然后j接着重复直到包含了目标字符串。
import java.util.HashMap; public class Solution { /** * @param source: A string * @param target: A string * @return: A string denote the minimum window * Return "" if there is no such a string */ public static void main(String[] args) { System.out.println(minWindow("ADOBECODEBANC", "ABC")); } public static String minWindow(String source, String target) { // write your code if (null == source || null == target || source.length() == 0 || target.length() == 0) { return ""; } HashMap<Character, Integer> sMap = new HashMap(); HashMap<Character, Integer> tMap = new HashMap(); // initialization for (int i = 0; i < target.length(); ++i) { char c = target.charAt(i); if (tMap.containsKey(c)) { tMap.put(c, tMap.get(c) + 1); } else { tMap.put(c, 1); } } int j = 0; int min = Integer.MAX_VALUE; String minStr = new String(); for (int i = 0; i < source.length(); ++i) { while (!isContain(sMap, tMap) && j < source.length()) { char c = source.charAt(j); if (sMap.containsKey(c)) { sMap.put(c, sMap.get(c) + 1); } else { sMap.put(c, 1); } j++; if (j >= source.length()) { break; } } if (isContain(sMap, tMap)) { if (min > j - i) { min = j - i; minStr = source.substring(i, j); } } if (sMap.get(source.charAt(i)) == 1) { sMap.remove(source.charAt(i)); } else { sMap.put(source.charAt(i), sMap.get(source.charAt(i)) - 1); } } return minStr; } public static boolean isContain(HashMap<Character, Integer> s, HashMap<Character, Integer> t) { if (t.size() > s.size()) { return false; } for (Character c : t.keySet()) { if (!s.containsKey(c) || s.get(c) < t.get(c)) { return false; } } return true; } }
11,最多有k个不同字符的最长的子串。
最多有k个不同字符的最长子字符串 描述 笔记 数据 评测 给定一个字符串,找到最多有k个不同字符的最长子字符串。 您在真实的面试中是否遇到过这个题? Yes 样例 例如,给定 s = "eceba" , k = 3, T 是 "eceb",长度为 4.
答案和思路:最关键的地方就是,只有是map不包含这个字符,并且这个时候map的size已经== k,也就是再进来一个就超出要求了的时候才能够计算长度。
import java.util.HashMap; public class Solution { /** * @param s : A string * @return : The length of the longest substring * that contains at most k distinct characters. */ public int lengthOfLongestSubstringKDistinct(String s, int k) { // write your code here if (null == s || s.length() == 0 || k <= 0) { return 0; } HashMap<Character, Integer> map = new HashMap(); int j = 0; int max = Integer.MIN_VALUE; for (int i = 0; i < s.length(); ++i) { while (j < s.length()) { char c = s.charAt(j); if (map.containsKey(c)) { map.put(c, map.get(c) + 1); } else { if (map.size() == k) { break; } map.put(c, 1); } j++; } if (map.size() <= k) { max = Math.max(max, j - i); } if (map.get(s.charAt(i)) == 1) { map.remove(s.charAt(i)); } else { map.put(s.charAt(i), map.get(s.charAt(i)) - 1); } } return max; } }
12,两个数组的最小差值
最小差 描述 笔记 数据 评测 给定两个整数数组(第一个是数组 A,第二个是数组 B),在数组 A 中取 A[i],数组 B 中取 B[j],A[i] 和 B[j]两者的差越小越好(|A[i] - B[j]|)。返回最小差。 您在真实的面试中是否遇到过这个题? Yes 样例 给定数组 A = [3,4,6,7], B = [2,3,8,9],返回 0。
答案和思路:排序之后,把较小的那个往后移动,那么他就会与A的那个值越来越接近。
思想就是:a > b的时候,找a - b的最小值,那么找a的最小的-b最大的。
public class Solution { /** * @param A, B: Two integer arrays. * @return: Their smallest difference. */ public int smallestDifference(int[] A, int[] B) { // write your code here if (null == A || A.length == 0 || null == B || B.length == 0) { return 0; } Arrays.sort(A); Arrays.sort(B); int ai = 0; int bi = 0; int min = Integer.MAX_VALUE; while (ai < A.length && bi < B.length) { min = Math.min(min, Math.abs(A[ai] - B[bi])); if (A[ai] < B[bi]) { ai++; } else { bi++; } } return min; } }
------------------------------------------------------------
第三周:数据结构下
补基础知识:
(1)heap定义:
java的 PriorityQueue是一个小顶堆,用于找最大前K个严肃。sort可重写。在本博客下面一个地方有详细介绍。
1,灌水。
接雨水 描述 笔记 数据 评测 给出 n 个非负整数,代表一张X轴上每个区域宽度为 1 的海拔图, 计算这个海拔图最多能接住多少(面积)雨水。 接雨水 您在真实的面试中是否遇到过这个题? Yes 样例 如上图所示,海拔分别为 [0,1,0,2,1,0,1,3,2,1,2,1], 返回 6. 挑战
答案和思路:就是双指针。从外面往里走。因为灌水问题,最左边最右边基本决定了灌水的基调。那么小的那个起了关键决定作用。
public class Solution { /** * @param heights: an array of integers * @return: a integer */ public int trapRainWater(int[] heights) { // write your code here if (null == heights || heights.length == 0) { return 0; } int ans = 0; int start = 0; int end = heights.length - 1; while (start < end) { if (heights[start] < heights[end]) { int smaller = heights[start]; while (start < end && heights[start] <= smaller) { ans += smaller - heights[start]; start++; } } else { int smaller = heights[end]; while (start < end && heights[end] <= smaller) { ans += smaller - heights[end]; end--; } } } return ans; } }
2,灌水II
接雨水 II 描述 笔记 数据 评测 给出 n * m 个非负整数,代表一张X轴上每个区域为 1 * 1 的 2d 海拔图, 计算这个海拔图最多能接住多少(面积)雨水。 您在真实的面试中是否遇到过这个题? Yes 样例 例如,给定一个 5*4 的矩阵: [ [12,13,0,12], [13,4,13,12], [13,8,10,12], [12,13,12,12], [13,13,13,13] ] 返回 14.
答案和思路:利用java heap来做。那么每一次灌水都是从最低的那个开始bfs。那么怎么来对这个二维数组排序呢,并且排序之后还要找得到他呢?
利用的还是从外层往里的思想。不过这里是二维,所以那么就是找最矮的那根柱子。那么这个需求
无疑是要用PriorityQueue来实现。为了排序后还能够找到这个柱子在二维数组中的位置。那么需要存下他
的下标
class Point { int x; int y; int h; public Point(int xx, int yy, int hh) { x = xx; y = yy; h = hh; } } public class Solution { /** * @param heights: a matrix of integers * @return: an integer */ public int trapRainWater(int[][] heights) { // write your code here // 思路:利用的还是从外层往里的思想。不过这里是二维,所以那么就是找最矮的那根柱子。那么这个需求 // 无疑是要用PriorityQueue来实现。为了排序后还能够找到这个柱子在二维数组中的位置。那么需要存下他 // 的下标 if (null == heights || heights.length == 0) { return 0; } int n = heights.length; int m = heights[0].length; PriorityQueue<Point> heap = new PriorityQueue<Point>(1, new Comparator<Point>() { public int compare(Point a, Point b) { if (a.h > b.h) { return 1; } else if (a.h < b.h) { return -1; } else { return 0; } } }); int[][] visited = new int[n][m]; // initialization for (int i = 0; i < n; ++i) { heap.add(new Point(i, 0, heights[i][0])); heap.add(new Point(i, m - 1, heights[i][m - 1])); visited[i][0] = 1; visited[i][m - 1] = 1; } for (int j = 0; j < m; ++j) { heap.add(new Point(0, j, heights[0][j])); heap.add(new Point(n - 1, j, heights[n - 1][j])); visited[0][j] = 1; visited[n - 1][j] = 1; } // start calculate int result = 0; int[] dx = {1, 0, -1, 0}; int[] dy = {0, 1, 0, -1}; while (!heap.isEmpty()) { Point top = heap.poll(); // bfs for (int i = 0; i < 4; ++i) { int nextX = top.x + dx[i]; int nextY = top.y + dy[i]; if (nextX >= 0 && nextX < n && nextY >= 0 && nextY < m && visited[nextX][nextY] == 0) { visited[nextX][nextY] = 1; // 下面这个式子非常关键,他存入的不是他自己的高度,而是他和他外围的高度的最大值 heap.add(new Point(nextX, nextY, Math.max(top.h, heights[nextX][nextY]))); result = result + Math.max(0, top.h - heights[nextX][nextY]); } } } return result; } };
3,房子轮廓(Building Outline)
大楼轮廓 描述 笔记 数据 评测 水平面上有 N 座大楼,每座大楼都是矩阵的形状,可以用三个数字表示 (start, end, height),分别代表其在x轴上的起点,终点和高度。大楼之间从远处看可能会重叠,求出 N 座大楼的外轮廓线。 外轮廓线的表示方法为若干三元组,每个三元组包含三个数字 (start, end, height),代表这段轮廓的起始位置,终止位置和高度。 Building Outline 注意事项 请注意合并同样高度的相邻轮廓,不同的轮廓线在x轴上不能有重叠。 您在真实的面试中是否遇到过这个题? Yes 样例 给出三座大楼: [ [1, 3, 3], [2, 4, 4], [5, 6, 1] ] 外轮廓线为: [ [1, 2, 3], [2, 4, 4], [5, 6, 1] ]
写了一份只能够A90%的答案:
import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.PriorityQueue; public class Solution { public static void main(String[] args) { int[][] a = {{1, 3, 3}, {2, 4, 4}, {5, 6, 1}}; System.out.println(buildingOutline(a)); } public static ArrayList<ArrayList<Integer>> buildingOutline(int[][] buildings) { ArrayList<ArrayList<Integer>> res = new ArrayList(); if (null == buildings || buildings.length == 0) { return res; } // 思路就是利用扫描线。然后按照下标来排序 ArrayList<ArrayList<Integer>> point = new ArrayList(); for (int i = 0; i < buildings.length; ++i) { ArrayList<Integer> list = new ArrayList(); list.add(buildings[i][0]); list.add(0); // 0 代表起点。1代表终点 list.add(buildings[i][2]); point.add(list); ArrayList<Integer> end = new ArrayList(); end.add(buildings[i][1]); end.add(1); end.add(buildings[i][2]); point.add(end); } Collections.sort(point, new Comparator<ArrayList<Integer>>() { public int compare(ArrayList<Integer> a, ArrayList<Integer> b) { if (a.get(0) == b.get(0)) { return a.get(1).compareTo(b.get(1)); } return a.get(0).compareTo(b.get(0)); } }); PriorityQueue<Integer> q = new PriorityQueue(1, new Comparator<Integer>() { public int compare(Integer a, Integer b) { return -1 * a.compareTo(b); } }); int height = 0; int start = point.get(0).get(0); int end = 0; height = point.get(0).get(2); for (ArrayList<Integer> a : point) { if (q.isEmpty()) { start = a.get(0); height = a.get(2); q.add(a.get(2)); } else { if (a.get(1) == 0) { q.add(a.get(2)); } else { q.remove(a.get(2)); } end = a.get(0); if (q.isEmpty() || height != q.peek()) { ArrayList<Integer> temp = new ArrayList(); temp.add(start); temp.add(end); temp.add(height); if (start != end) { res.add(temp); } if (!q.isEmpty()) { height = q.peek(); start = a.get(0); } } } } return res; } }
4,堆化(heapify)
堆化 描述 笔记 数据 评测 给出一个整数数组,堆化操作就是把它变成一个最小堆数组。 对于堆数组A,A[0]是堆的根,并对于每个A[i],A [i * 2 + 1]是A[i]的左儿子并且A[i * 2 + 2]是A[i]的右儿子。 您在真实的面试中是否遇到过这个题? Yes 说明 什么是堆? 堆是一种数据结构,它通常有三种方法:push, pop 和 top。其中,“push”添加新的元素进入堆,“pop”删除堆中最小/最大元素,“top”返回堆中最小/最大元素。 什么是堆化? 把一个无序整数数组变成一个堆数组。如果是最小堆,每个元素A[i],我们将得到A[i * 2 + 1] >= A[i]和A[i * 2 + 2] >= A[i] 如果有很多种堆化的结果? 返回其中任何一个。 样例 给出 [3,2,1,4,5],返回[1,2,3,4,5] 或者任何一个合法的堆数组
答案和思路:sort的话也可以,但是复杂度是O(n)。用siftdown的话可以优化到O(n)。
siftdown就是每次把他和父亲的最小的那个交换。
public class Solution { /** * @param A: Given an integer array * @return: void */ public void siftdown(int[] A, int k) { while (k < A.length) { int smallest = k; if (k * 2 + 1 < A.length && A[k * 2 + 1] < A[smallest]) { smallest = k * 2 + 1; } if (k * 2 + 2 < A.length && A[k * 2 + 2] < A[smallest]) { smallest = k * 2 + 2; } if (smallest == k) { break; } int temp = A[smallest]; A[smallest] = A[k]; A[k] = temp; k = smallest; } } public void heapify(int[] A) { // write your code here // Arrays.sort(A); // 用sort那么复杂度是O(nlogn).但是用heap的siftdwon优化到O(n) for (int i = A.length / 2; i >= 0; --i) { siftdown(A, i); } } }
5,数据流中位数
数据流中位数 描述 笔记 数据 评测 数字是不断进入数组的,在每次添加一个新的数进入数组的同时返回当前新数组的中位数。 您在真实的面试中是否遇到过这个题? Yes 说明 中位数的定义: 中位数是排序后数组的中间值,如果有数组中有n个数,则中位数为A[(n-1)/2]。 比如:数组A=[1,2,3]的中位数是2,数组A=[1,19]的中位数是1。 样例 持续进入数组的数的列表为:[1, 2, 3, 4, 5],则返回[1, 1, 2, 2, 3] 持续进入数组的数的列表为:[4, 5, 1, 3, 2, 6, 0],则返回 [4, 4, 4, 3, 3, 3, 3] 持续进入数组的数的列表为:[2, 20, 100],则返回[2, 2, 20]
答案和思路:用list存下来已经进来的数字,然后每次对进来的数字不断地sort。
public class Solution { /** * @param nums: A list of integers. * @return: the median of numbers */ public int[] medianII(int[] nums) { // write your code here if (null == nums || nums.length == 0) { return null; } int[] res = new int[nums.length]; ArrayList<Integer> list = new ArrayList(); for (int i = 0; i < nums.length; ++i) { list.add(nums[i]); Collections.sort(list); res[i] = list.get(i / 2); } return res; } }
上面这个解法每次进来都是要排序的。那么也就是O(n * k log k) 至少是O(n* n)。这里利用heap来优化到O(nlogn).思想很简单:中位数比他左边的大(maxHeap),比它右边的小(minHeap);那么只要不断地调整两个堆,保持maxHeap的size不比minHeap的多出两个并且不小于他就可以了。
import java.util.Comparator; import java.util.PriorityQueue; public class Solution { /** * @param nums: A list of integers. * @return: the median of numbers */ public int[] medianII(int[] nums) { // write your code here if (null == nums || nums.length == 0) { return null; } int count = 0; PriorityQueue<Integer> maxHeap = new PriorityQueue(1, new Comparator<Integer>() { public int compare(Integer a, Integer b) { return b - a; } }); PriorityQueue<Integer> minHeap = new PriorityQueue(); int[] res = new int[nums.length]; res[0] = nums[0]; maxHeap.add(nums[0]); for (int i = 1; i < nums.length; ++i) { int x = maxHeap.peek(); if (nums[i] <= x) { maxHeap.add(nums[i]); } else { minHeap.add(nums[i]); } if (maxHeap.size() > minHeap.size() + 1) { minHeap.add(maxHeap.poll()); }else if (minHeap.size() > maxHeap.size()) { maxHeap.add(minHeap.poll()); } res[i] = maxHeap.peek(); } return res; } }
6,滑动窗口的中位数
滑动窗口的中位数 描述 笔记 数据 评测 给定一个包含 n 个整数的数组,和一个大小为 k 的滑动窗口,从左到右在数组中滑动这个窗口,找到数组中每个窗口内的最大值。(如果数组中有偶数,则在该窗口存储该数字后,返回第 N/2-th 个数字。) 您在真实的面试中是否遇到过这个题? Yes 样例 对于数组 [1,2,7,8,5], 滑动大小 k = 3 的窗口时,返回 [2,7,7] 最初,窗口的数组是这样的: [ | 1,2,7 | ,8,5] , 返回中位数 2; 接着,窗口继续向前滑动一次。 [1, | 2,7,8 | ,5], 返回中位数 7; 接着,窗口继续向前滑动一次。 [1,2, | 7,8,5 | ], 返回中位数 7;
答案和思路:利用list来存,然后sort。注意点就是list.remove的参数是下标。
import java.util.ArrayList; import java.util.Collections; public class Solution { /** * @param nums: A list of integers. * @return: The median of the element inside the window at each moving. */ public static void main(String[] args) { int[] a = {1,2,7,7,2,10,3,4,5}; System.out.println(medianSlidingWindow(a, 2)); } public static ArrayList<Integer> medianSlidingWindow(int[] nums, int k) { // write your code here ArrayList<Integer> res = new ArrayList(); if (null == nums || nums.length == 0 || k == 0) { return res; } ArrayList<Integer> now = new ArrayList(); int i = 0; int index = 0; int j = 0; for (; i < k - 1; ++i) { now.add(nums[i]); } for (; i < nums.length; ++i) { now.add(nums[i]); Collections.sort(now); res.add(now.get((k - 1) / 2)); int find = 0; for (int f = 0; f < now.size() && j < nums.length; ++f) { if (now.get(f) == nums[j]) { find = f; break; } } j++; now.remove(find); } return res; } }
7,滑动窗口的最大值
滑动窗口的最大值 描述 笔记 数据 评测 给出一个可能包含重复的整数数组,和一个大小为 k 的滑动窗口, 从左到右在数组中滑动这个窗口,找到数组中每个窗口内的最大值。 您在真实的面试中是否遇到过这个题? Yes 样例 给出数组 [1,2,7,7,8], 滑动窗口大小为 k = 3. 返回 [7,7,8]. 解释: 最开始,窗口的状态如下: [|1, 2 ,7| ,7 , 8], 最大值为 7; 然后窗口向右移动一位: [1, |2, 7, 7|, 8], 最大值为 7; 最后窗口再向右移动一位: [1, 2, |7, 7, 8|], 最大值为 8.
答案和思路:利用deque来做。思想就是:如果这一刻他不是最大值,那么以后他就再也不可能是最大值了。
所以,一个数字offer进去之前,把那些比他小的都踢出去。然后另一个就是如果deque的第一个元素等于此时出去窗框的那个元素。那么也要poll出去。
import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Deque; public class Solution { /** * @param nums: A list of integers. * @return: The maximum number inside the window at each moving. */ public ArrayList<Integer> maxSlidingWindow(int[] nums, int k) { ArrayList<Integer> res = new ArrayList(); if (null == nums || nums.length == 0 || k <= 0) { return res; } Deque<Integer> deque = new ArrayDeque<Integer>(); for (int i = 0; i < nums.length; ++i) { // 下面这个操作的意思是:一旦这个数进来,那么他前面比他小的 // 就再也不可能成为最大值了。所以,把那些值poll while (!deque.isEmpty() && nums[i] > deque.peekLast()) { deque.pollLast(); } deque.offer(nums[i]); // 这里表示的是:如果最前面的这个这时候已经出窗口了那么就把他poll出去。 if (i > k - 1 && deque.peekFirst() == nums[i - k]) { deque.pollFirst(); } if (i >= k - 1) { res.add(deque.peekFirst()); } } return res; } }
-‘---------------------------------------------------------------------------------------------------
第二周:数据结构上
补基础知识:并查集:http://blog.csdn.net/dellaserss/article/details/7724401。
并查集模板:
import java.util.HashMap; import java.util.HashSet; class UnionFind { HashMap<Integer, Integer> father = new HashMap(); // 初始化 UnionFind(HashSet<Integer> hashSet) { for (Integer now : hashSet) { father.put(now, now); } } // O(n)复杂度的find // public int find(int x) { // int parent = father.get(x); // while (parent != father.get(parent)) { // parent = father.get(parent); // } // return parent; // } // O(1)复杂度的find public int compressedFind(int x) { int parent = father.get(x); while (parent != father.get(parent)) { parent = father.get(parent); } // 这里parent是他的最大祖先。那么,包括他以及中间的各种都直接设置为指道最大祖先。 int temp = -1; int xFa = x; while (xFa != father.get(xFa)) { temp = father.get(xFa); // 因为要把xFa的祖先设置为最大祖先了,所以,得先把踏上一级father记录下来。 father.put(xFa, parent); xFa = temp; } return parent; } // x,y有一条边。所以,如果他们的祖先不一样,那么就把他们随便谁和谁连起来 public void union(int x, int y) { int xFa = compressedFind(x); int yFa = compressedFind(y); if (xFa != yFa) { father.put(xFa, yFa); } } }
1,找无向图的联通块。
找出无向图中所有的连通块。 图中的每个节点包含一个label属性和一个邻接点的列表。(一个无向图的连通块是一个子图,其中任意两个顶点通过路径相连,且不与整个图中的其它顶点相连。) 您在真实的面试中是否遇到过这个题? Yes 样例 给定图: A------B C \ | | \ | | \ | | \ | | D E 返回 {A,B,D}, {C,E}。其中有 2 个连通块,即{A,B,D}, {C,E} 标签 相关题目
答案和思路1:利用队列来找。并用visited标记是否已经出现过了。
/** * Definition for Undirected graph. * class UndirectedGraphNode { * int label; * ArrayList<UndirectedGraphNode> neighbors; * UndirectedGraphNode(int x) { label = x; neighbors = new ArrayList<UndirectedGraphNode>(); } * }; */ public class Solution { /** * @param nodes a array of Undirected graph node * @return a connected set of a Undirected graph */ public List<List<Integer>> connectedSet(ArrayList<UndirectedGraphNode> nodes) { // Write your code here if (nodes == null || nodes.size() == 0) { return null; } int m = nodes.size(); Map<UndirectedGraphNode, Boolean> visited = new HashMap(); for (UndirectedGraphNode node : nodes) { visited.put(node, false); } List<List<Integer>> result = new ArrayList(); for (UndirectedGraphNode node : nodes) { if (!visited.get(node)) { find(node, visited, result); } } return result; } public static void find(UndirectedGraphNode node, Map<UndirectedGraphNode, Boolean> visited, List<List<Integer>> result) { List<Integer> now = new ArrayList(); Queue<UndirectedGraphNode> queue = new LinkedList(); visited.put(node, true); queue.offer(node); while (!queue.isEmpty()) { UndirectedGraphNode u = queue.poll(); now.add(u.label); for (UndirectedGraphNode v : u.neighbors) { if (visited.get(v) == false) { visited.put(v, true); queue.offer(v); } } } Collections.sort(now); result.add(now); } }
重点答案和思路:并查集。
/** * Definition for Undirected graph. * class UndirectedGraphNode { * int label; * ArrayList<UndirectedGraphNode> neighbors; * UndirectedGraphNode(int x) { label = x; neighbors = new ArrayList<UndirectedGraphNode>(); } * }; */ public class Solution { /** * @param nodes a array of Undirected graph node * @return a connected set of a Undirected graph */ public List<List<Integer>> connectedSet(ArrayList<UndirectedGraphNode> nodes) { // Write your code here HashSet<Integer> set = new HashSet(); for (UndirectedGraphNode n : nodes) { set.add(n.label); } UnionFind uf = new UnionFind(set); // build uf for (UndirectedGraphNode n : nodes) { for (UndirectedGraphNode nei : n.neighbors) { uf.union(n.label, nei.label); } } List<List<Integer>> res = new ArrayList(); HashMap<Integer, List<Integer>> allFather = new HashMap(); for (UndirectedGraphNode n : nodes) { int curFather = uf.compressedFind(n.label); if (!allFather.containsKey(curFather)) { allFather.put(curFather, new ArrayList()); } allFather.get(curFather).add(n.label); } for (List<Integer> list : allFather.values()) { res.add(list); } return res; } } class UnionFind { HashMap<Integer, Integer> father = new HashMap(); // initialization public UnionFind(HashSet<Integer> set) { for (Integer now : set) { father.put(now, now); } } // O(1) find public int compressedFind(int x) { int parent = father.get(x); while (parent != father.get(parent)) { parent = father.get(parent); } int temp = -1; int nextFa = x; while (nextFa != father.get(nextFa)) { temp = father.get(nextFa); father.put(nextFa, parent); nextFa = temp; } return parent; } // O(1) union public void union(int x, int y) { int xFa = compressedFind(x); int yFa = compressedFind(y); if (xFa != yFa) { father.put(xFa, yFa); } } }
2,找有向图的弱联通块。(与上面一题解法完全一样,只是有无Un)
有向图中:弱联通块:只要能够找到就行。强联通块要求必须相互能够找到(双向)。
/** * Definition for Undirected graph. * class DirectedGraphNode { * int label; * ArrayList<DirectedGraphNode> neighbors; * DirectedGraphNode(int x) { label = x; neighbors = new ArrayList<DirectedGraphNode>(); } * }; */ public class Solution { /** * @param nodes a array of Undirected graph node * @return a connected set of a Undirected graph */ public List<List<Integer>> connectedSet2(ArrayList<DirectedGraphNode> nodes) { // Write your code here HashSet<Integer> set = new HashSet(); for (DirectedGraphNode n : nodes) { set.add(n.label); } UnionFind uf = new UnionFind(set); // build uf for (DirectedGraphNode n : nodes) { for (DirectedGraphNode nei : n.neighbors) { uf.union(n.label, nei.label); } } List<List<Integer>> res = new ArrayList(); HashMap<Integer, List<Integer>> allFather = new HashMap(); for (DirectedGraphNode n : nodes) { int curFather = uf.compressedFind(n.label); if (!allFather.containsKey(curFather)) { allFather.put(curFather, new ArrayList()); } allFather.get(curFather).add(n.label); } for (List<Integer> list : allFather.values()) { res.add(list); } return res; } } class UnionFind { HashMap<Integer, Integer> father = new HashMap(); // initialization public UnionFind(HashSet<Integer> set) { for (Integer now : set) { father.put(now, now); } } // O(1) find public int compressedFind(int x) { int parent = father.get(x); while (parent != father.get(parent)) { parent = father.get(parent); } int temp = -1; int nextFa = x; while (nextFa != father.get(nextFa)) { temp = father.get(nextFa); father.put(nextFa, parent); nextFa = temp; } return parent; } // O(1) union public void union(int x, int y) { int xFa = compressedFind(x); int yFa = compressedFind(y); if (xFa != yFa) { father.put(xFa, yFa); } } }
3,找小岛数
Given a boolean 2D matrix, find the number of islands. Notice 0 is represented as the sea, 1 is represented as the island. If two 1 is adjacent, we consider them in the same island. We only consider up/down/left/right adjacent. Have you met this question in a real interview? Yes Example Given graph: [ [1, 1, 0, 0, 0], [0, 1, 0, 0, 1], [0, 0, 0, 1, 1], [0, 0, 0, 0, 0], [0, 0, 0, 0, 1] ]
答案和思路:首先遍历的过程中遇到是1(true)的时候就开始把他已经他周边的所有1变成0.然后result++。
总的思想就是遇到了1就开始移除,如果他周边有1那么接着移除。递归做这件事。
public class Solution { /** * @param grid a boolean 2D matrix * @return an integer */ static int[] dx = {1, 0, -1, 0}; static int[] dy = {0, 1, 0, -1}; public int numIslands(boolean[][] grid) { // Write your code here if (null == grid || grid.length == 0) { return 0; } int count = 0; for (int i = 0; i < grid.length; ++i) { for (int j = 0; j < grid[0].length; ++j) { if (grid[i][j]) { removeIsland(grid, i, j); count++; } } } return count; } public void removeIsland(boolean[][]grid, int x, int y) { grid[x][y] = false; int nextX = x; int nextY = y; for (int i = 0; i < 4; ++i) { nextX = x + dx[i]; nextY = y + dy[i]; if (nextX >= 0 && nextX < grid.length && nextY >= 0 && nextY < grid[0].length) { if (grid[nextX][nextY]) { removeIsland(grid, nextX, nextY); } } } } }
4,算小岛数II
岛屿的个数II 描述 笔记 数据 评测 给定 n,m,分别代表一个2D矩阵的行数和列数,同时,给定一个大小为 k 的二元数组A。起初,2D矩阵的行数和列数均为 0,即该矩阵中只有海洋。二元数组有 k 个运算符,每个运算符有 2 个整数 A[i].x, A[i].y,你可通过改变矩阵网格中的A[i].x],[A[i].y] 来将其由海洋改为岛屿。请在每次运算后,返回矩阵中岛屿的数量。 注意事项 0 代表海,1 代表岛。如果两个1相邻,那么这两个1属于同一个岛。我们只考虑上下左右为相邻。 您在真实的面试中是否遇到过这个题? Yes 样例 给定 n = 3, m = 3, 二元数组 A = [(0,0),(0,1),(2,2),(2,1)]. 返回 [1,1,2,2]. 标签 相关题目
答案和思路:用并查集来做。那么怎么区分每一个点,就是给他们编号为第几个(变成一维)。注意的地方就是,什么时候count--。应该使他们的父亲不同的时候才减。
/** * Definition for a point. * class Point { * int x; * int y; * Point() { x = 0; y = 0; } * Point(int a, int b) { x = a; y = b; } * } */ public class Solution { /** * @param n an integer * @param m an integer * @param operators an array of point * @return an integer array */ public int convertToId(int x, int y, int m) { return x * m + y; } public List<Integer> numIslands2(int n, int m, Point[] operators) { // Write your code here List<Integer> ans = new ArrayList<Integer>(); if (operators == null) { return ans; } int[][] island = new int[n][m]; int[] dx = {0, -1, 0, 1}; int[] dy = {1, 0, -1, 0}; UnionFind uf = new UnionFind(n, m); int count = 0; for (int i = 0; i < operators.length; ++i) { count++; int x = operators[i].x; int y = operators[i].y; if (island[x][y] != 1) { island[x][y] = 1; int id = convertToId(x, y, m); for (int j = 0; j < 4; ++j) { int nextX = x + dx[j]; int nextY = y + dy[j]; if (0 <= nextX && nextX < n && nextY >= 0 && nextY < m && island[nextX][nextY] == 1) { int nextId = convertToId(nextX, nextY, m); int fa = uf.compressedFind(id); int nextFa = uf.compressedFind(nextId); if (fa != nextFa) { count--; uf.union(id, nextId); } } } } ans.add(count); } return ans; } class UnionFind { HashMap<Integer, Integer> father = new HashMap(); // 初始化 UnionFind(int n, int m) { for (int i = 0; i < n; ++i) { for (int j = 0; j < m; ++j) { father.put(convertToId(i, j, m), convertToId(i, j, m)); } } } // O(n)复杂度的find // public int find(int x) { // int parent = father.get(x); // while (parent != father.get(parent)) { // parent = father.get(parent); // } // return parent; // } // O(1)复杂度的find public int compressedFind(int x) { int parent = father.get(x); while (parent != father.get(parent)) { parent = father.get(parent); } // 这里parent是他的最大祖先。那么,包括他以及中间的各种都直接设置为指道最大祖先。 int temp = -1; int xFa = x; while (xFa != father.get(xFa)) { temp = father.get(xFa); // 因为要把xFa的祖先设置为最大祖先了,所以,得先把踏上一级father记录下来。 father.put(xFa, parent); xFa = temp; } return parent; } // x,y有一条边。所以,如果他们的祖先不一样,那么就把他们随便谁和谁连起来 public void union(int x, int y) { int xFa = compressedFind(x); int yFa = compressedFind(y); if (xFa != yFa) { father.put(xFa, yFa); } } } }
5,图是否是树
给出 n 个节点,标号分别从 0 到 n - 1 并且给出一个 无向 边的列表 (给出每条边的两个顶点), 写一个函数去判断这张`无向`图是否是一棵树 注意事项 你可以假设我们不会给出重复的边在边的列表当中. 无向边 [0, 1] 和 [1, 0] 是同一条边, 因此他们不会同时出现在我们给你的边的列表当中。 您在真实的面试中是否遇到过这个题? Yes 样例 给出n = 5 并且 edges = [[0, 1], [0, 2], [0, 3], [1, 4]], 返回 true. 给出n = 5 并且 edges = [[0, 1], [1, 2], [2, 3], [1, 3], [1, 4]], 返回 false. 标签 相关题目
答案和树:
public class Solution { /** * @param n an integer * @param edges a list of undirected edges * @return true if it's a valid tree, or false */ class UnionFind { HashMap<Integer, Integer> father = new HashMap(); public UnionFind(int n) { for (int i = 0; i < n; ++i) { father.put(i, i); } } public int compressedFind(int x) { int parent = father.get(x); while (parent != father.get(parent)) { parent = father.get(parent); } return parent; } public void union(int x, int y) { int xFa = compressedFind(x); int yFa = compressedFind(y); if (xFa != yFa) { father.put(xFa, yFa); } } } public boolean validTree(int n, int[][] edges) { // Write your code here if (null == edges || edges.length == 0 || n == 0) { return true; } UnionFind uf = new UnionFind(n); for (int i = 0; i < edges.length; ++i) { for (int j = 0; j < edges[0].length; ++j) { if (uf.compressedFind(edges[i][0]) == uf.compressedFind(edges[i][1])) { return false; } } } return true; } }
6.XXOO区域围绕问题
被围绕的区域 描述 笔记 数据 评测 给一个二维的矩阵,包含 'X' 和 'O', 找到所有被 'X' 围绕的区域,并用 'X' 填充满。 您在真实的面试中是否遇到过这个题? Yes 样例 给出二维矩阵: X X X X X O O X X X O X X O X X 把被 'X' 围绕的区域填充之后变为: X X X X X X X X X X X X X O X X
答案和思路: // 思路:与其找哪些被包围,不如找没有被包围的。那么,导致没有被包围只有一种情况就是0在最外层
public class Solution { /** * @param board a 2D board containing 'X' and 'O' * @return void */ static int[] dx = {1, 0, -1, 0}; static int[] dy = {0, 1, 0, -1}; public void surroundedRegions(char[][] board) { // Write your code here // 思路:与其找哪些被包围,不如找没有被包围的。那么,导致没有被包围只有一种情况就是0在最外层。 if (null == board || board.length == 0) { return; } int m = board.length; int n = board[0].length; for (int i = 0; i < m; ++i) { if (board[i][0] == 'O') { bfsFind(board, i, 0); } if (board[i][n - 1] == 'O') { bfsFind(board, i, n - 1); } } for (int j = 0; j < n; ++j) { if (board[0][j] == 'O') { bfsFind(board, 0, j); } if (board[m - 1][j] == 'O') { bfsFind(board, m - 1, j); } } for (int i = 0; i < m; ++i) { for (int j = 0; j < n; ++j) { if (board[i][j] == '#') { board[i][j] = 'O'; } else { board[i][j] = 'X'; } } } } public static void bfsFind(char[][] board, int i, int j) { int m = board.length; int n = board[0].length; if (i >= m || j >= n || i < 0 || j < 0) { return; } board[i][j] = '#'; for (int k = 0; k < 4; ++k) { int nextX = i + dx[k]; int nextY = j + dy[k]; if (nextX >= 0 && nextX < m && nextY >= 0 && nextY < n && board[nextX][nextY] == 'O') { bfsFind(board, nextX, nextY); } } } }
7,实现trie树
实现 Trie 描述 笔记 数据 评测 实现一个 Trie,包含 insert, search, 和 startsWith 这三个方法。 注意事项 你可以假设所有的输入都是小写字母a-z。 您在真实的面试中是否遇到过这个题? Yes 样例 insert("lintcode") search("code") // return false startsWith("lint") // return true startsWith("linterror") // return false insert("linterror") search("lintcode) // return true startsWith("linterror") // return true
答案和思路:利用hashmap来做。
import java.util.HashMap; /** * Your Trie object will be instantiated and called as such: * Trie trie = new Trie(); * trie.insert("lintcode"); * trie.search("lint"); will return false * trie.startsWith("lint"); will return true */ // 思路:其实也就是建树的过程。用hashmap来建立的好处就是不需要开过多的没用的空间。那么他的每一枝就只需要往他的儿子里面 // put进去。 class TrieNode { HashMap<Character, TrieNode> children; boolean hasWord; TrieNode() { children = new HashMap<Character, TrieNode>(); hasWord = false; } } public class Trie { TrieNode root = new TrieNode(); // 虚根结点 public void insert(String word) { TrieNode now = root; for (int i = 0; i < word.length(); ++i) { Character c = word.charAt(i); if (!now.children.containsKey(c)) { now.children.put(c, new TrieNode()); } now = now.children.get(c); } now.hasWord = true; } public boolean search(String word) { TrieNode now = root; for (int i = 0; i < word.length(); ++i) { Character c = word.charAt(i); if (!now.children.containsKey(c)) { return false; } now = now.children.get(c); } return now.hasWord; } public boolean startsWith(String prefix) { TrieNode now = root; for (int i = 0; i < prefix.length(); ++i) { Character c = prefix.charAt(i); if (!now.children.containsKey(c)) { return false; } now = now.children.get(c); } return true; } }
8,单词搜索1
单词搜索 描述 笔记 数据 评测 给出一个二维的字母板和一个单词,寻找字母板网格中是否存在这个单词。 单词可以由按顺序的相邻单元的字母组成,其中相邻单元指的是水平或者垂直方向相邻。每个单元中的字母最多只能使用一次。 您在真实的面试中是否遇到过这个题? Yes 样例 给出board = [ "ABCE", "SFCS", "ADEE" ] word = "ABCCED", ->返回 true, word = "SEE",-> 返回 true, word = "ABCB", -> 返回 false.
答案和思路:利用bfs的方式来做。那么注意的地方就是:如果从他开始的地方,先把他给改变了一会再改回来。
public class Solution { /** * @param board: A list of lists of character * @param word: A string * @return: A boolean */ public boolean exist(char[][] board, String word) { if (null == board || board.length == 0 || word == null || word.length() == 0) { return false; } for (int i = 0; i < board.length; ++i) { for (int j = 0; j < board[0].length; ++j) { boolean result = find(board, i, j, word, 0); if (result) { // find it return true; } } } return false; } public static boolean find(char[][] board, int i, int j, String word, int start) { if (start == word.length()) { return true; } if (i < 0 || i >= board.length || j < 0 || j >= board[0].length || board[i][j] != word.charAt(start)) { return false; } board[i][j] = '#'; boolean result = find(board, i + 1, j, word, start + 1) || find(board, i - 1, j, word, start + 1) || find(board, i, j + 1, word, start + 1) ||find(board, i, j - 1, word, start + 1); board[i][j] = word.charAt(start); return result; } }
9,单词搜索II
单词搜索 II 描述 笔记 数据 评测 给出一个由小写字母组成的矩阵和一个字典。找出所有同时在字典和矩阵中出现的单词。一个单词可以从矩阵中的任意位置开始,可以向左/右/上/下四个相邻方向移动。 您在真实的面试中是否遇到过这个题? Yes 样例 给出矩阵: doaf agai dcan 和字典: {"dog", "dad", "dgdg", "can", "again"} 返回 {"dog", "dad", "can", "again"} dog: doaf agai dcan dad: doaf agai dcan can: doaf agai dcan again: doaf agai dcan
答案和思路:利用trie树来做。先把单词库建立好trie树。在找的过程中利用bfs来树里面找。如果找到,那么就存下来。这里就需要给树里面加上一个s用来记录这棵树到了这里的单词是什么,方便直接存入结果。
public class Solution { /** * @param board: A list of lists of character * @param words: A list of string * @return: A list of string */ class TrieNode { String s; boolean isString; HashMap<Character, TrieNode> subtree; public TrieNode() { // TODO Auto-generated constructor stub isString = false; subtree = new HashMap<Character, TrieNode>(); s = ""; } }; class TrieTree{ TrieNode root ; public TrieTree(TrieNode TrieNode) { root = TrieNode; } public void insert(String s) { TrieNode now = root; for (int i = 0; i < s.length(); i++) { if (!now.subtree.containsKey(s.charAt(i))) { now.subtree.put(s.charAt(i), new TrieNode()); } now = now.subtree.get(s.charAt(i)); } now.s = s; now.isString = true; } public boolean find(String s){ TrieNode now = root; for (int i = 0; i < s.length(); i++) { if (!now.subtree.containsKey(s.charAt(i))) { return false; } now = now.subtree.get(s.charAt(i)); } return now.isString ; } }; public int []dx = {1, 0, -1, 0}; public int []dy = {0, 1, 0, -1}; public void search(char[][] board, int x, int y, TrieNode root, ArrayList<String> ans) { if(root.isString == true) { if(!ans.contains(root.s)){ ans.add(root.s); } } if(x < 0 || x >= board.length || y < 0 || y >= board[0].length || board[x][y]==0 || root == null) return ; if(root.subtree.containsKey(board[x][y])){ for(int i = 0; i < 4; i++){ char now = board[x][y]; board[x][y] = 0; search(board, x+dx[i], y+dy[i], root.subtree.get(now), ans); board[x][y] = now; } } } public ArrayList<String> wordSearchII(char[][] board, ArrayList<String> words) { ArrayList<String> ans = new ArrayList<String>(); TrieTree tree = new TrieTree(new TrieNode()); for(String word : words){ tree.insert(word); } String res = ""; for(int i = 0; i < board.length; i++){ for(int j = 0; j < board[i].length; j++){ search(board, i, j, tree.root, ans); } } return ans; // write your code here } }
10,增加单词和查询单词。包含正则项。
设计一个包含下面两个操作的数据结构:addWord(word), search(word) addWord(word)会在数据结构中添加一个单词。而search(word)则支持普通的单词查询或是只包含.和a-z的简易正则表达式的查询。 一个 . 可以代表一个任何的字母。 注意事项 你可以假设所有的单词都只包含小写字母 a-z。 您在真实的面试中是否遇到过这个题? Yes 样例 addWord("bad") addWord("dad") addWord("mad") search("pad") // return false search("bad") // return true search(".ad") // return true search("b..") // return true
答案和思路:难点就在于有正则项,所以,要再写一个find函数,通过控制单词index来弄。
import java.util.HashMap; import java.util.Map; class TrieNode { HashMap<Character, TrieNode> children; boolean hasWord; public TrieNode() { children = new HashMap<Character, TrieNode>(); hasWord = false; } } public class WordDictionary { // Adds a word into the data structure. TrieNode root = new TrieNode(); public void addWord(String word) { // Write your code here TrieNode now = root; for (int i = 0; i < word.length(); ++i) { Character c = word.charAt(i); if (!now.children.containsKey(c)) { now.children.put(c, new TrieNode()); } now = now.children.get(c); } now.hasWord = true; } // Returns if the word is in the data structure. A word could // contain the dot character '.' to represent any one letter. public static boolean find(String word, int index, TrieNode now) { if (index == word.length()) { if (now.children.size() == 0) { return true; } else { return false; } } Character c = word.charAt(index); if (now.children.containsKey(c)) { if (index == word.length() - 1 && now.children.get(c).hasWord) { return true; } return find(word, index + 1, now.children.get(c)); } else if (c == '.') { boolean result = false; for (Map.Entry<Character, TrieNode> child : now.children.entrySet()) { if (index == word.length() - 1 && child.getValue().hasWord) { return true; } if (find(word, index + 1, child.getValue())) { return true; } } return result; } else { return false; } } public boolean search(String word) { // Write your code here return find(word, 0, root); } } // Your WordDictionary object will be instantiated and called as such: // WordDictionary wordDictionary = new WordDictionary(); // wordDictionary.addWord("word"); // wordDictionary.search("pattern");
11,数飞机。
数飞机
描述
笔记
数据
评测
给出飞机的起飞和降落时间的列表,用 interval 序列表示. 请计算出天上同时最多有多少架飞机?
注意事项
如果多架飞机降落和起飞在同一时刻,我们认为降落有优先权。
您在真实的面试中是否遇到过这个题? Yes
样例
对于每架飞机的起降时间列表:[[1,10],[2,3],[5,8],[4,7]], 返回3。
答案和思路:利用sweep-line来扫描。那么有一点区别就是要对她进行排序。开一个class Point来辅助着弄。
/** * Definition of Interval: * public classs Interval { * int start, end; * Interval(int start, int end) { * this.start = start; * this.end = end; * } */ class Point { int time; int flag; //标记起飞还是降落的 Point(int t, int s) { this.time = t; this.flag = s; } } class Solution { /** * @param intervals: An interval array * @return: Count of airplanes are in the sky. */ // public int countOfAirplanes(List<Interval> airplanes) { // // write your code here // } public int countOfAirplanes(List<Interval> airplanes) { List<Point> list = new ArrayList(); for (Interval i : airplanes) { list.add(new Point(i.start, 1)); list.add(new Point(i.end, 0)); } Collections.sort(list, new Comparator<Point>() { public int compare(Point p1, Point p2) { if (p1.time == p2.time) { return p1.flag - p2.flag; } else { return p1.time - p2.time; } } }); int count = 0; int ans = 0; for (Point p : list) { if (p.flag == 1) { count++; } else { count--; } ans = Math.max(ans, count); } return ans; } }
课前一遍,课后一遍。做错的打红。第二次接着做。
====================================================
第一周:
1,two sum。
Two Sum Description Notes Testcase Judge Given an array of integers, find two numbers such that they add up to a specific target number. The function twoSum should return indices of the two numbers such that they add up to the target, where index1 must be less than index2. Please note that your returned answers (both index1 and index2) are NOT zero-based. Notice You may assume that each input would have exactly one solution Have you met this question in a real interview? Yes Example numbers=[2, 7, 11, 15], target=9 return [1, 2]
答案和解析:利用HashMap来做题。关键点注意不不是zero-based.存进去的是需要的他的组合值。
public class Solution { /* * @param numbers : An array of Integer * @param target : target = numbers[index1] + numbers[index2] * @return : [index1 + 1, index2 + 1] (index1 < index2) */ public int[] twoSum(int[] numbers, int target) { if (null == numbers || numbers.length == 0) { return null; } HashMap<Integer, Integer> map = new HashMap(); for (int i = 0; i < numbers.length; ++i) { if (map.get(numbers[i]) != null) { int[] result = {map.get(numbers[i]), i + 1}; return result; } else { map.put(target - numbers[i], i + 1); } } return null; } }
2,two sum II。
Two Sum II Description Notes Testcase Judge Given an array of integers, find how many pairs in the array such that their sum is bigger than a specific target number. Please return the number of pairs. Have you met this question in a real interview? Yes Example Given numbers = [2, 7, 11, 15], target = 24. Return 1. (11 + 15 is the only pair)
答案和思路:先对它进行排序。然后用left, right来看right前面有多少个能和他组合大于target的。
public class Solution { /** * @param nums: an array of integer * @param target: an integer * @return: an integer */ public int twoSum2(int[] nums, int target) { // Write your code here if (nums == null || nums.length == 0) { return 0; } Arrays.sort(nums); int ans = 0; int left = 0; int right = nums.length - 1; int result = 0; while (left < right) { if (nums[left] + nums[right] > target) { result += right - left; right--; } else { left++; } } return result; } }
3,triangle count
Triangle Count Description Notes Testcase Judge Given an array of integers, how many three numbers can be found in the array, so that we can build an triangle whose three edges length is the three numbers that we find? Have you met this question in a real interview? Yes Example Given array S = [3,4,6,7], return 3. They are: [3,4,6] [3,6,7] [4,6,7] Given array S = [4,4,4,4], return 4. They are: [4(1),4(2),4(3)] [4(1),4(2),4(4)] [4(1),4(3),4(4)] [4(2),4(3),4(4)]
答案和思路:还是依然利用左右指针对撞来做。
public class Solution { /** * @param S: A list of integers * @return: An integer */ public int triangleCount(int[] S) { if (null == S || S.length == 0) { return 0; } int result = 0; Arrays.sort(S); for (int i = 0; i < S.length; ++i) { int left = 0; int right = i - 1; while (left < right) { if (S[left] + S[right] > S[i]) { result += right - left; right--; } else { left++; } } } return result; } }
4,--数组里找前k大元素
第k大元素 描述 笔记 数据 评测 在数组中找到第k大的元素 注意事项 你可以交换数组中的元素的位置 您在真实的面试中是否遇到过这个题? Yes 样例 给出数组 [9,3,2,4,8],第三大的元素是 4 给出数组 [1,2,3,4,5],第一大的元素是 5,第二大的元素是 4,第三大的元素是 3,以此类推
答案和思路:用快排来做。
public class Solution { public int findKthLargest(int[] nums, int k) { if (nums == null || nums.length == 0 || k <= 0) { return 0; } return helper(nums, 0, nums.length - 1, nums.length - k + 1); } private static int helper(int[] nums, int left, int right, int k) { if (left == right) { return nums[left]; } int position = partition(nums, left, right); if (position + 1 == k) { return nums[position]; } else if (position + 1 < k) { return helper(nums, position + 1, right, k); } else { return helper(nums, left, position - 1, k); } } private static int partition(int[] nums, int left, int right) { int first = left; int last = right; int key = nums[first]; while (first < last) { while (first < last && nums[last] >= key) { last--; } nums[first] = nums[last]; while (first < last && nums[first] <= key) { first++; } nums[last] = nums[first]; } nums[first] = key; return first; } }
用堆来做。复杂度比较高。
class Solution { /* * @param k : description of k * @param nums : array of nums * @return: description of return */ public int kthLargestElement(int k, int[] nums) { // write your code here PriorityQueue<Integer> p = new PriorityQueue(); for (int i = 0; i < nums.length; ++i) { p.add(nums[i]); if (p.size() > k) { p.poll(); } } return p.peek(); } };
如果需要找前k小。那么就重写compare。
import java.util.Comparator; import java.util.PriorityQueue; public class Main { public static void main (String[] args) { PriorityQueue<String> p = new PriorityQueue(2, new Comparator<String>() { public int compare(String a, String b) { return -1 * a.compareTo(b); } }); p.add("d"); p.add("b"); p.add("a"); p.add("c"); System.out.println(p); p.poll(); System.out.println(p); p.poll(); System.out.println(p); } }
5,二维数组找前k大元素。
在N个数组中找到第K大元素 注意事项 你可以交换数组中的元素 您在真实的面试中是否遇到过这个题? Yes 样例 n = 2 数组 [[9,3,2,4,7],[1,2,3,4,8]], 第三大元素是 7. n = 2 数组 [[9,3,2,4,8],[1,2,3,4,2]], 最大数是 9, 次大数是 8, 第三大数是 7
答案和思路:
public class Solution { /** * @param arrays a list of array * @param k an integer * @return an integer, K-th largest element in N arrays */ public int KthInArrays(int[][] arrays, int k) { // Write your code here if (null == arrays || arrays.length == 0) { return -1; } PriorityQueue<Integer> p = new PriorityQueue(); for (int i = 0; i < arrays.length; ++i) { for (int j = 0; j < arrays[i].length; ++j) { p.add(arrays[i][j]); if (p.size() > k) { p.poll(); } } } return p.peek(); } }
6,建立线段树:
线段树是一棵二叉树,他的每个节点包含了两个额外的属性start和end用于表示该节点所代表的区间。start和end都是整数,并按照如下的方式赋值: 根节点的 start 和 end 由 build 方法所给出。 对于节点 A 的左儿子,有 start=A.left, end=(A.left + A.right) / 2。 对于节点 A 的右儿子,有 start=(A.left + A.right) / 2 + 1, end=A.right。 如果 start 等于 end, 那么该节点是叶子节点,不再有左右儿子。 实现一个 build 方法,接受 start 和 end 作为参数, 然后构造一个代表区间 [start, end] 的线段树,返回这棵线段树的根。 您在真实的面试中是否遇到过这个题? Yes 说明 线段树(又称区间树), 是一种高级数据结构,他可以支持这样的一些操作: 查找给定的点包含在了哪些区间内 查找给定的区间包含了哪些点 见百科: 线段树 区间树 样例 比如给定start=1, end=6,对应的线段树为: [1, 6] / \ [1, 3] [4, 6] / \ / \ [1, 2] [3,3] [4, 5] [6,6] / \ / \ [1,1] [2,2] [4,4] [5,5]
答案和思路:注意判断start和end的关系。
/** * Definition of SegmentTreeNode: * public class SegmentTreeNode { * public int start, end; * public SegmentTreeNode left, right; * public SegmentTreeNode(int start, int end) { * this.start = start, this.end = end; * this.left = this.right = null; * } * } */ public class Solution { /** *@param start, end: Denote an segment / interval *@return: The root of Segment Tree */ public SegmentTreeNode build(int start, int end) { // write your code here // error:要判断如果start > end if (start > end) { return null; } SegmentTreeNode root = new SegmentTreeNode(start, end); if (root.start != root.end) { root.left = build(root.start, (root.start + root.end) / 2); root.right = build((root.start + root.end) / 2 + 1, root.end); } return root; } }
7,建立线段树II(含有max)
线段树的构造 II 描述 笔记 数据 评测 线段树是一棵二叉树,他的每个节点包含了两个额外的属性start和end用于表示该节点所代表的区间。start和end都是整数,并按照如下的方式赋值: 根节点的 start 和 end 由 build 方法所给出。 对于节点 A 的左儿子,有 start=A.left, end=(A.left + A.right) / 2。 对于节点 A 的右儿子,有 start=(A.left + A.right) / 2 + 1, end=A.right。 如果 start 等于 end, 那么该节点是叶子节点,不再有左右儿子。 对于给定数组设计一个build方法,构造出线段树 您在真实的面试中是否遇到过这个题? Yes 说明 wiki: Segment Tree Interval Tree 样例 给出[3,2,1,4],线段树将被这样构造 [0, 3] (max = 4) / \ [0, 1] (max = 3) [2, 3] (max = 4) / \ / \ [0, 0](max = 3) [1, 1](max = 2)[2, 2](max = 1) [3, 3] (max = 4)
答案和思路:注意利用好left,right与root的关系来处理max。
/** * Definition of SegmentTreeNode: * public class SegmentTreeNode { * public int start, end, max; * public SegmentTreeNode left, right; * public SegmentTreeNode(int start, int end, int max) { * this.start = start; * this.end = end; * this.max = max * this.left = this.right = null; * } * } */ public class Solution { /** *@param A: a list of integer *@return: The root of Segment Tree */ public SegmentTreeNode build(int[] A) { // write your code here return build(A, 0, A.length - 1); } public SegmentTreeNode build(int[] A, int start, int end) { if (start > end) { return null; } SegmentTreeNode root = new SegmentTreeNode(start, end, A[start]); if (start != end) { int mid = (start + end) / 2; root.left = build(A, start, mid); root.right = build(A, mid + 1, end); if (root.left != null && root.left.max > root.max) { root.max = root.left.max; } if (root.right != null && root.right.max > root.max) { root.max = root.right.max; } } return root; } }
8,线段树的查询
线段树的查询 描述 笔记 数据 评测 对于一个有n个数的整数数组,在对应的线段树中, 根节点所代表的区间为0-n-1, 每个节点有一个额外的属性max,值为该节点所代表的数组区间start到end内的最大值。 为SegmentTree设计一个 query 的方法,接受3个参数root, start和end,线段树root所代表的数组中子区间[start, end]内的最大值。 注意事项 在做此题之前,请先完成 线段树构造 这道题目。 您在真实的面试中是否遇到过这个题? Yes 样例 对于数组 [1, 4, 2, 3], 对应的线段树为: [0, 3, max=4] / \ [0,1,max=4] [2,3,max=3] / \ / \ [0,0,max=1] [1,1,max=4] [2,2,max=2], [3,3,max=3] query(root, 1, 1), return 4 query(root, 1, 2), return 4 query(root, 2, 3), return 3 query(root, 0, 2), return 4
答案和思路:
其实就是看左右区间是否能够成为他的字区间。
/** * Definition of SegmentTreeNode: * public class SegmentTreeNode { * public int start, end, max; * public SegmentTreeNode left, right; * public SegmentTreeNode(int start, int end, int max) { * this.start = start; * this.end = end; * this.max = max * this.left = this.right = null; * } * } */ public class Solution { /** *@param root, start, end: The root of segment tree and * an segment / interval *@return: The maximum number in the interval [start, end] */ public int query(SegmentTreeNode root, int start, int end) { // write your code here if (root == null || start > end) { return -1; } if (root.start == start && root.end == end) { return root.max; } int leftMax = Integer.MIN_VALUE; int rightMax = Integer.MIN_VALUE; int mid = (root.start + root.end) / 2; if (mid >= start) { leftMax = query(root.left, start, Math.min(end, mid)); } if (mid + 1 <= end) { rightMax = query(root.right, Math.max(mid + 1, start), end); } return Math.max(leftMax, rightMax); } }
9,线段树的修改:
对于一棵 最大线段树, 每个节点包含一个额外的 max 属性,用于存储该节点所代表区间的最大值。 设计一个 modify 的方法,接受三个参数 root、 index 和 value。该方法将 root 为跟的线段树中 [start, end] = [index, index] 的节点修改为了新的 value ,并确保在修改后,线段树的每个节点的 max 属性仍然具有正确的值。 注意事项 在做此题前,最好先完成线段树的构造和 线段树查询这两道题目。 您在真实的面试中是否遇到过这个题? Yes 样例 对于线段树: [1, 4, max=3] / \ [1, 2, max=2] [3, 4, max=3] / \ / \ [1, 1, max=2], [2, 2, max=1], [3, 3, max=0], [4, 4, max=3] 如果调用 modify(root, 2, 4), 返回: [1, 4, max=4] / \ [1, 2, max=4] [3, 4, max=3] / \ / \ [1, 1, max=2], [2, 2, max=4], [3, 3, max=0], [4, 4, max=3] 或 调用 modify(root, 4, 0), 返回: [1, 4, max=2] / \ [1, 2, max=2] [3, 4, max=0] / \ / \ [1, 1, max=2], [2, 2, max=1], [3, 3, max=0], [4, 4, max=0]
答案和思路:注意的就是,如果遇到最后一个了,那么就把value给他的max。然后return出来。那么递归就会一直往上退。退的时候把root.max = Math.max(root.right.max, root.left.max)。
/** * Definition of SegmentTreeNode: * public class SegmentTreeNode { * public int start, end, max; * public SegmentTreeNode left, right; * public SegmentTreeNode(int start, int end, int max) { * this.start = start; * this.end = end; * this.max = max * this.left = this.right = null; * } * } */ public class Solution { /** *@param root, index, value: The root of segment tree and *@ change the node's value with [index, index] to the new given value *@return: void */ public void modify(SegmentTreeNode root, int index, int value) { // write your code here if (null == root) { return; } if (root.start == index && root.end == index) { root.max = value; return; } int mid = (root.start + root.end) / 2; if (root.start <= index && index <= mid) { modify(root.left, index, value); } if (mid + 1 <= index && index <= root.end) { modify(root.right, index, value); } root.max = Math.max(root.left.max, root.right.max); } }
10,数组区间最小值查找
给定一个整数数组(下标由 0 到 n-1,其中 n 表示数组的规模),以及一个查询列表。每一个查询列表有两个整数 [start, end]。 对于每个查询,计算出数组中从下标 start 到 end 之间的数的最小值,并返回在结果列表中。 注意事项 在做此题前,建议先完成以下三道题 线段树的构造, 线段树的查询 及 线段树的修改。 您在真实的面试中是否遇到过这个题? Yes 样例 对于数组 [1,2,7,8,5], 查询 [(1,2),(0,4),(2,4)],返回 [2,1,5] 挑战 标签 相关题目
答案和思路:利用线段树来做。
/** * Definition of Interval: * public classs Interval { * int start, end; * Interval(int start, int end) { * this.start = start; * this.end = end; * } */ public class Solution { /** *@param A, queries: Given an integer array and an query list *@return: The result list */ public ArrayList<Integer> intervalMinNumber(int[] A, ArrayList<Interval> queries) { // write your code here if (A == null || queries == null) { return null; } ArrayList<Integer> ans = new ArrayList(); SegmentTreeNode root = build(0, A.length - 1, A); for (Interval in : queries) { ans.add(query(root, in.start, in.end)); } return ans; } // build segmentTree public static SegmentTreeNode build(int start, int end, int[] a) { if (start > end) { return null; } SegmentTreeNode root = new SegmentTreeNode(start, end, Integer.MAX_VALUE); if (start == end) { root.min = a[start]; } else { int mid = (start + end) / 2; root.left = build(start, mid, a); root.right = build(mid + 1, end, a); root.min = Math.min(root.left.min, root.right.min); } return root; } // query public static int query(SegmentTreeNode root, int start, int end) { if (root.start == start && root.end == end) { return root.min; } int mid = (root.start + root.end) / 2; int leftMin = Integer.MAX_VALUE; int rightMin = Integer.MAX_VALUE; if (start <= mid) { leftMin = query(root.left, start, Math.min(mid, end)); } if (mid + 1 <= end) { rightMin = query(root.right, Math.max(start, mid + 1), end); } return Math.min(leftMin, rightMin); } } class SegmentTreeNode { int start; int end; int min; SegmentTreeNode left, right; SegmentTreeNode(int s, int e, int m) { start = s; end = e; min = m; } }