3.双端队列html
There is an integer matrix which has the following features:java
The numbers in adjacent positions are different.
The matrix has n rows and m columns.
For all i < m, A[0][i] < A[1][i] && A[n – 2][i] > A[n – 1][i].
For all j < n, A[j][0] < A[j][1] && A[j][m – 2] > A[j][m – 1].
We define a position P is a peek if:c++
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]
Find a peak element in this matrix. Return the index of the peak.git
Noticegithub
The matrix may contains multiple peeks, find any of them.面试
Have you met this question in a real interview? Yes
Example
Given a matrix:算法
[
[1 ,2 ,3 ,6 ,5],
[16,41,23,22,6],
[15,17,24,21,7],
[14,18,19,20,10],
[13,14,11,10,9]
]
return index of 41 (which is [1,1]) or index of 24 (which is [2,2])windows
这道题我想到的是随便找个位置当起点,而后往高处爬.
我开始以为这种算法的时间复杂度为m+n, 后来以为本身有点脑残...
这种作法最坏状况下的时间复杂度为0(n^2).
状况以下:数组
XXXXXXXXXXXXXXXXXXXXXXXXXXXXX X 1 2 3 4 5 6 7 8 9 10 11XXXX X 19 18 17 16 15 14 13 12XXXX X 20 21 22 23 24 25 26 27XXXX ......
呈蛇形排列的状况.数据结构
而后呢咱们想一想能不能相似于find peak I 里面的二分去掉一半?
咱们首先改一下栗子...改为以下,方便理解:
[ 1, 2, 3, 6, 5], [16, 41, 23, 22, 6], [15, 17, 16, 21, 7], [14, 18, 19, 20, 10], [13, 14, 11, 10, 9]
咱们先假设取中间的一行:
[15, 17, 16, 21, 7]
中间一行有两个极大值:17和21, 咱们目标是:看看找到极值,而后极值往高了爬,能不能甩掉身后小的那半边矩阵, 并保证在剩下的通常里仍然有要找的答案.
假设咱们先选择17,而后上下找比它大的,意图删掉带有17的那半边:
若是走到41,是ok的...
可是若是走到18呢,18->19->20->21,又走回原来的第3行了...阿西吧,行不通.
而后咱们试试看看最大值,21呢?由于21是第三行的最大值, 因此咱们若是找个一个比21大的, 就是第二行第四列的22. 咱们发现, 若是从21走到22, 在从22找比它大的往高爬, 永远都不会爬回21所在的第三行, 因此我成功甩掉了第四五行…下面咱们来肯定在剩下的一半里面, 必定有咱们要找的答案:
题目中的条件:
For all i < m, A[0][i] < A[1][i] && A[n – 2][i] > A[n – 1][i].
For all j < n, A[j][0] < A[j][1] && A[j][m – 2] > A[j][m – 1].
能够保证四周的一圈是山脚,比中间的要小, 这样才能肯定中间必定有个极值峰, 如pic4.1图左, 当删掉21所在的第三行下面的第四五行, 图变成了pic4.1的右图形式,保证了峰值依然存在:
class Solution { /** * @param A: An integer matrix * @return: The index of the peak */ public List find(int x1, int x2, int y1, int y2, int[][] A, boolean flag) { if (flag) { int mid = x1 + (x2 - x1) / 2; int index = y1; for (int i = y1; i A[mid][index]) index = i; if (A[mid - 1][index] > A[mid][index]) return find(x1, mid - 1, y1, y2, A, !flag); else if (A[mid + 1][index] > A[mid][index]) return find(mid + 1, x2, y1, y2, A, !flag); else return new ArrayList(Arrays.asList(mid, index)); } else { int mid = y1 + (y2 - y1) / 2; int index = x1; for (int i = x1; i A[index][mid]) index = i; if (A[index][mid - 1] > A[index][mid]) return find(x1, x2, y1, mid - 1, A, !flag); else if (A[index][mid + 1] > A[index][mid]) return find(x1, x2, mid + 1, y2, A, !flag); else return new ArrayList(Arrays.asList(index, mid)); } } public List findPeakII(int[][] A) { // write your code here int n = A.length; int m = A[0].length; return find(1, n - 2, 1, m - 2, A, true); } }
这道题的时间复杂度是:
T(n) = O(n) + O(n/2) + T(n/2)
最后的时间仍是O(3n), 即为O(n).
每每没有给你一个数组让你二分, 而是找到知足某个条件的最大或者最小值
由于找答案有两种思路:
而神马是有二分性呢:
就是一个区间, 中间某个点画条线, 线一边的所有知足条件, 另外一边的所有不知足:
-> 知足 ->| |____________|____________|
经过猜值判断是否知足题意不对去搜索可能解
模板以下:
Implement int sqrt(int x).
Compute and return the square root of x.
Have you met this question in a real interview? Yes
Example
sqrt(3) = 1
sqrt(4) = 2
sqrt(5) = 2
sqrt(10) = 3
class Solution { /** * @param x: An integer * @return: The sqrt of x */ public int sqrt(int x) { // find the last number which square of it <= x long start = 1, end = x; while (start + 1 < end) { long mid = start + (end - start) / 2; if (mid * mid <= x) { start = mid; } else { end = mid; } } if (end * end <= x) { return (int) end; } return (int) start; } }
Implement double sqrt(double x) and x >= 0.
Compute and return the square root of x.
Notice
You do not care about the accuracy of the result, we will help you to output results.
Have you met this question in a real interview? Yes
Example
Given n = 2 return 1.41421356
这道题有个浮点精度问题, 在计算机里面, 两个树若是不相等,可是由于精度问题, 计算机会判断他们相等. 计算机如何判断两个浮点数y和z相等呢?
if (y ==z) 对于计算机来讲是 if (abs(y-z)<eps), 这个eps是计算机设定的很小的值. 那么对于计算机来讲:
y = 1.0
z = 1.000000000000000000000000000001
是相等的, 虽然他们原本是不相等的.
这道题的eps肿么肯定呢?
答案就是: 和面试官商量…
public class Solution { /** * @param x a double * @return the square root of x */ public double sqrt(double x) { // Write your code here double left = 0.0; double right = x; double eps = 1e-12; if(right eps) { // 二分浮点数 和二分整数不一样 // 通常都有一个精度的要求 譬如这题就是要求小数点后八位 // 也就是只要咱们二分的结果达到了这个精度的要求就能够 // 因此 须要让 right 和 left 小于一个咱们事先设定好的精度值 eps // 通常eps的设定1e-8,由于这题的要求是到1e-8,因此我把精度调到了1e-12 // 最后 选择 left 或 right 做为一个结果便可 double mid = (right + left) / 2; if(mid * mid < x) { left = mid; } else { right = mid; } } return left; } }
Given n pieces of wood with length L[i] (integer array). Cut them into small pieces to guarantee you could have equal or more than k pieces with the same length. What is the longest length you can get from the n pieces of wood? Given L & k, return the maximum length of the small pieces.
Notice
You couldn’t cut wood into float length.
If you couldn’t get >= k pieces, return 0.
Have you met this question in a real interview? Yes
Example
For L=[232, 124, 456], k=7, return 114.
这道题能够用heap来作, 同时呢, 也能够二分试答案:
public class Solution { /** *@param L: Given n pieces of wood with length L[i] *@param k: An integer *return: The maximum length of the small pieces. */ public int woodCut(int[] L, int k) { int max = 0; for (int i = 0; i = k) { return end; } if (count(L, start) >= k) { return start; } return 0; } private int count(int[] L, int length) { int sum = 0; for (int i = 0; i < L.length; i++) { sum += L[i] / length; } return sum; } }
这道题若是范围是[1, INT_MAX]的话, 最多咱们能找多少次呢?
就是log(max -min)次. 那么这个数大约是多大呢?
31, 由于int是四个byte,就是最多32位, 32位的最大的整数是2^31-1
,因此须要找log(2^31-1-1)次, 就是大约31次. 那么时间复杂度是多少呢?
log(max-min) * O(n)
Given an array nums containing n + 1 integers where each integer is between 1 and n (inclusive), prove that at least one duplicate number must exist. Assume that there is only one duplicate number, find the duplicate one.
Notice
You must not modify the array (assume the array is read only).
You must use only constant, O(1) extra space.
Your runtime complexity should be less than O(n^2).
There is only one duplicate number in the array, but it could be repeated more than once.
Have you met this question in a real interview? Yes
Example
Given nums = [5,5,4,3,2,1] return 5
Given nums = [5,4,4,3,2,1] return 4
由于一共有n+1个数,因此若是一个一个找的话,对于
[5,5,4,3,2,1] 来讲呢?咱们一个一个试:
1的时候 小于等于1的 1个 2的时候 小于等于2的 2个 3的时候 小于等于3的 3个 4的时候 小于等于4的 4个 5的时候 小于等于5的 6个
咱们发现, 对于数字n来讲,若是小于等于它的数字个数小于等于n, 那么它和它以前木有重复的. 反之有重复.
对于这种for的查找, 答案有二分性, 咱们要想到神马?
public class Solution { /** * @param nums an array containing n + 1 integers which is between 1 and n * @return the duplicate one */ public int findDuplicate(int[] nums) { // Write your code here int start = 1; int end = nums.length - 1; while(start + 1 < end) { int mid = start + (end - start) / 2; if (check_smaller_num(mid, nums) <= mid) { start = mid; } else { end = mid; } } if (check_smaller_num(start, nums) <= start) { return end; } return start; } public int check_smaller_num(int mid, int[] nums) { int cnt = 0; for(int i = 0; i < nums.length; i++){ if(nums[i] <= mid){ cnt++; } } return cnt; } } // 映射法 public class Solution { /** * @param nums an array containing n + 1 integers which is between 1 and n * @return the duplicate one */ public int findDuplicate(int[] nums) { // Write your code here if (nums.length <= 1) return -1; int slow = nums[0]; int fast = nums[nums[0]]; while (slow != fast) { slow = nums[slow]; fast = nums[nums[fast]]; } fast = 0; while (fast != slow) { fast = nums[fast]; slow = nums[slow]; } return slow; } }
知足条件和不知足条件的混合:
-> 不知足->知足 |____________|____________|
Given an interval list which are flying and landing time of the flight. How many airplanes are on the sky at most?
Notice
If landing and flying happens at the same time, we consider landing should happen at first.
Have you met this question in a real interview? Yes
Example
For interval list
[
[1,10],
[2,3],
[5,8],
[4,7]
]
这道题呢, 和起点终点的大小有关.
可是有个问题, 若是按起点排序呢, 终点无法处理.
若是按终点排序呢, 起点又无法处理.
区间类的问题, 咱们要想扫描线.
这道题咱们能够怎么作?
for一遍1 2 3 4 5 6 7 8 9 10的时间段, 找当时天空中一共有几架飞机, 而后取最大
本质是这样的, 如图4.2所示:
咱们有个时间轴, 每一个时间咱们画一条铅笔的总线, 看各个时间的这条总线和横线的区间交点最可能是几个.
如今咱们把起点和终点拆开记录,T表明起飞, F表明降落:
1 T 10 F 2 T 3 F 5 T 8 F 4 T 7 F
排序而后依次遍历这些时间节点, 记录count:
1 count++ 2 count++ 3 count-- ......
class Point{ int time; int flag; Point(int t, int s){ this.time = t; this.flag = s; } public static Comparator PointComparator = new Comparator(){ public int compare(Point p1, Point p2){ if(p1.time == p2.time) return p1.flag - p2.flag; else return p1.time - p2.time; } }; } class Solution { /** * @param intervals: An interval array * @return: Count of airplanes are in the sky. */ public int countOfAirplanes(List airplanes) { List list = new ArrayList(airplanes.size()*2); for(Interval i : airplanes){ list.add(new Point(i.start, 1)); list.add(new Point(i.end, 0)); } Collections.sort(list,Point.PointComparator ); int count = 0, ans = 0; for(Point p : list){ if(p.flag == 1) count++; else count--; ans = Math.max(ans, count); } return ans; } }
Given N buildings in a x-axis,each building is a rectangle and can be represented by a triple (start, end, height),where start is the start position on x-axis, end is the end position on x-axis and height is the height of the building. Buildings may overlap if you see them from far away,find the outline of them。
An outline can be represented by a triple, (start, end, height), where start is the start position on x-axis of the outline, end is the end position on x-axis and height is the height of the outline.
Notice
Please merge the adjacent outlines if they have the same height and make sure different outlines cant overlap on x-axis.
Example
Given 3 buildings:
[ [1, 3, 3], [2, 4, 4], [5, 6, 1] ]
The outlines are:
[ [1, 2, 3], [2, 4, 4], [5, 6, 1] ]
图示参考:
https://briangordon.github.io/2014/08/the-skyline-problem.html
这是一道扫描线和其它结合的题:
区间问题, 若是按起点和终点排序解决不了, 那咱们要想到扫描线. 而扫描线由于须要排序, 因此时间复杂度通常是O(nlogn).
这道题举个栗子:
[ [1, 3, 2], [2, 4, 3], [5, 6, 2] ]
就要拆成:
1 T 2 3 F 2 2 T 3 4 F 3 5 T 2 6 F 2
sweep line + heap 由于要删除, 因此c++要用heap
import java.util.*; public class Solution { class HashHeap { ArrayList heap; String mode; int size_t; HashMap hash; class Node { public Integer id; public Integer num; Node(Node now) { id = now.id; num = now.num; } Node(Integer first, Integer second) { this.id = first; this.num = second; } } public HashHeap(String mod) { // TODO Auto-generated constructor stub heap = new ArrayList(); mode = mod; hash = new HashMap(); size_t = 0; } public int peek() { return heap.get(0); } public int size() { return size_t; } public Boolean isEmpty() { return (heap.size() == 0); } int parent(int id) { if (id == 0) { return -1; } return (id - 1) / 2; } int lson(int id) { return id * 2 + 1; } int rson(int id) { return id * 2 + 2; } boolean comparesmall(int a, int b) { if (a 0) { siftdown(0); } } else { hash.put(now, new Node(0, hashnow.num - 1)); } return now; } public void add(int now) { size_t++; if (hash.containsKey(now)) { Node hashnow = hash.get(now); hash.put(now, new Node(hashnow.id, hashnow.num + 1)); } else { heap.add(now); hash.put(now, new Node(heap.size() - 1, 1)); } siftup(heap.size() - 1); } public void delete(int now) { size_t--; Node hashnow = hash.get(now); int id = hashnow.id; int num = hashnow.num; if (hashnow.num == 1) { swap(id, heap.size() - 1); hash.remove(now); heap.remove(heap.size() - 1); if (heap.size() > id) { siftup(id); siftdown(id); } } else { hash.put(now, new Node(id, num - 1)); } } void siftup(int id) { while (parent(id) > -1) { int parentId = parent(id); if (comparesmall(heap.get(parentId), heap.get(id)) == true) { break; } else { swap(id, parentId); } id = parentId; } } void siftdown(int id) { while (lson(id) = heap.size() || (comparesmall(heap.get(leftId), heap.get(rightId)) == true)) { son = leftId; } else { son = rightId; } if (comparesmall(heap.get(id), heap.get(son)) == true) { break; } else { swap(id, son); } id = son; } } } class Edge { int pos; int height; boolean isStart; public Edge(int pos, int height, boolean isStart) { this.pos = pos; this.height = height; this.isStart = isStart; } } class EdgeComparator implements Comparator { @Override public int compare(Edge arg1, Edge arg2) { Edge l1 = (Edge) arg1; Edge l2 = (Edge) arg2; if (l1.pos != l2.pos) return compareInteger(l1.pos, l2.pos); if (l1.isStart && l2.isStart) { return compareInteger(l2.height, l1.height); } if (!l1.isStart && !l2.isStart) { return compareInteger(l1.height, l2.height); } return l1.isStart ? -1 : 1; } int compareInteger(int a, int b) { return a <= b ? -1 : 1; } } List output(List res) { List ans = new ArrayList(); if (res.size() > 0) { int pre = res.get(0).get(0); int height = res.get(0).get(1); for (int i = 1; i 0) { now.add(pre); now.add(id); now.add(height); ans.add(now); } pre = id; height = res.get(i).get(1); } } return ans; } public List buildingOutline(int[][] buildings) { // write your code here List res = new ArrayList(); if (buildings == null || buildings.length == 0 || buildings[0].length == 0) { return res; } ArrayList edges = new ArrayList(); for (int[] building : buildings) { Edge startEdge = new Edge(building[0], building[2], true); edges.add(startEdge); Edge endEdge = new Edge(building[1], building[2], false); edges.add(endEdge); } Collections.sort(edges, new EdgeComparator()); HashHeap heap = new HashHeap("max"); List now = null; for (Edge edge : edges) { if (edge.isStart) { if (heap.isEmpty() || edge.height > heap.peek()) { now = new ArrayList(Arrays.asList(edge.pos, edge.height)); res.add(now); } heap.add(edge.height); } else { heap.delete(edge.height); if (heap.isEmpty() || edge.height > heap.peek()) { if (heap.isEmpty()) { now = new ArrayList(Arrays.asList(edge.pos, 0)); } else { now = new ArrayList(Arrays.asList(edge.pos, heap.peek())); } res.add(now); } } } return output(res); } }
Given an array of n integer with duplicate number, and a moving window(size k), move the window at each iteration from the start of the array, find the maximum number inside the window at each moving.
Have you met this question in a real interview? Yes
Example
For array [1, 2, 7, 7, 8], moving window size k = 3. return [7, 7, 8]
At first the window is at the start of the array like this
[|1, 2, 7| ,7, 8] , return the maximum 7;
then the window move one step forward.
[1, |2, 7 ,7|, 8], return the maximum 7;
then the window move one step forward again.
[1, 2, |7, 7, 8|], return the maximum 8;
queue: 是吃饭拉屎
stack: 是吃饭有毒吐出来
queue: 是上下都吃 上下都排…
这道题若是用heap作呢, 就是O(nlogk)的时间. 若是优化的话呢?
只能想线性数据结构啦
线性数据结构有啥呢? queue stack 和deque
那咱们先试试stack?
好比: [ 1 2 7 5 8] 这组数据:
咱们想找最大值, 那么就是若是从左到右, 若是遇到比一个数a大的, 就把a踢出去:
先:
1 1加入 1 2 2加入 2 1踢掉 2 7 7加入 7 2踢掉 7 5 5加入
这时候注意了, 5比7 小并不能说明5不会成为最大值, 由于stack先进后出, 因此有保留有效信息的能力, 因此咱们先把5 存着, 而后:
7 5 8 8加入 7 8 5踢掉 8 7踢掉
单调栈升级为单调双端队列:
public class Solution { /** * @param nums: A list of integers. * @return: The maximum number inside the window at each moving. */ void inQueue(Deque deque, int num) { while (!deque.isEmpty() && deque.peekLast() < num) { deque.pollLast(); } deque.offer(num); } void outQueue(Deque deque, int num) { if (deque.peekFirst() == num) { deque.pollFirst(); } } public ArrayList maxSlidingWindow(int[] nums, int k) { // write your code here ArrayList ans = new ArrayList(); Deque deque = new ArrayDeque(); if (nums.length == 0) { return ans; } for (int i = 0; i < k - 1; i++) { inQueue(deque, nums[i]); } for(int i = k - 1; i < nums.length; i++) { inQueue(deque, nums[i]); ans.add(deque.peekFirst()); outQueue(deque, nums[i - k + 1]); } return ans; } }
为何Debug必定要靠本身?
缘由有四:
Debug的基本步骤
在第4步中,若是没法经过肉眼看出错误的部分,就一步步“模拟执行”程序,找出错误。
实在Debug 不出来怎么办?
若是你已经 Debug 了一成天,能够考虑向他人求助。