Patching Array

Problem

Given a sorted positive integer array nums and an integer n, add/patch elements to the array such that any number in range [1, n] inclusive can be formed by the sum of some elements in the array. Return the minimum number of patches required.

Example 1:
nums = [1, 3], n = 6
Return 1.

Combinations of nums are [1], [3], [1,3], which form possible sums of: 1, 3, 4.
Now if we add/patch 2 to nums, the combinations are: [1], [2], [3], [1,3], [2,3], [1,2,3].
Possible sums are 1, 2, 3, 4, 5, 6, which now covers the range [1, 6].
So we only need 1 patch.

Example 2:
nums = [1, 5, 10], n = 20
Return 2.
The two patches can be [2, 4].

Example 3:
nums = [1, 2, 2], n = 5
Return 0.

Solution

思路如下:设absent为当前缺失的最小的数,ind为nums的索引,count计数需要添加的数

  • 如果存在一个nums[ind]小于absent,那么absent=nums[ind]+absent。这是因为[1,absent)都可以获得,现在又多了一个nums[ind],所以当前可以得到的范围是[1,absent+nums[ind]),即absent=nums[ind]+absent
  • 否则,我们需要引入absent,因为不引入它就没有办法继续,引入absent后,因为[1,absent-1]也是可以得到的,所以当前可以得到的集合是[1,absent+absent-1],所以新的absent=2*absent
  • 还需要注意的是数的表示范围,由于有一个例子n=2147483647,已经超出了int的表示范围,所以absent必须是long型的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Solution {
public int minPatches(int[] nums, int n) {
long count = 0, absent = 1, ind = 0;
while (absent <= n) {
if (ind < nums.length && nums[(int) ind] <= absent) {
absent += nums[(int) ind++];
} else {
++count;
absent += absent;
}
}
return (int) count;
}
}
分享到

Linked List Cycle

Problem 1

Description

Given a linked list, determine if it has a cycle in it.

Note: Solve it using O(1) space

Solution 1

决定一个list是否有环的方法是:

  • 使用一个慢指针slow每次移动1步
  • 使用一个快指针fast每次移动2步
  • 如果快指针和慢指针在迭代一定次数后相遇,则存在回路
  • 否则,如果fast.next=null或者slow.next=null,说明不存在回路
1
2
3
4
5
6
7
8
9
10
11
public boolean hasCycle(ListNode head) {
if(head==null) return false;
ListNode walker = head;
ListNode runner = head;
while(runner.next!=null && runner.next.next!=null) {
walker = walker.next;
runner = runner.next.next;
if (walker==runner) return true;
}
return false;
}

Problem 2

Description

Given a linked list, return the node where the cycle begins. If there is no cycle, return null.

Note: Do not modify the linked list.
Solve it using O(1) space

Solution 2

确认一个list是否有回路的方法如上。确定回路入口的方法如下:

  • 不妨假设list头到loop入口的距离是 l1
  • loop入口到两个指针相遇点的距离是 l2
  • loop的长度位 c,n表示fast指针绕loop的圈数
  • 指针相遇时,slow指针走过的距离是:l1 + l2 + m x c,fast指针走过的距离是:l1 + l2 + n x c
  • 因为fast指针的路程是slow指针的两倍, 所以:l1 + l2 + n x c = 2 x (l1 + l2 + m x c)
  • l1 = (c-l2) + (n-2 x m-1)c,而c-l2是相遇点到loop入口的距离
  • 所以分别从list头和指针相遇点出发、每次移动步长均为1的两个指针会在loop入口回合
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode slow = head;
ListNode fast = head;

while (fast!=null && fast.next!=null){
fast = fast.next.next;
slow = slow.next;
// 找到快指针和慢指针的汇合点,说明loop存在
if (fast == slow){
ListNode slow2 = head;
// 分别从链表头和汇合点出发的指针,在loop入口处相遇
while (slow2 != slow){
slow = slow.next;
slow2 = slow2.next;
}
return slow;
}
}
return null;
}
}

相似问题:Find the Duplicate Number

分享到

Find the Duplicate Number

Problem

Description

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.

Note:

  • 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(n2).
  • There is only one duplicate number in the array, but it could be repeated more than once.

Solution

  • 题中给定的是数组,由于长度位n+1的数组只有1-n的整数,所以可以把数组看成静态链表,数组下标是节点值,对应的值指示下一节点的值
  • 我们所要求的重复元素其实就是:不同的下标对应相同的值,在静态链表中也就是loop的入口节点的值
  • 由于必然存在重复,所以这个静态链表肯定存在loop,查找loop入口节点的值方法和Linked List Cycle II一致
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Solution {
public int findDuplicate(int[] nums) {
int slow=nums[0], fast=nums[nums[0]];
while (fast != slow) {
slow=nums[slow];
fast=nums[nums[fast]];
}
fast = 0;
while (fast != slow) {
slow=nums[slow];
fast=nums[fast];
}
return fast;
}
}

相关问题:Linked List Cycle II

分享到

Majority Element

Problem

Given an integer array of size n, find all elements that appear more than ⌊n/3⌋(这是向下取整符号) times. The algorithm should run in linear time and in O(1) space.

Solution

这是主元素法的扩展,原主元素问题是:

设T[0:n-1]是n个元素的数组。对任一元素x,设S(x)={i|T[i]=x}。当|S(x)|>n/2时,称x为T的主元素。设计一个线性时间算法,确定T[0:n-1]是否有一个主元素。

解法为:如果每次删除两个不同的数字(不管是否包含主元素的数字),那么在剩下的数字中,主元素的出现的次数仍然超过总数的一半。可以通过不断的重复这个过程,转化为更小的问题,从而得到答案。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
master(A):
n ← length[A]
count ← 1
seed ← A[0]
找候选主元素,即数目最多的那个元素
for i ← 1 to n – 1
do if A[i] = seed
then count ← count + 1
else if count > 0
then count ← count – 1
else seed ← A[i]
查找候选主元素是否是主元素
count ← 0
for i ← 0 to n – 1
do if A[i] = seed
then count ← count + 1
if count > n/2
then return seed and count
else
return null

在此基础上,本题的解题思路:

  • 题目要求寻找个数大于⌊n/3⌋的元素,那么长度为n的数组中最多只有两个这样的元素。
  • 使用两个标记,寻找出现次数最大和次大的元素
  • 判断这两个元素是否满足:个数大于⌊n/3⌋
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public Class Solution {
public List<Integer> majorityElement(int[] nums) {
List<Integer> ret = new ArrayList<Integer>();
if (nums.length == 0) return ret;
int cand1=0,cand2=0,count1=0,count2=0;
for (int i=0; i<nums.length; i++) {
// if elseif可以防止cand1,cand2相同
if (nums[i] == cand1) ++count1;
else if (nums[i] == cand2) ++count2;
else if (count1 == 0) { cand1 = nums[i]; count1 = 1; }
else if (count2 == 0) { cand2 = nums[i]; count2 = 1; }
else { --count1; --count2; }
}
count1=0; count2=0;
// 判断候选元素数目是否满足条件
for (int i=0; i < nums.length; i++) {
if (nums[i] == cand1) ++count1;
else if (nums[i] == cand2) ++count2;
}
if (count1 > nums.length/3) ret.add(cand1);
if (count2 > nums.length/3) ret.add(cand2);
return ret;
}
}
分享到

Count Primes

Problem

Description:

Count the number of prime numbers less than a non-negative number, n.

Solution

埃拉托斯特尼筛法

本题使用埃拉托斯特尼筛法解,此方法用于找出一定范围内的所有质数,也是最有效的方法之一

  • 原理:从2开始,将每个质数的倍数标记成合数,倍数的实现可以借助于等差数列,不断剔除合数即可
  • 方法:找出sqrt(n)以内的质数、并将相应的合数去掉即可。
  • 说明:大于sqrt(n)的合数可以被消除,因为假设一个合数sqrt(n)小于z=a*b,那么,min(a,b)小于sqrt(n),所以算法在遇到min(a,b)的时候就已经将z标记为合数了

埃拉托斯特尼筛法的伪代码如下

1
2
3
4
5
6
7
8
9
10
Input: an integer n > 1
Let A be an array of Boolean values, indexed by integers 2 to n,
initially all set to true.

for i = 2, 3, 4, ..., not exceeding √n:
if A[i] is true:
for j = i2, i2+i, i2+2i, i2+3i, ..., not exceeding n :
A[j] := false

Output: all i such that A[i] is true.

具体实现过程中,java使用BitSet类可以节约空间。

BitSet
  • BitSet类实现了大小可动态改变, 取值为true或false的位集合(每位长度位1bit)。用于表示一组布尔标志,默认情况下,set 中所有位的初始值都是 false。
  • 使用方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 构造函数
public BitSet();
public BitSet(int nbits);
// 方法
public void set(int pos); //位置pos的字位设置为true
public void set(int bitIndex, boolean value); //将指定索引处的位设置为指定的值
public void clear(int pos); //位置pos的字位设置为false
public void clear(); //将此 BitSet 中的所有位设置为 false
public int cardinality(); //返回此 BitSet 中设置为 true 的位数
public boolean get(int pos); //返回位置是pos的字位值
public void and(BitSet other); //other同该字位集进行与操作,结果作为该字位集的新值
public void or(BitSet other); //other同该字位集进行或操作,结果作为该字位集的新值
public void xor(BitSet other); //other同该字位集进行异或操作,结果作为该字位集的新值
public void andNot(BitSet set); //清除此 BitSet 中所有的位,set-用来屏蔽此 BitSet 的 BitSet
public int size(); //返回此 BitSet 表示位值时实际使用空间的位数
public int length(); //返回此 BitSet 的“逻辑大小”:BitSet 中最高设置位的索引加 1
public int hashCode(); //返回该集合Hash 码, 这个码同集合中的字位值有关
public boolean equals(Object other); //如果other中的字位同集合中的字位相同,返回true
public Object clone(); //克隆此 BitSet,生成一个与之相等的新 BitSet
public String toString(); //返回此位 set 的字符串表示形式
分享到

Super Ugly Number

Problem

Write a program to find the nth super ugly number.

Super ugly numbers are positive numbers whose all prime factors are in the given prime list primes of size k. For example, [1, 2, 4, 7, 8, 13, 14, 16, 19, 26, 28, 32] is the sequence of the first 12 super ugly numbers given primes = [2, 7, 13, 19] of size 4.

Note:
(1) 1 is a super ugly number for any given primes.
(2) The given numbers in primes are in ascending order.
(3) 0 < k ≤ 100, 0 < n ≤ 106, 0 < primes[i] < 1000.

Solution

  • 维护一个长度为n的数组,存储ugly数
  • 使用一个数组idx,长度与primes一致,用以记录每个primes元素已经和哪一位的ugly相乘过,相乘过就移动之
  • 去重,需要扫描idx,对应primes[j]*un[idx[j]]<=un[i]的都要去掉
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Solution {
public int nthSuperUglyNumber(int n, int[] primes) {
int[] pos = new int[primes.length], un = new int[n];
un[0] = 1;
for (int i=1; i<n; i++) {
un[i] = Integer.MAX_VALUE;
// 找当下最小的数
for (int j=0; j<primes.length; j++)
un[i] = Math.min(primes[j]*un[pos[j]], un[i]);
// 去重
for (int j=0; j<primes.length; j++)
while (primes[j]*un[pos[j]] <= un[i]) pos[j]++;
}
return un[n-1];
}
}
分享到

Maximal Rectangle

Problem

Given a 2D binary matrix filled with 0’s and 1’s, find the largest rectangle containing only 1’s and return its area.

For example, given the following matrix:

1 0 1 0 0
1 0 1 1 1
1 1 1 1 1
1 0 0 1 0

Return 6.

Solution

本题是DP题,为了能对所有情形考虑,每一列对应一个height值,表示从当前行开始一直向上的最大高度
所以具体的做法是:

  • 每到一个位置,计算以这个位置所在列为最高的矩形面积
  • 逐行处理,遇到0忽略,遇到1时,计算其height值
  • 有了height,还要知道width才能计算数量,width的计算需要借助left、right和left_most、right_most
  • 每一行的left、right到下一行才有用,因为要使用当前位置向上延伸的最高高度,所以在向两边延伸时需要上一行两边的情况
  • 对于left,从左向右扫描,遇到0时left=0,否则left=max(left_last_row, 本行最近的0的下一位置序号)。(这里遇到0设置left=0是为了不影响下一行)
  • 对于right,从右向左扫描,遇到0时right=行长度-1,否则,left=min(right_last_row, 本行最近的0的上一位置序号)
  • 扫描这一行,多次计算(right-left+1)*height,并及时更新最大值
  • 实现过程中,left、right和height可以分别用一维数组存储,详细见代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class Solution {
public int maximalRectangle(char[][] matrix) {
int m=0, n=0, ret=0;
if ((m=matrix.length)==0 || (n=matrix[0].length)==0) return 0;
int[] height=new int[n], left=new int[n], right=new int[n];
for (int j=0; j<n; j++) right[j] = n-1;
for (int i=0; i<m; i++) {
// left_most是当前点左侧最近0点的下一位
// right_most是当前点右侧最近0点的前一个
// 这两个变量可以确定以当前点为中心,连续为1的长度
int left_most = 0, right_most = n-1;
// 要想使用上一行的left值,本行的left_most必须小于等于上一行对应位的left值
for (int j=0; j<n; j++) {
if (matrix[i][j]=='0') {left_most=Math.max(left_most, j+1); left[j] = 0;}
else left[j] = Math.max(left[j], left_most);
}
for (int j=n-1; j>=0; j--) {
if (matrix[i][j]=='1') right[j] = Math.min(right[j], right_most);
else {right_most = Math.min(right_most, j-1); right[j] = n-1;}
}
// 更新height值,并计算以本行中每个点为底,最高的矩形的面积
for (int j=0; j<n; j++) {
if (matrix[i][j]=='0') height[j] = 0;
else height[j] += 1;
ret = Math.max(ret, (right[j]-left[j]+1)*height[j]);
}
}
return ret;
}
}
分享到

Minimum Height Trees

Problem

For a undirected graph with tree characteristics, we can choose any node as the root. The result graph is then a rooted tree. Among all possible rooted trees, those with minimum height are called minimum height trees (MHTs). Given such a graph, write a function to find all the MHTs and return a list of their root labels.

Format
The graph contains n nodes which are labeled from 0 to n - 1. You will be given the number n and a list of undirected edges (each edge is a pair of labels).

You can assume that no duplicate edges will appear in edges. Since all edges are undirected, [0, 1] is the same as [1, 0] and thus will not appear together in edges.

Example 1:

Given n = 4, edges = [[1, 0], [1, 2], [1, 3]]

    0
    |
    1
   / \
  2   3

return [1]

Example 2:

Given n = 6, edges = [[0, 3], [1, 3], [2, 3], [4, 3], [5, 4]]

 0  1  2
  \ | /
    3
    |
    4
    |
    5

return [3, 4]

Solution

解体思想是,维护图的每个度为1的节点为叶节点集合,loop:

  • 每层循环,对每一个叶节点,从叶节点集合中去掉当前页节点,并去除该叶节点上的边(指向此叶节点的边也得去掉)
  • 对于,每个去掉的叶节点,如果与其相连的节点也成为了叶节点,将其加入叶节点集合
  • 每次删除一个叶节点,将节点总数-1
  • 当节点总数<=2时,退出循环,此时叶节点集合即为所求
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class Solution {
public List<Integer> findMinHeightTrees(int n, int[][] edges) {
if (n == 1) return Collections.singletonList(0);
HashMap<Integer, Set<Integer>> map = new HashMap<>();
// 建立邻接表
for (int[] edge : edges) {
map.computeIfAbsent(edge[0], k->new HashSet<Integer>()).add(edge[1]);
map.computeIfAbsent(edge[1], k->new HashSet<Integer>()).add(edge[0]);
}
// 叶节点集合
List<Integer> leafs = new LinkedList<>();
for (Integer key : map.keySet())
if (map.get(key).size() == 1) leafs.add(key);
while (n > 2) {
n -= leafs.size();
List<Integer> new_leafs = new LinkedList<>();
for (Integer leaf : leafs) {
// 删除指向此叶节点的边
int box = map.get(leaf).iterator().next();
map.get(box).remove(leaf);
// 新的叶节点出现
if (map.get(box).size() == 1) new_leafs.add(box);
}
leafs = new_leafs;
}
return leafs;
}
}
分享到

Increasing Triplet Subsequence

Problem

Given an unsorted array return whether an increasing subsequence of length 3 exists or not in the array.

Formally the function should:
Return true if there exists i, j, k
such that arr[i] < arr[j] < arr[k] given 0 ≤ i < j < k ≤ n-1 else return false.
Your algorithm should run in O(n) time complexity and O(1) space complexity.

Examples:
Given [1, 2, 3, 4, 5],
return true.

Given [5, 4, 3, 2, 1],
return false.

Solution

  • 解题思路是维护两个变量,min和mid
  • min表示当前遇到的最小的数
  • mid表示在当前情况下,所有长度为2的递增序列第二个数的最小值(表示前面有一个比mid还小的数且没有一个小于mid的数能取代mid的位置)
  • 当出现一个比min大的数时:
    1. 如果这个数比mid大,则返回true
    2. 如果这个数比mid小,则更新mid为当前值

代码如下:

1
2
3
4
5
6
7
8
9
public boolean increasingTriplet(int[] nums) {
int min = Integer.MAX_VALUE, secondMin = Integer.MAX_VALUE;
for(int num : nums){
if(num <= min) min = num;
else if(num < secondMin) secondMin = num;
else if(num > secondMin) return true;
}
return false;
}

分享到

Design Twitter

Problem

Design a simplified version of Twitter where users can post tweets, follow/unfollow another user and is able to see the 10 most recent tweets in the user’s news feed. Your design should support the following methods:

  • postTweet(userId, tweetId): Compose a new tweet.
  • getNewsFeed(userId): Retrieve the 10 most recent tweet ids in the user’s news feed. Each item in the news feed must be posted by users who the user followed or by the user herself. Tweets must be ordered from most recent to least recent.
  • follow(followerId, followeeId): Follower follows a followee.
  • unfollow(followerId, followeeId): Follower unfollows a followee.

Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Twitter twitter = new Twitter();

// User 1 posts a new tweet (id = 5).
twitter.postTweet(1, 5);

// User 1's news feed should return a list with 1 tweet id -> [5].
twitter.getNewsFeed(1);

// User 1 follows user 2.
twitter.follow(1, 2);

// User 2 posts a new tweet (id = 6).
twitter.postTweet(2, 6);

// User 1's news feed should return a list with 2 tweet ids -> [6, 5].
// Tweet id 6 should precede tweet id 5 because it is posted after tweet id 5.
twitter.getNewsFeed(1);

// User 1 unfollows user 2.
twitter.unfollow(1, 2);

// User 1's news feed should return a list with 1 tweet id -> [5],
// since user 1 is no longer following user 2.
twitter.getNewsFeed(1);

Solution

解题思路:如果将所有用户的tweet放在一个队列里,在getNewsFeed时遍历查找会超时

  • 每个用户维护一个follow集合,集合中元素时该用户关注的用户ID
  • 每个用户维护一个自己的tweet列表,列表元素包含时间和tweet ID,列表按时间排好序
  • getNewsFeed时,采用merge k sorted array的思想,将用户自己和关注的用户的tweet列表放入一个堆中
  • 每次弹出第一个元素对应时间最近的列表,取出第一个元素,然后再将其加入进堆中(如果该列表还有元素)
  • 取出的元素达到要求或者堆为空时退出
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
public class Twitter {
HashMap<Integer, Set<Integer>> cmap;
HashMap<Integer, List<int[]>> tw;
int count;
/** Initialize your data structure here. */
public Twitter() {
cmap = new HashMap<>();
tw = new HashMap<>();
count = 0;
}

/** Compose a new tweet. */
public void postTweet(int userId, int tweetId) {
int[] tmp = {count++, tweetId};
tw.computeIfAbsent(userId, k->new LinkedList<>()).add(0,tmp);
System.out.println(count);
}

/** Retrieve the 10 most recent tweet ids in the user's news feed. Each item in the news feed must be posted by users who the user followed or by the user herself. Tweets must be ordered from most recent to least recent. */
public List<Integer> getNewsFeed(int userId) {
int n = 0;
Set<Integer> tmp = cmap.containsKey(userId)?cmap.get(userId):new HashSet<Integer>();
List<Integer> ret = new LinkedList<>();

PriorityQueue<List<int[]> > pq = new PriorityQueue<>(new Comparator<List<int[]>>(){

@Override
public int compare(List<int[]> o1, List<int[]> o2) {
// TODO Auto-generated method stub
return o2.get(0)[0] - o1.get(0)[0];
}});
if (tw.containsKey(userId) && tw.get(userId).size()>0) pq.offer(tw.get(userId));
for (Integer it : tmp) {
if (!tw.containsKey(it) || tw.get(it).size()==0) continue;
pq.offer(tw.get(it));
}
while (pq.size()>0 && n++<10) {
List<int[]> list = pq.poll();
ret.add(list.get(0)[1]);
if (list.size()>1) pq.offer(list.subList(1, list.size()));
}
return ret;
}

/** Follower follows a followee. If the operation is invalid, it should be a no-op. */
public void follow(int followerId, int followeeId) {
if (followerId == followeeId) return;
cmap.computeIfAbsent(followerId, k -> new HashSet<Integer>()).add(followeeId);
}

/** Follower unfollows a followee. If the operation is invalid, it should be a no-op. */
public void unfollow(int followerId, int followeeId) {
if (followerId==followeeId || !cmap.containsKey(followerId)) return;
Set<Integer> tmp = cmap.get(followerId);
if (!tmp.contains(followeeId)) return;
tmp.remove(followeeId);
}
}

实现过程中,tweet集合不使用java自带的List,改用自定义类和索引标识可以进一步加速程序。

分享到