diff --git a/.travis.yml b/.travis.yml
index a38e851fa..e5bfe2a93 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -28,6 +28,7 @@ before_script:
- unzip ~/raw_fonts/NotoSansCJKtc-hinted.zip -d ~/raw_fonts/NotoSansCJKtc-hinted
script:
+ - while sleep 30; do echo "still alive"; done &
- sed '/\(en\/\)/! d' LANGS.md.bak > LANGS.md
- mv -t ~/.fonts/noto ~/raw_fonts/NotoSans-unhinted/*-Regular.ttf ~/raw_fonts/NotoSans-unhinted/*-Bold.ttf ~/raw_fonts/NotoSans-unhinted/*-Black.ttf
- sudo fc-cache -f -v
@@ -39,7 +40,7 @@ script:
- sed '/\(zh-hans\/\)/! d' LANGS.md.bak > LANGS.md
- mv -t ~/.fonts/noto ~/raw_fonts/NotoSansCJKsc-hinted/*-DemiLight.otf ~/raw_fonts/NotoSansCJKsc-hinted/*-Bold.otf ~/raw_fonts/NotoSansCJKsc-hinted/*-Black.otf
- sudo fc-cache -f -v
- - gitbook pdf .
+ # - gitbook pdf .
- gitbook epub .
- gitbook mobi .
- rm ~/.fonts/noto/*.otf
@@ -56,7 +57,6 @@ script:
before_deploy:
- tar czvf book_html.tar.gz _book/
- - git tag "$(date +'%Y%m%d_%H%M%S')"
deploy:
- provider: pages
@@ -68,23 +68,6 @@ deploy:
on:
branch: master
target-branch: gh-pages
- - provider: releases
- api_key:
- secure: B/dqY0sC9I0Uu5o3HVHouX/mfLTUH/dt16IsBdm+sZQW+/YeTUTGwpx+Vl5lXpMhUoxrh60Qxt0KbnqvPqAxsPXGMH2Mhl1E5lgvMYViOGQyx9JJuuba+GWOzD+r2+XOUsqBzOCUhvC7iZbuGiWYvV2+4noAmAoKa1pO1j2yxiI=
- file:
- - book_html.tar.gz
- - book_en.epub
- - book_en.mobi
- - book_en.pdf
- - book_zh-hans.epub
- - book_zh-hans.mobi
- - book_zh-hans.pdf
- - book_zh-tw.epub
- - book_zh-tw.mobi
- - book_zh-tw.pdf
- skip_cleanup: true
- on:
- repo: billryan/algorithm-exercise
after_success:
- ls -lh
diff --git a/CNAME b/CNAME
new file mode 100644
index 000000000..6d8a4f13a
--- /dev/null
+++ b/CNAME
@@ -0,0 +1 @@
+algorithm.yuanbin.me
diff --git a/README.md b/README.md
index 7849ce11e..4fb918b0c 100644
--- a/README.md
+++ b/README.md
@@ -4,11 +4,11 @@
[](https://slackin4ds-algo.herokuapp.com/)
[](https://ds-algo.slack.com/)
-This book is some notes on learning data structure and algorithm. It was written in Simplified Chinese but other languages such as English and Traditional Chinese are work in progress.
+This book is notes about learning data structure and algorithm. It was written in Simplified Chinese but other languages such as English and Traditional Chinese are also working in progress. Contributions are welcome!
-- [English](https://algorithm.yuanbin.me/en/), seldom updated
+- [English](https://algorithm.yuanbin.me/en/), rarely updated
- [简体中文](https://algorithm.yuanbin.me/zh-hans/), frequently updated
-- [繁體中文](https://algorithm.yuanbin.me/zh-tw/), seldom updated
+- [繁體中文](https://algorithm.yuanbin.me/zh-tw/), rarely updated
## Introduction
@@ -34,6 +34,6 @@ This work is licensed under the **Creative Commons Attribution-ShareAlike 4.0 In
## To Do
- [ ] add multiple languages support
-- [ ] add implementations of `Python`, `C++` and `Java` code
+- [ ] add implementations of `Python`, `C++`, `Go` and `Java` code
- [x] add time and space complexity analysis
- [x] add proper Chinese fonts for PDF output
diff --git a/book.json b/book.json
index d1c40b9ab..04eb5605d 100644
--- a/book.json
+++ b/book.json
@@ -4,7 +4,6 @@
"ga",
"katex",
"richquotes",
- "swiftype",
"sitemap2",
"github",
"edit-link",
@@ -23,10 +22,6 @@
"richquotes": {
"todos": true
},
- "swiftype": {
- "token": "utQHy1R2XyiVqDqaZqws",
- "version": "2.0.0"
- },
"sitemap2": {
"hostname": "http://algorithm.yuanbin.me/"
},
diff --git a/codes/algorithm-java/.gitignore b/codes/algorithm-java/.gitignore
new file mode 100644
index 000000000..1d1652eb2
--- /dev/null
+++ b/codes/algorithm-java/.gitignore
@@ -0,0 +1,10 @@
+
+.idea/
+*.iml
+project/
+
+out/
+target/
+
+.DS_Store
+
diff --git a/codes/algorithm-java/pom.xml b/codes/algorithm-java/pom.xml
new file mode 100644
index 000000000..ae0f17fcd
--- /dev/null
+++ b/codes/algorithm-java/pom.xml
@@ -0,0 +1,59 @@
+
+
+ 4.0.0
+
+ me.yuanbin
+ algorithm-java
+ 1.0-SNAPSHOT
+
+
+ 1.8
+ 1.8
+ 4.12
+ 1.7.25
+ 1.18.8
+
+
+
+
+ org.projectlombok
+ lombok
+ ${lombok.version}
+ provided
+
+
+
+ org.slf4j
+ slf4j-log4j12
+ ${slf4j.version}
+ provided
+
+
+
+ junit
+ junit
+ ${junit.version}
+ test
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.1
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 3.2.1
+
+
+
+
+
\ No newline at end of file
diff --git a/codes/algorithm-java/src/main/java/basic/DataStructure.java b/codes/algorithm-java/src/main/java/basic/DataStructure.java
new file mode 100644
index 000000000..452e552df
--- /dev/null
+++ b/codes/algorithm-java/src/main/java/basic/DataStructure.java
@@ -0,0 +1,22 @@
+package basic;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.LinkedList;
+import java.util.Queue;
+
+/**
+ * Basic data structure including queue, stack, graph...
+ *
+ * @author billryan
+ * @date 2019-06-22
+ */
+@Data
+@NoArgsConstructor
+public class DataStructure {
+ private Queue queue = new LinkedList<>();
+ private Deque stack = new ArrayDeque<>();
+}
diff --git a/codes/algorithm-java/src/main/resources/log4j.properties b/codes/algorithm-java/src/main/resources/log4j.properties
new file mode 100644
index 000000000..0a64bd839
--- /dev/null
+++ b/codes/algorithm-java/src/main/resources/log4j.properties
@@ -0,0 +1,9 @@
+# Root logger option
+# By default, everything goes to console and file
+log4j.rootLogger=INFO, stdout
+
+# Direct log messages to stdout
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.Target=System.out
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
diff --git a/codes/algorithm-java/src/test/java/basic/DataStructureTest.java b/codes/algorithm-java/src/test/java/basic/DataStructureTest.java
new file mode 100644
index 000000000..7577a95b6
--- /dev/null
+++ b/codes/algorithm-java/src/test/java/basic/DataStructureTest.java
@@ -0,0 +1,15 @@
+package basic;
+
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author billryan
+ * @date 10/8/2019 10:59
+ */
+public class DataStructureTest {
+
+ private static final Logger log = LoggerFactory.getLogger(DataStructureTest.class);
+
+}
diff --git a/codes/algorithm-java/src/test/java/basic/QueueTest.java b/codes/algorithm-java/src/test/java/basic/QueueTest.java
new file mode 100644
index 000000000..a39d4e76d
--- /dev/null
+++ b/codes/algorithm-java/src/test/java/basic/QueueTest.java
@@ -0,0 +1,33 @@
+package basic;
+
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.LinkedList;
+import java.util.Queue;
+
+/**
+ * @author billryan
+ * @date 10/8/2019 12:05
+ */
+public class QueueTest {
+
+ private static final Logger log = LoggerFactory.getLogger(QueueTest.class);
+
+ @Test
+ public void testQueue() {
+ Queue queue = new LinkedList<>();
+ queue.offer(1);
+ queue.offer(2);
+ queue.offer(3);
+
+ int peek = 1;
+ while (!queue.isEmpty()) {
+ int queuePeek = queue.poll();
+ log.info("queue peek: {}", queuePeek);
+ assert peek == queuePeek;
+ peek++;
+ }
+ }
+}
diff --git a/codes/algorithm-java/src/test/java/basic/StackTest.java b/codes/algorithm-java/src/test/java/basic/StackTest.java
new file mode 100644
index 000000000..3eb4e57f6
--- /dev/null
+++ b/codes/algorithm-java/src/test/java/basic/StackTest.java
@@ -0,0 +1,33 @@
+package basic;
+
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayDeque;
+import java.util.Deque;
+
+/**
+ * @author billryan
+ * @date 10/8/2019 12:04
+ */
+public class StackTest {
+
+ private static final Logger log = LoggerFactory.getLogger(StackTest.class);
+
+ @Test
+ public void testStack() {
+ Deque stack = new ArrayDeque<>();
+ stack.push(1);
+ stack.push(2);
+ stack.push(3);
+
+ int peek = 3;
+ while (!stack.isEmpty()) {
+ int stackPeek = stack.pop();
+ log.info("stack peek: {}", stackPeek);
+ assert peek == stackPeek;
+ peek--;
+ }
+ }
+}
diff --git a/en/SUMMARY.md b/en/SUMMARY.md
index 5bc49adb5..eef5ac755 100644
--- a/en/SUMMARY.md
+++ b/en/SUMMARY.md
@@ -259,4 +259,3 @@
* [System Architecture](appendix_ii_system_design/system_architecture.md)
* [Scalability](appendix_ii_system_design/scalability.md)
* [Tags](tags.md)
-
diff --git a/en/basics_sorting/quick_sort.md b/en/basics_sorting/quick_sort.md
new file mode 100644
index 000000000..8e17d6a5c
--- /dev/null
+++ b/en/basics_sorting/quick_sort.md
@@ -0,0 +1,299 @@
+# Quick Sort
+
+In essence, quick sort is an application of `divide and conquer` strategy. There are usually three steps:
+
+1. Pick a pivot -- a random element.
+2. Partition -- put the elements smaller than pivot to its left and greater ones to its right.
+3. Recurse -- apply above steps until the whole sequence is sorted.
+
+## out-in-place implementation
+
+Recursive implementation is easy to understand and code. Python `list comprehension` looks even nicer:
+
+```python
+#!/usr/bin/env python
+
+
+def qsort1(alist):
+ print(alist)
+ if len(alist) <= 1:
+ return alist
+ else:
+ pivot = alist[0]
+ return qsort1([x for x in alist[1:] if x < pivot]) + \
+ [pivot] + \
+ qsort1([x for x in alist[1:] if x >= pivot])
+
+unsortedArray = [6, 5, 3, 1, 8, 7, 2, 4]
+print(qsort1(unsortedArray))
+```
+
+The output:
+
+```
+[6, 5, 3, 1, 8, 7, 2, 4]
+[5, 3, 1, 2, 4]
+[3, 1, 2, 4]
+[1, 2]
+[]
+[2]
+[4]
+[]
+[8, 7]
+[7]
+[]
+[1, 2, 3, 4, 5, 6, 7, 8]
+```
+
+Despite of its simplicity, above quick sort code is not that 'quick': recursive calls keep creating new arrays which results in high space complexity. So `list comprehension` is not proper for quick sort implementation.
+
+### Complexity
+
+Take a quantized look at how much space it actually cost.
+
+In the best case, the pivot happens to be the **median** value, and quick sort partition divides the sequence almost equally, so the recursions' depth is $$\log n$$ . As to the space complexity of each level (depth), it is worth some discussion.
+
+A common mistake can be: each level contains $$n$$ elements, then the space complexity is surely $$O(n)$$ . The answer is right, while the approach is not. As we know, space complexity is usually measured by memory consumption of a running program. Take above out-in-place implementation as example, **in the best case, each level costs half as much memory as its upper level does** . Sums up to be:
+
+ $$\sum _{i=0} ^{} \frac {n}{2^i} = 2n$$ .
+
+For more detail, refer to the picture below as well as above python code. The first level of recursion saves 8 values, the second 4, and so on so forth.
+
+In the worst case, it will take $$i - 1$$ times of swap on level $$i$$. Sums up to be:
+
+$$\sum_{i=0}^n (n-i+1) = O(n^2)$$
+
+
+
+## in-place implementation
+
+### one index for partition
+
+One in-place implementation of quick sort is to use one index for partition, as the following image illustrates. Take example of `[6, 5, 3, 1, 8, 7, 2, 4]` again, $$l$$ and $$u$$ stand for the lower bound and upper bound of index respectively. $$i$$ traverses and $$m$$ maintains index of partition which varies with $$i$$. $$target$$ is the pivot.
+
+
+
+For each specific value of $$i$$, $$x[i]$$ will take one of the follwing cases: if $$x[i] \geq t$$ , $$i$$ increases and goes on traversing; else if $$x[i] < t$$ , $$x[i]$$ will be swapped to the left part, as statement `swap(x[++m], x[i])` does. Partition is done when `i == u`, and then we apply quick sort to the left and right parts, recursively. Under what circumstance does recursion terminate? Yes, `l >= u`.
+
+### Python
+
+```python
+#!/usr/bin/env python
+
+
+def qsort2(alist, l, u):
+ print(alist)
+ if l >= u:
+ return
+
+ m = l
+ for i in xrange(l + 1, u + 1):
+ if alist[i] < alist[l]:
+ m += 1
+ alist[m], alist[i] = alist[i], alist[m]
+ # swap between m and l after partition, important!
+ alist[m], alist[l] = alist[l], alist[m]
+ qsort2(alist, l, m - 1)
+ qsort2(alist, m + 1, u)
+
+unsortedArray = [6, 5, 3, 1, 8, 7, 2, 4]
+print(qsort2(unsortedArray, 0, len(unsortedArray) - 1))
+```
+
+### Java
+
+```java
+public class Sort {
+ public static void main(String[] args) {
+ int unsortedArray[] = new int[]{6, 5, 3, 1, 8, 7, 2, 4};
+ quickSort(unsortedArray);
+ System.out.println("After sort: ");
+ for (int item : unsortedArray) {
+ System.out.print(item + " ");
+ }
+ }
+
+ public static void quickSort1(int[] array, int l, int u) {
+ for (int item : array) {
+ System.out.print(item + " ");
+ }
+ System.out.println();
+
+ if (l >= u) return;
+ int m = l;
+ for (int i = l + 1; i <= u; i++) {
+ if (array[i] < array[l]) {
+ m += 1;
+ int temp = array[m];
+ array[m] = array[i];
+ array[i] = temp;
+ }
+ }
+ // swap between array[m] and array[l]
+ // put pivot in the mid
+ int temp = array[m];
+ array[m] = array[l];
+ array[l] = temp;
+
+ quickSort1(array, l, m - 1);
+ quickSort1(array, m + 1, u);
+ }
+
+ public static void quickSort(int[] array) {
+ quickSort1(array, 0, array.length - 1);
+ }
+}
+```
+
+The swap of $$x[i]$$ and $$x[m]$$ should not be left out.
+
+The output:
+
+```
+[6, 5, 3, 1, 8, 7, 2, 4]
+[4, 5, 3, 1, 2, 6, 8, 7]
+[2, 3, 1, 4, 5, 6, 8, 7]
+[1, 2, 3, 4, 5, 6, 8, 7]
+[1, 2, 3, 4, 5, 6, 8, 7]
+[1, 2, 3, 4, 5, 6, 8, 7]
+[1, 2, 3, 4, 5, 6, 8, 7]
+[1, 2, 3, 4, 5, 6, 7, 8]
+[1, 2, 3, 4, 5, 6, 7, 8]
+```
+
+### Two-way partitioning
+
+Another implementation is to use two indexes for partition. It speeds up the partition by working two-way simultaneously, both from lower bound toward right and from upper bound toward left, instead of traversing one-way through the sequence.
+
+The gif below shows the complete process on `[6, 5, 3, 1, 8, 7, 2, 4]`.
+
+
+
+1. Take `3` as the pivot.
+2. Let pointer `lo` start with number `6` and pointer `hi` start with number `4`. Keep increasing `lo` until it comes to an element ≥ the pivot, and decreasing `hi` until it comes to an element < the pivot. Then swap these two elements.
+3. Increase `lo` and decrease `hi` (both by 1), and repeat step 2 so that `lo` comes to `5` and `hi` comes to `1`. Swap again.
+4. Increase `lo` and decrease `hi` (both by 1) until they meet (at `3`). The partition for pivot `3` ends. Apply the same operations on the left and right part of pivot `3`.
+
+A more general interpretation:
+
+1. Init $$i$$ and $$j$$ to be at the two ends of given array.
+2. Take the first element as the pivot.
+3. Perform partition, which is a loop with two inner-loops:
+ - One that increases $$i$$, until it comes to an element ≥ pivot.
+ - The other that decreases $$j$$, until it comes to an element < pivot.
+4. Check whether $$i$$ and $$j$$ meet or overlap. If so, swap the elements.
+
+Think of a sequence whose elements are *all equal*. In such case, each partition will return the middle element, thus recursion will happen $$\log n$$ times. For each level of recursion, it takes $$n$$ times of comparison. The total comparison is $$n \log n$$ then. [^programming_pearls]
+
+### Python
+
+```python
+#!/usr/bin/env python
+
+def qsort3(alist, lower, upper):
+ print(alist)
+ if lower >= upper:
+ return
+
+ pivot = alist[lower]
+ left, right = lower + 1, upper
+ while left <= right:
+ while left <= right and alist[left] < pivot:
+ left += 1
+ while left <= right and alist[right] >= pivot:
+ right -= 1
+ if left > right:
+ break
+ # swap while left <= right
+ alist[left], alist[right] = alist[right], alist[left]
+ # swap the smaller with pivot
+ alist[lower], alist[right] = alist[right], alist[lower]
+
+ qsort3(alist, lower, right - 1)
+ qsort3(alist, right + 1, upper)
+
+unsortedArray = [6, 5, 3, 1, 8, 7, 2, 4]
+print(qsort3(unsortedArray, 0, len(unsortedArray) - 1))
+```
+
+### Java
+
+```java
+public class Sort {
+ public static void main(String[] args) {
+ int unsortedArray[] = new int[]{6, 5, 3, 1, 8, 7, 2, 4};
+ quickSort(unsortedArray);
+ System.out.println("After sort: ");
+ for (int item : unsortedArray) {
+ System.out.print(item + " ");
+ }
+ }
+
+ public static void quickSort2(int[] array, int l, int u) {
+ for (int item : array) {
+ System.out.print(item + " ");
+ }
+ System.out.println();
+
+ if (l >= u) return;
+ int pivot = array[l];
+ int left = l + 1;
+ int right = u;
+ while (left <= right) {
+ while (left <= right && array[left] < pivot) {
+ left++;
+ }
+ while (left <= right && array[right] >= pivot) {
+ right--;
+ }
+ if (left > right) break;
+ // swap array[left] with array[right] while left <= right
+ int temp = array[left];
+ array[left] = array[right];
+ array[right] = temp;
+ }
+ /* swap the smaller with pivot */
+ int temp = array[right];
+ array[right] = array[l];
+ array[l] = temp;
+
+ quickSort2(array, l, right - 1);
+ quickSort2(array, right + 1, u);
+ }
+
+ public static void quickSort(int[] array) {
+ quickSort2(array, 0, array.length - 1);
+ }
+}
+```
+
+The output:
+
+```
+[6, 5, 3, 1, 8, 7, 2, 4]
+[2, 5, 3, 1, 4, 6, 7, 8]
+[1, 2, 3, 5, 4, 6, 7, 8]
+[1, 2, 3, 5, 4, 6, 7, 8]
+[1, 2, 3, 5, 4, 6, 7, 8]
+[1, 2, 3, 5, 4, 6, 7, 8]
+[1, 2, 3, 4, 5, 6, 7, 8]
+[1, 2, 3, 4, 5, 6, 7, 8]
+[1, 2, 3, 4, 5, 6, 7, 8]
+[1, 2, 3, 4, 5, 6, 7, 8]
+[1, 2, 3, 4, 5, 6, 7, 8]
+```
+
+Having analyzed three implementations of quick sort, we may grasp one key difference between *quick sort* and *merge sort* :
+
+1. Merge sort divides the original array into two sub-arrays, and merges the sorted sub-arrays to form a totally ordered one. In this case, recursion happens before processing(merging) the whole array.
+2. Quick sort divides the original array into two sub-arrays, and then sort them. The whole array is ordered as soon as the sub-arrays get sorted. In this case, recursion happens after processing(partition) the whole array.
+
+Robert Sedgewick's presentation on [quick sort](http://algs4.cs.princeton.edu/23quicksort/) is strongly recommended.
+
+## Reference
+
+- [Quicksort - wikepedia](https://en.wikipedia.org/wiki/Quicksort)
+- [Quicksort | Robert Sedgewick](http://algs4.cs.princeton.edu/23quicksort/)
+- Programming Pearls Column 11 Sorting - gives an in-depth discussion on insertion sort and quick sort
+- [Quicksort Analysis](http://7xojrx.com1.z0.glb.clouddn.com/docs/algorithm-exercise/docs/quicksort_analysis.pdf)
+- [^programming_pearls]: Programming Pearls
diff --git a/en/binary_search/search_in_rotated_sorted_array.md b/en/binary_search/search_in_rotated_sorted_array.md
new file mode 100644
index 000000000..4b5ccdc1e
--- /dev/null
+++ b/en/binary_search/search_in_rotated_sorted_array.md
@@ -0,0 +1,225 @@
+# Search in Rotated Sorted Array
+
+## Question
+
+- leetcode: [(33) Search in Rotated Sorted Array](https://leetcode.com/problems/search-in-rotated-sorted-array/)
+- lintcode: [(62) Search in Rotated Sorted Array](http://www.lintcode.com/en/problem/search-in-rotated-sorted-array/)
+
+### Problem Statement
+
+Suppose a sorted array is rotated at some pivot unknown to you beforehand.
+
+(i.e., `0 1 2 4 5 6 7` might become `4 5 6 7 0 1 2`).
+
+You are given a target value to search. If found in the array return its
+index, otherwise return -1.
+
+You may assume no duplicate exists in the array.
+
+#### Example
+
+For `[4, 5, 1, 2, 3]` and `target=1`, return `2`.
+
+For `[4, 5, 1, 2, 3]` and `target=0`, return `-1`.
+
+#### Challenge
+
+O(logN) time
+
+## Solution1 - work on sorted subarray
+
+Draw it. Rotated sorted array will take one of the following two forms:
+
+
+
+Binary search does well in sorted array, while this problem gives an unordered one. Be patient. It is actually a combination of two sorted subarrayss. The solution takes full advantage of this. BTW, another approach can be comparing `target` with `A[mid]`, but dealing with lots of cases is kind of sophisticated.
+
+### C++
+
+```c++
+/**
+ * 本代码fork自
+ * http://www.jiuzhang.com/solutions/search-in-rotated-sorted-array/
+ */
+class Solution {
+ /**
+ * param A : an integer ratated sorted array
+ * param target : an integer to be searched
+ * return : an integer
+ */
+public:
+ int search(vector &A, int target) {
+ if (A.empty()) {
+ return -1;
+ }
+
+ vector::size_type start = 0;
+ vector::size_type end = A.size() - 1;
+ vector::size_type mid;
+
+ while (start + 1 < end) {
+ mid = start + (end - start) / 2;
+ if (target == A[mid]) {
+ return mid;
+ }
+ if (A[start] < A[mid]) {
+ // situation 1, numbers between start and mid are sorted
+ if (A[start] <= target && target < A[mid]) {
+ end = mid;
+ } else {
+ start = mid;
+ }
+ } else {
+ // situation 2, numbers between mid and end are sorted
+ if (A[mid] < target && target <= A[end]) {
+ start = mid;
+ } else {
+ end = mid;
+ }
+ }
+ }
+
+ if (A[start] == target) {
+ return start;
+ }
+ if (A[end] == target) {
+ return end;
+ }
+ return -1;
+ }
+};
+```
+
+### Java
+
+```java
+public class Solution {
+ /**
+ *@param A : an integer rotated sorted array
+ *@param target : an integer to be searched
+ *return : an integer
+ */
+ public int search(int[] A, int target) {
+ if (A == null || A.length == 0) return -1;
+
+ int lb = 0, ub = A.length - 1;
+ while (lb + 1 < ub) {
+ int mid = lb + (ub - lb) / 2;
+ if (A[mid] == target) return mid;
+
+ if (A[mid] > A[lb]) {
+ // case1: numbers between lb and mid are sorted
+ if (A[lb] <= target && target <= A[mid]) {
+ ub = mid;
+ } else {
+ lb = mid;
+ }
+ } else {
+ // case2: numbers between mid and ub are sorted
+ if (A[mid] <= target && target <= A[ub]) {
+ lb = mid;
+ } else {
+ ub = mid;
+ }
+ }
+ }
+
+ if (A[lb] == target) {
+ return lb;
+ } else if (A[ub] == target) {
+ return ub;
+ }
+ return -1;
+ }
+}
+```
+
+### Source Code Analysis
+
+1. If `target == A[mid]`, just return.
+2. Observe the two sorted subarrays, we can find that the least one of the left is greater than the biggest of the right. So if `A[start] < A[mid]`, then interval [start, mid] will be sorted.
+3. Do binary search on `A[start] ~ A[mid]` on condition that `A[start] <= target <= A[mid]`.
+4. Or do binary search on `A[mid]~A[end]` on condition that `A[mid] <= target <= A[end]`.
+5. If while loop ends and none `A[mid]` hits, then examine `A[start]` and `A[end]`.
+6. Return -1 if `target` is not found.
+
+### Complexity
+
+The time complexity is approximately ***O(log n)***.
+
+## Solution2 - double binary search
+
+Do binary search twice: first on the given array to find the break point; then on the proper piece of subarray to search for the target.
+
+It may take a small step to see why the given array is binary-searchable. Though a rotated array itself is neither sorted nor monotone, there is implicit monotonicity. All elements on the left of break point are ≥A[0], and those on the right of break point are = A[0]) {
+ // search in [lo, segPoint]
+ return binSearch(A, target, 0, p);
+ } else {
+ // search in [segPoint, hi]
+ return binSearch(A, target, p, A.length - 1);
+ }
+ }
+
+ private int findBreakPoint(int[] A) {
+ // A[index] < A[0], min[index]
+ int index;
+
+ int lo = 0, hi = A.length - 1, segValue = A[0];
+ while (lo + 1 < hi) {
+ int md = lo + (hi - lo)/2;
+ if (A[md] > segValue) {
+ lo = md;
+ } else {
+ hi = md;
+ }
+ }
+ index = A[lo] < segValue ? lo : hi;
+
+ return index;
+ }
+
+ private int binSearch(int[] A, int target, int lo, int hi) {
+ while (lo + 1 < hi) {
+ int md = lo + (hi - lo) / 2;
+ if (A[md] == target) {
+ lo = md;
+ } else if (A[md] < target) {
+ lo = md;
+ } else {
+ hi = md;
+ }
+ }
+
+ if (A[lo] == target) {
+ return lo;
+ }
+ if (A[hi] == target) {
+ return hi;
+ }
+ return -1;
+ }
+}
+```
+
+### Complexity
+
+The first binary search costs ***O(log n)*** time complexity, and the second costs no more than ***O(log n)***.
diff --git a/en/integer_array/kth_largest_element.md b/en/integer_array/kth_largest_element.md
new file mode 100644
index 000000000..774e502c6
--- /dev/null
+++ b/en/integer_array/kth_largest_element.md
@@ -0,0 +1,142 @@
+# Kth Largest Element in an Array
+
+Tags: Quick Sort, Divide and Conquer, Medium
+
+## Question
+
+- leetcode: [(215) Kth Largest Element in an Array](https://leetcode.com/problems/kth-largest-element-in-an-array/)
+- lintcode: [(5) Kth Largest Element](http://www.lintcode.com/en/problem/kth-largest-element/)
+
+### Problem Statement
+
+Find the **k**th largest element in an unsorted array. Note that it is the kth
+largest element in the sorted order, not the kth distinct element.
+
+For example,
+Given `[3,2,1,5,6,4]` and k = 2, return 5.
+
+**Note: **
+You may assume k is always valid, 1 ≤ k ≤ array's length.
+
+**Credits:**
+
+Special thanks to [@mithmatt](https://leetcode.com/discuss/user/mithmatt) for
+adding this problem and creating all test cases.
+
+## Solution
+
+Trail and error: Comparison-based sorting algorithms don't work because they incur ***O(n2)*** time complexity. Neither does Radix Sort which requires the elements to be in a certain range. In fact, Quick Sort is the answer to `kth largest` problems ([Here](http://algorithm.yuanbin.me/zh-hans/basics_sorting/quick_sort.html) are code templates of quick sort).
+
+By quick sorting, we get the final index of a pivot. And by comparing that index with `K`, we decide which side (the greater or the smaller) of the pivot to recurse on.
+
+### Java - Recursion
+
+```java
+public class Solution {
+ public int findKthLargest(int[] nums, int k) {
+ if (nums == null || nums.length == 0) {
+ return Integer.MIN_VALUE;
+ }
+
+ int kthLargest = qSort(nums, 0, nums.length - 1, k);
+ return kthLargest;
+ }
+
+ private int qSort(int[] nums, int left, int right, int k) {
+ if (left >= right) {
+ return nums[right];
+ }
+
+ int m = left;
+ for (int i = left + 1; i <= right; i++) {
+ if (nums[i] > nums[left]) {
+ m++;
+ swap(nums, m, i);
+ }
+ }
+ swap(nums, m, left);
+
+ if (k == m + 1) {
+ return nums[m];
+ } else if (k > m + 1) {
+ return qSort(nums, m + 1, right, k);
+ } else {
+ return qSort(nums, left, m - 1, k);
+ }
+ }
+
+ private void swap(int[] nums, int i, int j) {
+ int tmp = nums[i]; nums[i] = nums[j]; nums[j] = tmp;
+ }
+}
+```
+
+### Src Code Analysis
+
+Two cases when the recursion ceases:
+a. left bound equals right bound;
+b. final index of pivot equals K.
+
+Since 'Kth **largest**' is wanted, numbers greater than pivot are placed to the left and numbers smaller to the right, which is a little different with typical quick sort code.
+
+### Java - Iteration
+
+Recursive code is easier to read than to write, and it demands some experience and skill. Here is an iterative implementation.
+
+```
+class Solution {
+ public int findKthLargest(int[] A, int k) {
+ if (A == null || A.length == 0 || k < 0 || k > A.length) {
+ return -1;
+ }
+
+ int lo = 0, hi = A.length - 1;
+ while (lo <= hi) {
+ int idx = partition(A, lo, hi);
+ if (idx == k - 1) {
+ return A[idx];
+ } else if (idx < k - 1) {
+ lo = idx + 1;
+ } else {
+ hi = idx - 1;
+ }
+ }
+
+ return -1;
+ }
+
+ private int partition(int[] A, int lo, int hi) {
+ int pivot = A[lo], i = lo + 1, j = hi;
+ while (i <= j) {
+ while (i <= j && A[i] > pivot) {
+ i++;
+ }
+ while (i <= j && A[j] <= pivot) {
+ j--;
+ }
+ if (i < j) {
+ swap(A, i, j);
+ }
+ }
+ swap(A, lo, j);
+
+ return j;
+ }
+
+ private void swap(int[] A, int i, int j) {
+ int tmp = A[i];
+ A[i] = A[j];
+ A[j] = tmp;
+ }
+}
+```
+
+### Src Code Analysis
+
+The `while` loop in `findKthLargest` is very much like that in `binary search`. And `partition` method is just the same as quick sort partition.
+
+### Complexity
+
+Time Complexity. Worse case (when the array is sorted): ***n + n - 1 + ... + 1 = O(n^2)*** . Amortized complexity: ***n + n/2 + n/4 + ... + 1 = O(2n)=O(n)*** .
+
+Space complexity is ***O(1)*** .
diff --git a/en/integer_array/partition_array_by_odd_and_even.md b/en/integer_array/partition_array_by_odd_and_even.md
new file mode 100644
index 000000000..74bbf0475
--- /dev/null
+++ b/en/integer_array/partition_array_by_odd_and_even.md
@@ -0,0 +1,75 @@
+# Partition Array by Odd and Even
+
+## Question
+
+- lintcode: [(373) Partition Array by Odd and Even](http://www.lintcode.com/en/problem/partition-array-by-odd-and-even/)
+- [Segregate Even and Odd numbers - GeeksforGeeks](http://www.geeksforgeeks.org/segregate-even-and-odd-numbers/)
+
+```
+Partition an integers array into odd number first and even number second.
+
+Example
+Given [1, 2, 3, 4], return [1, 3, 2, 4]
+
+Challenge
+Do it in-place.
+```
+
+## Solution
+
+Use **two pointers** to keep the odd before the even, and swap when necessary.
+
+### Java
+
+```java
+public class Solution {
+ /**
+ * @param nums: an array of integers
+ * @return: nothing
+ */
+ public void partitionArray(int[] nums) {
+ if (nums == null) return;
+
+ int left = 0, right = nums.length - 1;
+ while (left < right) {
+ // odd number
+ while (left < right && nums[left] % 2 != 0) {
+ left++;
+ }
+ // even number
+ while (left < right && nums[right] % 2 == 0) {
+ right--;
+ }
+ // swap
+ if (left < right) {
+ int temp = nums[left];
+ nums[left] = nums[right];
+ nums[right] = temp;
+ }
+ }
+ }
+}
+```
+
+### C++
+
+```c++
+void partitionArray(vector &nums) {
+ if (nums.empty()) return;
+
+ int i=0, j=nums.size()-1;
+ while (i2->3, the reversed linked list is 3->2->1
+
+Challenge
+Reverse it in-place and in one-pass
+```
+
+## Solution1 - Non-recursively
+
+It would be much easier to reverse an array than a linked list, since array supports random access with index, while singly linked list can ONLY be operated through its head node. So an approach without index is required.
+
+Think about how '1->2->3' can become '3->2->1'. Starting from '1', we should turn '1->2' into '2->1', then '2->3' into '3->2', and so on. The key is how to swap two adjacent nodes.
+
+```
+temp = head -> next;
+head->next = prev;
+prev = head;
+head = temp;
+```
+
+The above code maintains two pointer, `prev` and `head`, and keeps record of next node before swapping. More detailed analysis:
+
+
+
+1. Keep record of next node
+2. change `head->next` to `prev`
+3. update `prev` with `head`, to keep moving forward
+4. update `head` with the record in step 1, for the sake of next loop
+
+### Python
+
+```python
+# Definition for singly-linked list.
+# class ListNode:
+# def __init__(self, x):
+# self.val = x
+# self.next = None
+
+class Solution:
+ # @param {ListNode} head
+ # @return {ListNode}
+ def reverseList(self, head):
+ prev = None
+ curr = head
+ while curr is not None:
+ temp = curr.next
+ curr.next = prev
+ prev = curr
+ curr = temp
+ # fix head
+ head = prev
+
+ return head
+```
+
+### C++
+
+```c++
+/**
+ * Definition for singly-linked list.
+ * struct ListNode {
+ * int val;
+ * ListNode *next;
+ * ListNode(int x) : val(x), next(NULL) {}
+ * };
+ */
+class Solution {
+public:
+ ListNode* reverse(ListNode* head) {
+ ListNode *prev = NULL;
+ ListNode *curr = head;
+ while (curr != NULL) {
+ ListNode *temp = curr->next;
+ curr->next = prev;
+ prev = curr;
+ curr = temp;
+ }
+ // fix head
+ head = prev;
+
+ return head;
+ }
+};
+```
+
+### Java
+
+```java
+/**
+ * Definition for singly-linked list.
+ * public class ListNode {
+ * int val;
+ * ListNode next;
+ * ListNode(int x) { val = x; }
+ * }
+ */
+public class Solution {
+ public ListNode reverseList(ListNode head) {
+ ListNode prev = null;
+ ListNode curr = head;
+ while (curr != null) {
+ ListNode temp = curr.next;
+ curr.next = prev;
+ prev = curr;
+ curr = temp;
+ }
+ // fix head
+ head = prev;
+
+ return head;
+ }
+}
+```
+
+### Source Code Analysis
+
+Already covered in the solution part. One more word, the assignment of `prev` is neat and skilled.
+
+### Complexity
+
+Traversing the linked list leads to ***O(n)*** time complexity, and auxiliary space complexity is ***O(1)***.
+
+## Solution2 - Recursively
+
+Three cases when the recursion ceases:
+
+1. If given linked list is null, just return.
+2. If given linked list has only one node, return that node.
+3. If given linked list has at least two nodes, pick out the head node and regard the following nodes as a sub-linked-list, swap them, then recurse that sub-linked-list.
+
+Be careful when swapping the head node (refer as `nodeY`) and head of the sub-linked-list (refer as `nodeX` ): First, swap `nodeY` and `nodeX`; Second, assign `null` to `nodeY->next` (or it would fall into infinite loop, and tail of result list won't point to `null`).
+
+### Python
+
+```python
+"""
+Definition of ListNode
+
+class ListNode(object):
+
+ def __init__(self, val, next=None):
+ self.val = val
+ self.next = next
+"""
+class Solution:
+ """
+ @param head: The first node of the linked list.
+ @return: You should return the head of the reversed linked list.
+ Reverse it in-place.
+ """
+ def reverse(self, head):
+ # case1: empty list
+ if head is None:
+ return head
+ # case2: only one element list
+ if head.next is None:
+ return head
+ # case3: reverse from the rest after head
+ newHead = self.reverse(head.next)
+ # reverse between head and head->next
+ head.next.next = head
+ # unlink list from the rest
+ head.next = None
+
+ return newHead
+```
+
+### C++
+
+```c++
+/**
+ * Definition of ListNode
+ *
+ * class ListNode {
+ * public:
+ * int val;
+ * ListNode *next;
+ *
+ * ListNode(int val) {
+ * this->val = val;
+ * this->next = NULL;
+ * }
+ * }
+ */
+class Solution {
+public:
+ /**
+ * @param head: The first node of linked list.
+ * @return: The new head of reversed linked list.
+ */
+ ListNode *reverse(ListNode *head) {
+ // case1: empty list
+ if (head == NULL) return head;
+ // case2: only one element list
+ if (head->next == NULL) return head;
+ // case3: reverse from the rest after head
+ ListNode *newHead = reverse(head->next);
+ // reverse between head and head->next
+ head->next->next = head;
+ // unlink list from the rest
+ head->next = NULL;
+
+ return newHead;
+ }
+};
+```
+
+### Java
+
+```java
+/**
+ * Definition for singly-linked list.
+ * public class ListNode {
+ * int val;
+ * ListNode next;
+ * ListNode(int x) { val = x; }
+ * }
+ */
+public class Solution {
+ public ListNode reverse(ListNode head) {
+ // case1: empty list
+ if (head == null) return head;
+ // case2: only one element list
+ if (head.next == null) return head;
+ // case3: reverse from the rest after head
+ ListNode newHead = reverse(head.next);
+ // reverse between head and head->next
+ head.next.next = head;
+ // unlink list from the rest
+ head.next = null;
+
+ return newHead;
+ }
+}
+```
+
+### Source Code Analysis
+
+case1 and case2 can be combined.What case3 returns is head of reversed list, which means it is exact the same Node (tail of origin linked list) through the recursion.
+
+### Complexity
+
+The depth of recursion: ***O(n)***. Time Complexity: ***O(N)***. Space Complexity (without considering the recursion stack): ***O(1)***.
+
+### Reference
+
+- [全面分析再动手的习惯:链表的反转问题(递归和非递归方式) - 木棉和木槿 - 博客园](http://www.cnblogs.com/kubixuesheng/p/4394509.html)
+- [data structures - Reversing a linked list in Java, recursively - Stack Overflow](http://stackoverflow.com/questions/354875/reversing-a-linked-list-in-java-recursively)
+- [反转单向链表的四种实现(递归与非递归,C++) | 宁心勉学,慎思笃行](http://ceeji.net/blog/reserve-linked-list-cpp/)
+- [iteratively and recursively Java Solution - Leetcode Discuss](https://leetcode.com/discuss/37804/iteratively-and-recursively-java-solution)
diff --git a/scripts/leetcode.py b/scripts/leetcode.py
deleted file mode 100644
index adad1c428..000000000
--- a/scripts/leetcode.py
+++ /dev/null
@@ -1,78 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-
-from selenium import webdriver
-
-
-class Leetcode(object):
-
- def __init__(self):
- chrome_options = webdriver.ChromeOptions()
- chrome_options.add_argument('--headless')
- chrome_options.add_argument('--disable-gpu')
- self.driver = webdriver.Chrome(chrome_options=chrome_options)
-
- def open_url(self, url):
- self.url = url
- print('open URL: {}'.format(url))
- self.driver.get(url)
-
- def teardown(self):
- self.driver.close()
-
- def get_title(self):
- print('get title...')
- raw_title = self.driver.title
- title = raw_title[:-len(' - LeetCode')].strip()
- return title
-
- def get_description(self):
- print('get description...')
- elem = self.driver.find_element_by_class_name('question-description')
- return elem.get_attribute('innerHTML')
-
- def get_difficulty(self):
- print('get difficulty...')
- elem = self.driver.find_element_by_class_name('difficulty-label')
- return elem.get_attribute('innerHTML')
-
- def get_tags(self):
- print('get tags...')
- tags_id = self.driver.find_element_by_id('tags-topics')
- tags_id_a = tags_id.find_elements_by_tag_name('a')
- tags = []
- for i in tags_id_a:
- tag = i.get_attribute('innerHTML')
- tags.append(tag)
- return tags
-
- def _clean_url(self, url):
- new_url = ['https:/', 'leetcode.com', 'problems']
- problem_slug = url[len('https://'):].strip('/').split('/')[2]
- new_url.append(problem_slug)
- return '/'.join(new_url)
-
- def get_problem_all(self, url):
- """获取所有细节"""
- print('get all the problem detail...')
- self.open_url(url)
- title = self.get_title()
- difficulty = self.get_difficulty()
- tags = self.get_tags()
- description = self.get_description()
- problem = {
- 'title': title,
- 'difficulty': difficulty,
- 'tags': tags,
- 'description': description,
- 'url': self._clean_url(url)
- }
- self.teardown()
- return problem
-
-
-if __name__ == '__main__':
- url = 'https://leetcode.com/problems/palindrome-number'
- leetcode = Leetcode()
- print(leetcode.get_problem_all(url))
diff --git a/scripts/lintcode.py b/scripts/lintcode.py
deleted file mode 100644
index 04d970ef2..000000000
--- a/scripts/lintcode.py
+++ /dev/null
@@ -1,78 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-
-from pyquery import PyQuery as pq
-
-
-class Lintcode(object):
-
- def __init__(self):
- self.driver = None
-
- def open_url(self, url):
- self.url = url
- print('open URL: {}'.format(url))
- self.driver = pq(url=url)
-
- def get_title(self):
- print('get title...')
- title = self.driver('title').text()
- return title
-
- def get_description(self):
- print('get description...')
- desc_pq = self.driver('#description')
- desc_html = desc_pq('.m-t-lg:nth-child(1)').html()
- example_html = desc_pq('.m-t-lg:nth-child(2)').html()
- return desc_html + example_html
-
- def get_difficulty(self):
- print('get difficulty...')
- progress_bar = self.driver('.progress-bar')
- original_title = progress_bar.attr('data-original-title')
- splits = original_title.strip().split(' ')
- difficulty = splits[1]
- ac_rate = splits[-1]
- return difficulty
-
- def get_tags(self):
- print('get tags...')
- tags = []
- for i in self.driver('#tags.tags a'):
- tags.append(i.text)
- return tags
-
- def _get_related(self):
- print('get related...')
- related = self.driver('.m-t-lg:last')
- return related
-
- def _clean_url(self, url):
- new_url = ['http:/', 'www.lintcode.com', 'en/problem']
- problem_slug = url[len('http://'):].strip('/').split('/')[3]
- new_url.append(problem_slug)
- return '/'.join(new_url)
-
- def get_problem_all(self, url):
- """获取所有细节"""
- print('get all the problem detail...')
- self.open_url(url)
- title = self.get_title()
- difficulty = self.get_difficulty()
- tags = self.get_tags()
- description = self.get_description()
- problem = {
- 'title': title,
- 'difficulty': difficulty,
- 'tags': tags,
- 'description': description,
- 'url': self._clean_url(url)
- }
- return problem
-
-
-if __name__ == '__main__':
- url = 'http://www.lintcode.com/en/problem/palindrome-number/'
- leetcode = Lintcode()
- print(leetcode.get_problem_all(url))
diff --git a/scripts/main.py b/scripts/main.py
index 440ea8dfd..04a422297 100644
--- a/scripts/main.py
+++ b/scripts/main.py
@@ -11,6 +11,7 @@
from util import par_dir, mkdir_p
from leetcode import Leetcode
from lintcode import Lintcode
+from summary import update_summary
from ojhtml2markdown import problem2md
BASEDIR = os.path.abspath(os.path.dirname(__file__))
@@ -36,18 +37,21 @@ def curr_time():
print('Called with arguments: {}'.format(args))
ROOTDIR = par_dir(BASEDIR)
- raw_url = args.new
- problem_md = ''
- problem_slug = ''
- xxxcode = None
- if raw_url.startswith('https://leetcode'):
- xxxcode = Leetcode()
- elif raw_url.startswith('http://www.lintcode.com'):
- xxxcode = Lintcode()
- problem = xxxcode.get_problem_all(raw_url)
- problem_slug = slugify(problem['title'], separator="_")
- problem_md = problem2md(problem)
-
+ if args.new:
+ raw_url = args.new
+ problem_md = ''
+ problem_slug = ''
+ xxxcode = None
+ convert_desc = True
+ if raw_url.startswith('https://leetcode'):
+ xxxcode = Leetcode()
+ elif raw_url.startswith('https://www.lintcode.com'):
+ xxxcode = Lintcode()
+ convert_desc = False
+ problem = xxxcode.get_problem_all(raw_url)
+ problem_slug = slugify(problem['title'], separator="_")
+ problem_md = problem2md(problem, convert_desc)
+
if args.dir:
post_dir = os.path.join(ROOTDIR, args.dir)
post_fn = os.path.join(post_dir, problem_slug + '.md')
diff --git a/scripts/ojhtml2markdown.py b/scripts/ojhtml2markdown.py
deleted file mode 100644
index b9904745f..000000000
--- a/scripts/ojhtml2markdown.py
+++ /dev/null
@@ -1,71 +0,0 @@
-#!/usr/bin/env python3
-"""Parse Leetcode/Lintcode html page to markdown."""
-
-import frontmatter
-import requests
-import html2text
-
-
-class YamlContent(object):
- def __init__(self, metadata, content):
- self.metadata_ = metadata
- self.content_ = content
-
- @property
- def metadata(self):
- return self.metadata_
-
- @property
- def content(self):
- return self.content_
-
- content = '# ' + title
- yaml_content = YamlContent(metadata, content)
-
-
-def leet_lint_url(url):
- problem_slug = url.strip('/').split('/')[-1]
- leetcode_url = 'https://leetcode.com/problems/{}/'.format(problem_slug)
- lintcode_url = 'http://www.lintcode.com/en/problem/{}/'.format(problem_slug)
- urls = {}
- for url in [leetcode_url, lintcode_url]:
- response = requests.head(url)
- if response.status_code != 404:
- if url.startswith('https://leetcode'):
- urls['leetcode'] = url
- elif url.startswith('http://www.lintcode'):
- urls['lintcode'] = url
- else:
- print('cannot find url with: {}'.format(url))
- return urls
-
-
-def problem2md(problem):
- metadata = {
- 'title': problem['title'],
- 'difficulty': problem['difficulty']
- }
- if problem['tags']:
- metadata['tags'] = problem['tags']
-
- description = problem['description']
- h = html2text.HTML2Text()
- description_md = h.handle(description)
-
- lines = []
- lines.append('# ' + problem['title'] + '\n')
- lines.append('## Problem\n')
- lines.append('### Metadata\n')
- if problem['tags']:
- lines.append('- tags: ' + ', '.join(problem['tags']))
- lines.append('- difficulty: ' + problem['difficulty'])
- urls = leet_lint_url(problem['url'])
- for k, v in urls.items():
- lines.append('- source({}): <{}>'.format(k, v))
- lines.append('\n### Description\n')
- lines.append(description_md)
-
- content = '\n'.join(lines)
- yaml_content = YamlContent(metadata, content)
- problem_md = frontmatter.dumps(yaml_content, allow_unicode=True)
- return problem_md
diff --git a/scripts/requirements.txt b/scripts/requirements.txt
deleted file mode 100644
index 833b824c2..000000000
--- a/scripts/requirements.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-certifi==2018.1.18
-chardet==3.0.4
-cssselect==1.0.3
-html2text==2018.1.9
-idna==2.6
-lxml==4.1.1
-pyquery==1.4.0
-python-frontmatter==0.4.2
-python-slugify==1.2.4
-PyYAML==3.12
-requests==2.18.4
-selenium==3.10.0
-six==1.11.0
-Unidecode==1.0.22
-urllib3==1.22
diff --git a/shared-files/images/linked_list_summary_en.png b/shared-files/images/linked_list_summary_en.png
new file mode 100644
index 000000000..6aefac2e7
Binary files /dev/null and b/shared-files/images/linked_list_summary_en.png differ
diff --git a/shared-files/images/topological-sorting.jpeg b/shared-files/images/topological-sorting.jpeg
new file mode 100644
index 000000000..87e4c5cbb
Binary files /dev/null and b/shared-files/images/topological-sorting.jpeg differ
diff --git a/zh-hans/README.md b/zh-hans/README.md
index 0fd351ca9..8930288c2 100644
--- a/zh-hans/README.md
+++ b/zh-hans/README.md
@@ -104,33 +104,9 @@ Slack 的自助邀请注册功能已启用,访问 , <
在循环中, 分三种情况讨论边界。 要注意, 在移动`start`和`end`的时候, 只要单纯的把指针指向`mid`的位置, 不要`+1`或者`-1`。 因为只移动边界到`mid`的位置, 不会误删除target。在工程中,尽量在程序最后的时候统一写`return`, 这样可以增强可读性。
4. A[start], A[end]? target
- 在循环结束时,因为只有1~2个元素需要讨论,所以结果非常容易解释清楚。 只存在的2种情况为, 1. `start + 1 == end` 边界指向相邻的两个元素, 这时只需要分情况讨论`start`和`end`与target的关系,就可以得出结果。 2. `start == end` 边界指向同一元素, 其实这个情况还是可以按照1的方法,分成`start``end`讨论,只不过讨论结果一样而已。
+ 在循环结束时,因为只有1~2个元素需要讨论,所以结果非常容易解释清楚。 只存在的2种情况为, 1. `start + 1 == end` 边界指向相邻的两个元素, 这时只需要分情况讨论`start`和`end`与target的关系,就可以得出结果。 2. `start == end` 边界指向同一元素, 其实这个情况还是可以按照1的方法,分成`start`与`end`讨论,只不过讨论结果一样而已。
### Python
```python
diff --git a/zh-hans/basics_data_structure/heap.md b/zh-hans/basics_data_structure/heap.md
index 2b691e04e..dc4fc3868 100644
--- a/zh-hans/basics_data_structure/heap.md
+++ b/zh-hans/basics_data_structure/heap.md
@@ -1,6 +1,6 @@
# Heap - 堆
-一般情况下,堆通常指的是**二叉堆**,**二叉堆**是一个近似**完全二叉树**的数据结构,**即披着二叉树羊皮的数组,**故使用数组来实现较为便利。子结点的键值或索引总是小于(或者大于)它的父节点,且每个节点的左右子树又是一个**二叉堆**(大根堆或者小根堆)。根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。**常被用作实现优先队列。**
+一般情况下,堆通常指的是**二叉堆**,**二叉堆**是一个近似**完全二叉树**的数据结构,但由于对二叉树平衡及插入/删除操作较为麻烦,二叉堆实际上使用数组来实现。即物理结构为数组,逻辑结构为完全二叉树。子结点的键值或索引总是小于(或者大于)它的父节点,且每个节点的左右子树又是一个**二叉堆**(大根堆或者小根堆)。根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。**常被用作实现优先队列。**
## 特点
@@ -15,15 +15,19 @@
以大根堆为例,堆的常用操作如下。
-1. 最大堆调整(Max_Heapify):将堆的末端子节点作调整,使得子节点永远小于父节点
-2. 创建最大堆(Build_Max_Heap):将堆所有数据重新排序
-3. 堆排序(HeapSort):移除位在第一个数据的根节点,并做最大堆调整的递归运算
+1. 最大堆调整:将堆的末端子节点作调整,使得子节点永远小于父节点
+2. 创建最大堆:将堆所有数据重新排序
+3. 堆排序:移除位于第一个数据的根节点,并做最大堆调整的递归运算
其中步骤1是给步骤2和3用的。

-## Python
+## 堆实现
+
+使用迭代实现也较为简单,注意索引值初始化和置位即可,尤其是记住最后一个索引的值 last,Java 的实现中 `poll()` 操作同时带有排序功能。
+
+### Python
```python
class MaxHeap:
@@ -38,7 +42,10 @@ class MaxHeap:
left, right = 2 * i + 1, 2 * i + 2
max_index = i
# should compare two chidren then determine which one to swap with
- flag = array[left] > array[right]
+ if left < len(array) and right < len(array):
+ flag = array[left] > array[right]
+ else:
+ flag = True
if left < len(array) and array[left] > array[max_index] and flag:
max_index = left
if right < len(array) and array[right] > array[max_index] and not flag:
@@ -71,6 +78,108 @@ class MaxHeap:
self._sink(self.heap, 0)
return item
```
+
+### Java
+
+```java
+import java.util.*;
+
+/**
+ * Created by billryan on 29/7/2018.
+ */
+public class MaxHeap {
+ private final int MAX_N = 10;
+ private final int[] heap = new int[MAX_N];
+ private int last = 0;
+
+ public int getLast() {
+ return last;
+ }
+
+ public void push(int x) {
+ int i = last++;
+ while (i > 0) {
+ int p = (i - 1) / 2;
+ if (heap[p] >= x) {
+ break;
+ }
+ heap[i] = heap[p];
+ i = p;
+ }
+ heap[i] = x;
+ }
+
+ public int pop() {
+ int result = heap[0];
+ int x = heap[--last];
+ heap[last] = result;
+
+ int i = 0;
+ while (2 * i + 1 < last) {
+ int left = 2 * i + 1, right = 2 * i + 2, swap = left;
+ if (right < last && heap[left] < heap[right]) {
+ swap = right;
+ }
+ if (heap[swap] <= x) {
+ break;
+ }
+
+ heap[i] = heap[swap];
+ i = swap;
+ }
+ heap[i] = x;
+
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("maxHeap: [");
+ for (int i = 0; i < last - 1; i++) {
+ sb.append(String.format("%d, ", heap[i]));
+ }
+ if (last > 0) {
+ sb.append(heap[last - 1]);
+ }
+ sb.append("]");
+ return sb.toString();
+ }
+
+ public static void main(String[] args) {
+ MaxHeap maxHeap = new MaxHeap();
+
+ int[] array = new int[]{6, 5, 3, 1, 8, 7, 2, 4, 10, 9};
+ for (int i : array) {
+ maxHeap.push(i);
+ System.out.println(maxHeap);
+ }
+
+ for (int i = maxHeap.getLast() - 1; i >= 0; i--) {
+ System.out.println("pop max heap value: " + maxHeap.pop());
+ System.out.println(maxHeap);
+ }
+
+ PriorityQueue pq = new PriorityQueue(10, Collections.reverseOrder());
+ for (int i : array) {
+ pq.offer(i);
+ System.out.println(pq);
+ }
+
+ // Top K problem
+ int k = 5;
+ for (int i = 0; i < k; i++) {
+ Integer topk = pq.poll();
+ if (topk != null) {
+ System.out.println("top " + (i + 1) + ": " + topk);
+ } else {
+ System.out.println("poll null value!!!");
+ }
+ }
+ }
+}
+```
+
## C++
```c++
#ifndef HEAP_H
diff --git a/zh-hans/basics_data_structure/huffman_compression.md b/zh-hans/basics_data_structure/huffman_compression.md
index 28290d960..7a9a48f41 100644
--- a/zh-hans/basics_data_structure/huffman_compression.md
+++ b/zh-hans/basics_data_structure/huffman_compression.md
@@ -58,7 +58,7 @@ class SimpleCompression:
def uncompress(self, bits):
string = ''
- for i in xrange(0, len(bits), self.bit_len):
+ for i in range(0, len(bits), self.bit_len):
string += self.b2s[bits[i:i + self.bit_len]]
return string
@@ -71,8 +71,14 @@ class HuffmanCompression:
self.coding = ''
self.left = self.right = None
- def __cmp__(self, other):
- return self.val - other.val
+ def __eq__(self, other):
+ return self.val == other.val
+
+ def __lt__(self, other):
+ return self.val < other.val
+
+ def __gt__(self, other):
+ return self.val > other.val
def __init__(self, string):
self.string = string
@@ -169,6 +175,4 @@ Huffman Compression-compress rate: 45%
简单压缩: 根据字符串出现的字符,将ASCII替换成更短的表示形式
霍夫曼压缩: 根据字符串出现频率,构建Trie树, 对每个tree node进行定义,使得频率越高的字符离root节点越近
-
-
有关霍夫曼编码的具体步骤可参考 [Huffman 编码压缩算法 | 酷 壳 - CoolShell.cn](http://coolshell.cn/articles/7459.html) 和 [霍夫曼编码 - 维基百科,自由的百科全书](http://zh.wikipedia.org/wiki/%E9%9C%8D%E5%A4%AB%E6%9B%BC%E7%BC%96%E7%A0%81),清晰易懂。
diff --git a/zh-hans/basics_data_structure/linked_list.md b/zh-hans/basics_data_structure/linked_list.md
index 29c111377..b94a78159 100644
--- a/zh-hans/basics_data_structure/linked_list.md
+++ b/zh-hans/basics_data_structure/linked_list.md
@@ -131,7 +131,7 @@ public ListNode reverse(ListNode head) {
class DListNode:
def __init__(self, val):
self.val = val
- self.prev = self.next = null
+ self.prev = self.next = None
def reverse(self, head):
curt = None
@@ -218,3 +218,39 @@ class NodeCircle:
else:
return False
```
+```c++: is a circle
+class List
+{
+public:
+bool iscircle(ListNode* head)
+{
+ if (!head || !head->next) return false;
+ ListNode *low = head, *fast = high;
+ while(fast && fast->next)
+ {
+ low = low->next;
+ fast = fast->next->next;
+ if (low == fast) return true;
+ }
+ return false;
+}
+};
+```
+
+```c++
+class List
+{
+public:
+ListNode* InLisNodet(ListNode *head)
+{
+ if (!head || !head->next) return head;
+ ListNode *low = head, *fast = head;
+ while(fast && fast->next)
+ {
+ low = low->next;
+ fast = fast->next->next;
+ }
+ return low;
+}
+};
+```
diff --git a/zh-hans/basics_data_structure/stack.md b/zh-hans/basics_data_structure/stack.md
index e76ee96fc..891b3ab19 100644
--- a/zh-hans/basics_data_structure/stack.md
+++ b/zh-hans/basics_data_structure/stack.md
@@ -18,7 +18,7 @@ stack = collections.deque()
#### Methods
-- `len(stack) != 0` - 判断`stack`是否weikong
+- `len(stack) != 0` - 判断`stack`是否为空
- `stack[-1]` - 取栈顶元素,不移除
- `pop()` - 移除栈顶元素并返回该元素
- `append(item)` - 向栈顶添加元素
diff --git a/zh-hans/basics_sorting/quick_sort.md b/zh-hans/basics_sorting/quick_sort.md
index 62908736b..fa35c034c 100644
--- a/zh-hans/basics_sorting/quick_sort.md
+++ b/zh-hans/basics_sorting/quick_sort.md
@@ -142,7 +142,7 @@ public class Sort {
}
```
-容易出错的地方在于当前 partition 结束时未将 $$i$$ 和 $$m$$ 交换。比较`alist[i]`和`alist[l]`时只能使用`<`而不是`<=`! 因为只有取`<`才能进入收敛条件,`<=`则可能会出现死循环,因为在`=`时第一个元素可能保持不变进而产生死循环。
+容易出错的地方在于当前 partition 结束时未将 $$i$$ 和 $$m$$ 交换。
相应的结果输出为:
@@ -182,6 +182,8 @@ public class Sort {
这样一来对于数组元素均相等的情形下,每次 partition 恰好在中间元素,故共递归调用 $$\log n$$ 次,每层递归调用进行 partition 操作的比较次数总和近似为 $$n$$. 故总计需 $$n \log n$$ 次比较。[^programming_pearls]
+可以推断出在最坏情况下,即数组本来就有序,这种方法仍然无法避免 $$O(n^2)$$ 的厄运... 即便如此,这种两边推进的方法大大提高了分区的效率,减少了 swap 的次数。
+
### Python
```python
diff --git a/zh-hans/bigdata/README.md b/zh-hans/bigdata/README.md
new file mode 100644
index 000000000..1d9eb6844
--- /dev/null
+++ b/zh-hans/bigdata/README.md
@@ -0,0 +1 @@
+ 本小节主要总结 bigdata 相关的题,涉及 MapReduce, Top K 等经典问题。
\ No newline at end of file
diff --git a/zh-hans/bigdata/k_closest_points.md b/zh-hans/bigdata/k_closest_points.md
new file mode 100644
index 000000000..e594dc3b2
--- /dev/null
+++ b/zh-hans/bigdata/k_closest_points.md
@@ -0,0 +1,114 @@
+---
+difficulty: Medium
+tags:
+- Heap
+- Amazon
+- LinkedIn
+title: K Closest Points
+---
+
+# K Closest Points
+
+## Problem
+
+### Metadata
+
+- tags: Heap, Amazon, LinkedIn
+- difficulty: Medium
+- source(lintcode):
+
+### Description
+
+Given some `points` and a point `origin` in two dimensional space, find `k` points out of the some points which are nearest to `origin`.
+Return these points sorted by distance, if they are same with distance, sorted by x-axis, otherwise sorted by y-axis.
+
+#### Example
+
+Given points = `[[4,6],[4,7],[4,4],[2,5],[1,1]]`, origin = `[0, 0]`, k = `3`
+return `[[1,1],[2,5],[4,4]]`
+
+## 题解
+
+和普通的字符串及数目比较,此题为距离的比较。
+
+### Java
+
+```java
+/**
+ * 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 points: a list of points
+ * @param origin: a point
+ * @param k: An integer
+ * @return: the k closest points
+ */
+ public Point[] kClosest(Point[] points, Point origin, int k) {
+ // write your code here
+ Queue heap = new PriorityQueue(new DistanceComparator(origin));
+ for (Point point : points) {
+ if (heap.size() < k) {
+ heap.offer(point);
+ } else {
+ Point peek = heap.peek();
+ if (distance(peek, origin) <= distance(point, origin)) {
+ continue;
+ } else {
+ heap.poll();
+ heap.offer(point);
+ }
+ }
+ }
+
+ int minK = Math.min(k, heap.size());
+ Point[] kClosestPoints = new Point[minK];
+ for (int i = 1; i <= minK; i++) {
+ kClosestPoints[minK - i] = heap.poll();
+ }
+
+ return kClosestPoints;
+ }
+
+ public int distance(Point p, Point origin) {
+ return (p.x - origin.x) * (p.x - origin.x) +
+ (p.y - origin.y) * (p.y - origin.y);
+ }
+
+ class DistanceComparator implements Comparator {
+ private Point origin = null;
+ public DistanceComparator(Point origin) {
+ this.origin = origin;
+ }
+
+ public int compare(Point p1, Point p2) {
+ int d1 = distance(p1, origin);
+ int d2 = distance(p2, origin);
+ if (d1 != d2) {
+ return d2 - d1;
+ } else {
+ if (p1.x != p2.x) {
+ return p2.x - p1.x;
+ } else {
+ return p2.y - p1.y;
+ }
+ }
+ }
+ }
+}
+```
+
+### 源码分析
+
+注意 Comparator 的用法和大小根堆的选择即可。
+
+### 复杂度分析
+
+堆的删除插入操作,最大为 K, 故时间复杂度为 $$O(n \log k)$$, 空间复杂度为 $$O(K)$$.
\ No newline at end of file
diff --git a/zh-hans/bigdata/top_k_frequent_words.md b/zh-hans/bigdata/top_k_frequent_words.md
new file mode 100644
index 000000000..db4d41a62
--- /dev/null
+++ b/zh-hans/bigdata/top_k_frequent_words.md
@@ -0,0 +1,139 @@
+---
+difficulty: Medium
+tags:
+- Pocket Gems
+- Hash Table
+- Amazon
+- Priority Queue
+- Bloomberg
+- Yelp
+- Heap
+- Uber
+- EditorsChoice
+title: Top K Frequent Words
+---
+
+# Top K Frequent Words
+
+## Problem
+
+### Metadata
+
+- tags: Pocket Gems, Hash Table, Amazon, Priority Queue, Bloomberg, Yelp, Heap, Uber, EditorsChoice
+- difficulty: Medium
+- source(lintcode):
+- source(leetcode):
+
+### Description
+
+Given a list of words and an integer k, return the top k frequent words in the list.
+
+#### Notice
+
+You should order the words by the frequency of them in the return list, the most frequent one comes first. If two words has the same frequency, the one with lower alphabetical order come first.
+
+#### Example
+
+Given
+
+ [
+ "yes", "lint", "code",
+ "yes", "code", "baby",
+ "you", "baby", "chrome",
+ "safari", "lint", "code",
+ "body", "lint", "code"
+ ]
+
+for k = `3`, return `["code", "lint", "baby"]`.
+
+for k = `4`, return `["code", "lint", "baby", "yes"]`,
+
+#### Challenge
+
+Do it in O(nlogk) time and O(n) extra space.
+
+## 题解
+
+输出出现频率最高的 K 个单词并对相同频率的单词按照字典序排列。如果我们使用大根堆维护,那么我们可以在输出结果时依次移除根节点即可。这种方法虽然可行,但不可避免会产生不少空间浪费,理想情况下,我们仅需要维护 K 个大小的堆即可。所以接下来的问题便是我们怎么更好地维护这种 K 大小的堆,并且在新增元素时剔除的是最末尾(最小)的节点。
+
+### Java
+
+```java
+public class Solution {
+ /**
+ * @param words: an array of string
+ * @param k: An integer
+ * @return: an array of string
+ */
+ public String[] topKFrequentWords(String[] words, int k) {
+ // write your code here
+ if (words == null || words.length == 0) return words;
+ if (k <= 0) return new String[0];
+
+ Map wordFreq = new HashMap<>();
+ for (String word : words) {
+ wordFreq.putIfAbsent(word, 0);
+ wordFreq.put(word, wordFreq.get(word) + 1);
+ }
+
+ PriorityQueue pq = new PriorityQueue(k);
+ for (Map.Entry entry : wordFreq.entrySet()) {
+ KeyFreq kf = new KeyFreq(entry.getKey(), entry.getValue());
+ if (pq.size() < k) {
+ pq.offer(kf);
+ } else {
+ KeyFreq peek = pq.peek();
+ if (peek.compareTo(kf) <= 0) {
+ pq.poll();
+ pq.offer(kf);
+ }
+ }
+ }
+
+ int topKSize = Math.min(k, pq.size());
+ String[] topK = new String[topKSize];
+ for (int i = 0; i < k && !pq.isEmpty(); i++) {
+ topK[i] = pq.poll().key;
+ }
+
+ // reverse array
+ for (int i = 0, j = topKSize - 1; i < j; i++, j--) {
+ String temp = topK[i];
+ topK[i] = topK[j];
+ topK[j] = temp;
+ }
+
+ return topK;
+ }
+
+ class KeyFreq implements Comparable {
+ String key;
+ int freq;
+
+ public KeyFreq(String key, int freq) {
+ this.key = key;
+ this.freq = freq;
+ }
+
+ @Override
+ public int compareTo(KeyFreq kf) {
+ if (this.freq != kf.freq) {
+ return this.freq - kf.freq;
+ }
+
+ return kf.key.compareTo(this.key);
+ }
+ }
+}
+```
+
+
+### 源码分析
+
+使用 Java 自带的 PriorityQueue 来实现堆,由于需要定制大小比较,所以这里自定义类中实现了 `Comparable` 的 `compareTo` 接口,另外需要注意的是这里原生使用了小根堆,所以我们在覆写 `compareTo` 时需要注意字符串的比较,相同频率的按照字典序排序,即优先保留字典序较小的字符串,所以正好和 freq 的比较相反。最后再输出答案时,由于是小根堆,所以还需要再转置一次。此题的 Java 实现中,使用的 PriorityQueue 并非线程安全,实际使用中需要注意是否需要用到线程安全的 PriorityBlockingQueue
+
+对于 Java, 虽然标准库中暂未有定长的 PriorityQueue 实现,但是我们常用的 Google guava 库中其实已有类似实现,见 [MinMaxPriorityQueue](https://google.github.io/guava/releases/snapshot/api/docs/com/google/common/collect/MinMaxPriorityQueue.html) 不必再自己造轮子了。
+
+### 复杂度分析
+
+堆的插入删除操作,定长为 K, n 个元素,故时间复杂度约 $$O(n \log K)$$, 空间复杂度为 $$O(n)$$.
\ No newline at end of file
diff --git a/zh-hans/bigdata/top_k_frequent_words_ii.md b/zh-hans/bigdata/top_k_frequent_words_ii.md
new file mode 100644
index 000000000..be0d7042a
--- /dev/null
+++ b/zh-hans/bigdata/top_k_frequent_words_ii.md
@@ -0,0 +1,120 @@
+---
+difficulty: Hard
+tags:
+- Heap
+- Data Structure Design
+- Hash Table
+title: Top K Frequent Words II
+---
+
+# Top K Frequent Words II
+
+## Problem
+
+### Metadata
+
+- tags: Heap, Data Structure Design, Hash Table
+- difficulty: Hard
+- source(lintcode):
+
+### Description
+
+Find top *k* frequent words in realtime data stream.
+
+Implement three methods for *Topk* Class:
+
+1. `TopK(k)`. The constructor.
+2. `add(word)`. Add a new word.
+3. `topk()`. Get the current top *k* frequent words.
+
+#### Notice
+
+If two words have the same frequency, rank them by alphabet.
+
+#### Example
+
+```
+TopK(2)
+add("lint")
+add("code")
+add("code")
+topk()
+>> ["code", "lint"]
+```
+
+## 题解
+
+此题较难,实际上和 Redis 的有序集合类似,综合使用字典和排序集合可完美解决。
+
+### Java
+
+```java
+public class TopK {
+ private int k;
+ private Map wordFreq = null;
+ private TreeSet topkSet = null;
+
+ class TopkComparator implements Comparator {
+ public int compare(String s1, String s2) {
+ int s1Freq = wordFreq.get(s1), s2Freq = wordFreq.get(s2);
+ if (s1Freq != s2Freq) {
+ return s2Freq - s1Freq;
+ } else {
+ return s1.compareTo(s2);
+ }
+ }
+ }
+
+ /*
+ * @param k: An integer
+ */public TopK(int k) {
+ // do intialization if necessary
+ this.k = k;
+ wordFreq = new HashMap(k);
+ topkSet = new TreeSet(new TopkComparator());
+ }
+
+ /*
+ * @param word: A string
+ * @return: nothing
+ */
+ public void add(String word) {
+ // write your code here
+ if (wordFreq.containsKey(word)) {
+ if (topkSet.contains(word)) {
+ topkSet.remove(word);
+ }
+ wordFreq.put(word, wordFreq.get(word) + 1);
+ } else {
+ wordFreq.put(word, 1);
+ }
+
+ topkSet.add(word);
+ if (topkSet.size() > k) {
+ topkSet.pollLast();
+ }
+ }
+
+ /*
+ * @return: the current top k frequent words.
+ */
+ public List topk() {
+ // write your code here
+ List result = new ArrayList(k);
+ Iterator it = topkSet.iterator();
+ while (it.hasNext()) {
+ result.add(it.next());
+ }
+
+ return result;
+ }
+}
+```
+
+### 源码分析
+
+略
+
+### 复杂度分析
+
+待续
\ No newline at end of file
diff --git a/zh-hans/bigdata/top_k_frequent_words_map_reduce.md b/zh-hans/bigdata/top_k_frequent_words_map_reduce.md
new file mode 100644
index 000000000..d1f68ea54
--- /dev/null
+++ b/zh-hans/bigdata/top_k_frequent_words_map_reduce.md
@@ -0,0 +1,189 @@
+---
+difficulty: Medium
+tags:
+- Big Data
+- Map Reduce
+- EditorsChoice
+title: Top K Frequent Words (Map Reduce)
+---
+
+# Top K Frequent Words (Map Reduce)
+
+## Problem
+
+### Metadata
+
+- tags: Big Data, Map Reduce, EditorsChoice
+- difficulty: Medium
+- source(lintcode):
+
+### Description
+
+Find top k frequent words with map reduce framework.
+
+The mapper's key is the document id, value is the content of the document, words in a document are split by spaces.
+
+For reducer, the output should be at most k key-value pairs, which are the top k words and their frequencies in this reducer. The judge will take care about how to merge different reducers' results to get the global top k frequent words, so you don't need to care about that part.
+
+The *k* is given in the constructor of TopK class.
+
+#### Notice
+
+For the words with same frequency, rank them with alphabet.
+
+#### Example
+
+Given document A =
+```
+lintcode is the best online judge
+I love lintcode
+```
+and document B =
+```
+lintcode is an online judge for coding interview
+you can test your code online at lintcode
+```
+
+The top 2 words and their frequencies should be
+```
+lintcode, 4
+online, 3
+```
+
+## 题解
+
+使用 Map Reduce 来做 Top K, 相比传统的 Top K 多了 Map 和 Reduce 这两大步骤。Map Reduce 模型实际上是在处理分布式问题时总结出的抽象模型,主要分为 Map 和 Reduce 两个阶段。
+
+- Map 阶段:数据分片,每个分片由一个 Map task 处理,不进行分片则无法分布式处理
+- Reduce 阶段:并行对前一阶段的结果进行规约处理并得到最终最终结果
+
+实际的 MapReduce 编程模型可由以下5个分布式步骤组成:
+
+1. 将输入数据解析为 `` 对
+2. 将输入的 `` map 为另一种 ``
+3. 根据 key 对 map 阶段的数据分组
+4. 对上一阶段的分组数据进行规约(Reduce) 并生成新的 ``
+5. 进一步处理 Reduce 阶段的数据并进行持久化
+
+根据题意,我们只需要实现 Map, Reduce 这两个步骤即可,输出出现频率最高的 K 个单词并对相同频率的单词按照字典序排列。如果我们使用大根堆维护,那么我们可以在输出结果时依次移除根节点即可。这种方法虽然可行,但不可避免会产生不少空间浪费,理想情况下,我们仅需要维护 K 个大小的堆即可。所以接下来的问题便是我们怎么更好地维护这种 K 大小的堆,并且在新增元素时剔除的是最末尾(最小)的节点。
+
+### Java
+
+```java
+/**
+ * Definition of OutputCollector:
+ * class OutputCollector {
+ * public void collect(K key, V value);
+ * // Adds a key/value pair to the output buffer
+ * }
+ * Definition of Document:
+ * class Document {
+ * public int id;
+ * public String content;
+ * }
+ */
+
+class KeyFreq implements Comparable {
+ public String key = null;
+ public int freq = 0;
+
+ public KeyFreq(String key, int freq) {
+ this.key = key;
+ this.freq = freq;
+ }
+
+ @Override
+ public int compareTo(KeyFreq kf) {
+ if (kf.freq != this.freq) {
+ return this.freq - kf.freq;
+ }
+
+ // keep small alphabet
+ return kf.key.compareTo(this.key);
+ }
+}
+
+public class TopKFrequentWords {
+
+ public static class Map {
+ public void map(String _, Document value,
+ OutputCollector output) {
+ // Write your code here
+ // Output the results into output buffer.
+ // Ps. output.collect(String key, int value);
+ if (value == null || value.content == null) return;
+
+ String[] splits = value.content.split(" ");
+ for (String split : splits) {
+ if (split.length() > 0) {
+ output.collect(split, 1);
+ }
+ }
+ }
+ }
+
+ public static class Reduce {
+
+ private int k = 0;
+ private PriorityQueue pq = null;
+
+ public void setup(int k) {
+ // initialize your data structure here
+ this.k = k;
+ pq = new PriorityQueue(k);
+ }
+
+ public void reduce(String key, Iterator values) {
+ int sum = 0;
+ while (values.hasNext()) {
+ int value = values.next();
+ sum += value;
+ }
+
+ KeyFreq kf = new KeyFreq(key, sum);
+
+ if (pq.size() < k) {
+ pq.offer(kf);
+ } else {
+ KeyFreq peekKf = pq.peek();
+ if (peekKf.compareTo(kf) <= 0) {
+ pq.poll();
+ pq.offer(kf);
+ }
+ }
+ }
+
+ public void cleanup(OutputCollector output) {
+ // Output the top k pairs into output buffer.
+ // Ps. output.collect(String key, Integer value);
+
+ List kfList = new ArrayList(k);
+ for (int i = 0; i < k && (!pq.isEmpty()); i++) {
+ kfList.add(pq.poll());
+ }
+
+ // get max k from min-heapqueue
+ int kfLen = kfList.size();
+ for (int i = 0; i < kfLen; i++) {
+ KeyFreq kf = kfList.get(kfLen - i - 1);
+ output.collect(kf.key, kf.freq);
+ }
+ }
+ }
+}
+```
+
+### 源码分析
+
+使用 Java 自带的 PriorityQueue 来实现堆,由于需要定制大小比较,所以这里自定义类中实现了 `Comparable` 的 `compareTo` 接口,另外需要注意的是这里原生使用了小根堆,所以我们在覆写 `compareTo` 时需要注意字符串的比较,相同频率的按照字典序排序,即优先保留字典序较小的字符串,所以正好和 freq 的比较相反。最后再输出答案时,由于是小根堆,所以还需要再转置一次。此题的 Java 实现中,使用的 PriorityQueue 并非线程安全,实际使用中需要注意是否需要用到线程安全的 PriorityBlockingQueue
+
+对于 Java, 虽然标准库中暂未有定长的 PriorityQueue 实现,但是我们常用的 Google guava 库中其实已有类似实现,见 [MinMaxPriorityQueue](https://google.github.io/guava/releases/snapshot/api/docs/com/google/common/collect/MinMaxPriorityQueue.html) 不必再自己造轮子了。
+
+### 复杂度分析
+
+堆的插入删除操作,定长为 K, n 个元素,故时间复杂度约 $$O(n \log K)$$, 空间复杂度为 $$O(n)$$.
+
+## Reference
+
+- 《大数据技术体系详解》——董西成,MapReduce 编程模型
+- [九章算法 - topk-mapreduce](https://www.jiuzhang.com/solution/top-k-frequent-words-map-reduce/)
\ No newline at end of file
diff --git a/zh-hans/bigdata/top_k_largest_numbers.md b/zh-hans/bigdata/top_k_largest_numbers.md
new file mode 100644
index 000000000..2232dccd5
--- /dev/null
+++ b/zh-hans/bigdata/top_k_largest_numbers.md
@@ -0,0 +1,65 @@
+---
+difficulty: Medium
+tags:
+- Priority Queue
+- Heap
+title: Top k Largest Numbers
+---
+
+# Top k Largest Numbers
+
+## Problem
+
+### Metadata
+
+- tags: Priority Queue, Heap
+- difficulty: Medium
+- source(lintcode):
+
+### Description
+
+Given an integer array, find the top *k* largest numbers in it.
+
+#### Example
+
+Given `[3,10,1000,-99,4,100]` and *k* = `3`.
+Return `[1000, 100, 10]`.
+
+## 题解
+
+简单题,使用堆即可。
+
+### Java
+
+```java
+public class Solution {
+ /**
+ * @param nums: an integer array
+ * @param k: An integer
+ * @return: the top k largest numbers in array
+ */
+ public int[] topk(int[] nums, int k) {
+ if (nums == null || nums.length <= 1) return nums;
+
+ PriorityQueue pq = new PriorityQueue(nums.length, Collections.reverseOrder());
+ for (int num : nums) {
+ pq.offer(num);
+ }
+
+ int[] maxK = new int[k];
+ for (int i = 0; i < k; i++) {
+ maxK[i] = pq.poll();
+ }
+
+ return maxK;
+ }
+}
+```
+
+### 源码分析
+
+略
+
+### 复杂度分析
+
+略
\ No newline at end of file
diff --git a/zh-hans/bigdata/top_k_largest_numbers_ii.md b/zh-hans/bigdata/top_k_largest_numbers_ii.md
new file mode 100644
index 000000000..2f3589ceb
--- /dev/null
+++ b/zh-hans/bigdata/top_k_largest_numbers_ii.md
@@ -0,0 +1,102 @@
+---
+difficulty: Medium
+tags:
+- Priority Queue
+- Heap
+- Data Stream
+title: Top k Largest Numbers II
+---
+
+# Top k Largest Numbers II
+
+## Problem
+
+### Metadata
+
+- tags: Priority Queue, Heap, Data Stream
+- difficulty: Medium
+- source(lintcode):
+
+### Description
+
+Implement a data structure, provide two interfaces:
+
+1. `add(number)`. Add a new number in the data structure.
+2. `topk()`. Return the top *k* largest numbers in this data structure. *k* is given when we create the data structure.
+
+#### Example
+
+```
+s = new Solution(3);
+>> create a new data structure.
+s.add(3)
+s.add(10)
+s.topk()
+>> return [10, 3]
+s.add(1000)
+s.add(-99)
+s.topk()
+>> return [1000, 10, 3]
+s.add(4)
+s.topk()
+>> return [1000, 10, 4]
+s.add(100)
+s.topk()
+>> return [1000, 100, 10]
+```
+
+## 题解
+
+此题只用堆的话在最后的排序输出会比较难受,最后用 List 的排序也可以。
+
+### Java
+
+```java
+public class Solution {
+ private int k = -1;
+ private Queue heap = null;
+ /*
+ * @param k: An integer
+ */public Solution(int k) {
+ // do intialization if necessary
+ this.k = k;
+ heap = new PriorityQueue(k);
+ }
+
+ /*
+ * @param num: Number to be added
+ * @return: nothing
+ */
+ public void add(int num) {
+ // write your code here
+ if (heap.size() < k) {
+ heap.offer(num);
+ } else if (heap.peek() < num) {
+ heap.poll();
+ heap.offer(num);
+ }
+ }
+
+ /*
+ * @return: Top k element
+ */
+ public List topk() {
+ // write your code here
+ List result = new ArrayList<>(k);
+ Iterator it = heap.iterator();
+ while(it.hasNext()) {
+ result.add(it.next());
+ }
+ result.sort(Collections.reverseOrder());
+ return result;
+ }
+}
+```
+
+### 源码分析
+
+略
+
+### 复杂度分析
+
+略
\ No newline at end of file
diff --git a/zh-hans/binary_search/search_in_rotated_sorted_array.md b/zh-hans/binary_search/search_in_rotated_sorted_array.md
index 4d93bdbca..3b12c5b49 100644
--- a/zh-hans/binary_search/search_in_rotated_sorted_array.md
+++ b/zh-hans/binary_search/search_in_rotated_sorted_array.md
@@ -1,18 +1,35 @@
+---
+difficulty: Medium
+tags:
+- Binary Search
+- LinkedIn
+- Array
+- Facebook
+- Sorted Array
+- Uber
+- Microsoft
+- Bloomberg
+title: Search in Rotated Sorted Array
+---
+
# Search in Rotated Sorted Array
-## Question
+## Problem
+
+### Metadata
-- leetcode: [Search in Rotated Sorted Array | LeetCode OJ](https://leetcode.com/problems/search-in-rotated-sorted-array/)
-- lintcode: [(62) Search in Rotated Sorted Array](http://www.lintcode.com/en/problem/search-in-rotated-sorted-array/)
+- tags: Binary Search, LinkedIn, Array, Facebook, Sorted Array, Uber, Microsoft, Bloomberg
+- difficulty: Medium
+- source(leetcode):
+- source(lintcode):
-### Problem Statement
+### Description
Suppose a sorted array is rotated at some pivot unknown to you beforehand.
(i.e., `0 1 2 4 5 6 7` might become `4 5 6 7 0 1 2`).
-You are given a target value to search. If found in the array return its
-index, otherwise return -1.
+You are given a target value to search. If found in the array return its index, otherwise return -1.
You may assume no duplicate exists in the array.
@@ -26,7 +43,7 @@ For `[4, 5, 1, 2, 3]` and `target=0`, return `-1`.
O(logN) time
-## 题解 - 找到有序数组
+## 题解1 - 找到有序数组
对于旋转数组的分析可使用画图的方法,如下图所示,升序数组经旋转后可能为如下两种形式。
@@ -146,3 +163,80 @@ public class Solution {
### 复杂度分析
分两段二分,时间复杂度仍近似为 $$O(\log n)$$.
+
+## 题解2 - 应用两次二分
+
+应用两次二分搜索:第一次是找到分段点,第二次是对分段点两边的有序数组(之一)进行搜索。后者非常简单,关键是第一步怎么找分段点。
+
+乍一看,有序数组经过旋转就不再有序、也不单调了,好像用不了二分。其实不然,分段点左边的元素全都 ≥A[0]、右边元素全都 = A[0]) {
+ // search in [lo, segPoint]
+ return binSearch(A, target, 0, p);
+ } else {
+ // search in [segPoint, hi]
+ return binSearch(A, target, p, A.length - 1);
+ }
+ }
+
+ private int findBreakPoint(int[] A) {
+ // A[index] < A[0], min[index]
+ int index;
+
+ int lo = 0, hi = A.length - 1, segValue = A[0];
+ while (lo + 1 < hi) {
+ int md = lo + (hi - lo)/2;
+ if (A[md] > segValue) {
+ lo = md;
+ } else {
+ hi = md;
+ }
+ }
+ index = A[lo] < segValue ? lo : hi;
+
+ return index;
+ }
+
+ private int binSearch(int[] A, int target, int lo, int hi) {
+ while (lo + 1 < hi) {
+ int md = lo + (hi - lo) / 2;
+ if (A[md] == target) {
+ lo = md;
+ } else if (A[md] < target) {
+ lo = md;
+ } else {
+ hi = md;
+ }
+ }
+
+ if (A[lo] == target) {
+ return lo;
+ }
+ if (A[hi] == target) {
+ return hi;
+ }
+ return -1;
+ }
+}
+```
+
+### 复杂度分析
+
+第一次二分找段点时间复杂度为 ***O(log n)*** , 第二次在局部有序数组上二分时间复杂度不超过 ***O(log n)*** , 总起来还是近似 ***O(log n)*** .
diff --git a/zh-hans/binary_search/search_in_rotated_sorted_array_ii.md b/zh-hans/binary_search/search_in_rotated_sorted_array_ii.md
index faa162c97..ce96ce032 100644
--- a/zh-hans/binary_search/search_in_rotated_sorted_array_ii.md
+++ b/zh-hans/binary_search/search_in_rotated_sorted_array_ii.md
@@ -102,7 +102,7 @@ public class Solution {
ub = mid;
}
} else {
- // case3: A[mid] == target
+ // case3: A[mid] == A[lb]
lb++;
}
}
@@ -117,7 +117,7 @@ public class Solution {
### 源码分析
-在`A[start] == A[mid]`时递增start序号即可。
+在`A[lb] == A[mid]`时递增lb序号即可。
### 复杂度分析
diff --git a/zh-hans/exhaustive_search/combination_sum.md b/zh-hans/exhaustive_search/combination_sum.md
index 2bd216eb4..0ebb9d349 100644
--- a/zh-hans/exhaustive_search/combination_sum.md
+++ b/zh-hans/exhaustive_search/combination_sum.md
@@ -1,33 +1,50 @@
# Combination Sum
+Tags: Array, Backtracking, Medium
+
## Question
-- leetcode: [Combination Sum | LeetCode OJ](https://leetcode.com/problems/combination-sum/)
-- lintcode: [(135) Combination Sum](http://www.lintcode.com/en/problem/combination-sum/)
+- leetcode: [Combination Sum](https://leetcode.com/problems/combination-sum/)
+- lintcode: [Combination Sum](https://www.lintcode.com/problem/combination-sum/)
+
+### Problem Statement
+
+Given a **set** of candidate numbers (`candidates`) **(without duplicates)**
+and a target number (`target`), find all unique combinations in `candidates`
+where the candidate numbers sums to `target`.
+
+The **same** repeated number may be chosen from `candidates` unlimited number
+of times.
+
+**Note:**
+
+ * All numbers (including `target`) will be positive integers.
+ * The solution set must not contain duplicate combinations.
+
+**Example 1:**
+
+
+
+ **Input:** candidates = [2,3,6,7], target = 7,
+ **A solution set is:**
+ [
+ [7],
+ [2,2,3]
+ ]
+
+
+**Example 2:**
+
+
+
+ **Input:** candidates = [2,3,5], target = 8,
+ **A solution set is:**
+ [
+ [2,2,2,2],
+ [2,3,3],
+ [3,5]
+ ]
-```
-Given a set of candidate numbers (C) and a target number (T),
-find all unique combinations in C where the candidate numbers sums to T.
-The same repeated number may be chosen from C unlimited number of times.
-
-For example, given candidate set 2,3,6,7 and target 7,
-A solution set is:
-[7]
-[2, 2, 3]
-
-Have you met this question in a real interview? Yes
-Example
-given candidate set 2,3,6,7 and target 7,
-A solution set is:
-[7]
-[2, 2, 3]
-
-Note
-- All numbers (including target) will be positive integers.
-- Elements in a combination (a1, a2, … , ak) must be in non-descending order.
-(ie, a1 ≤ a2 ≤ … ≤ ak).
-- The solution set must not contain duplicate combinations.
-```
## 题解
diff --git a/zh-hans/graph/topological_sorting.md b/zh-hans/graph/topological_sorting.md
index 079b59e64..cfba6f2af 100644
--- a/zh-hans/graph/topological_sorting.md
+++ b/zh-hans/graph/topological_sorting.md
@@ -1,42 +1,67 @@
+---
+difficulty: Medium
+tags:
+- Geeks for Geeks
+- Topological Sort
+- LintCode Copyright
+- BFS
+- DFS
+title: Topological Sorting
+---
+
# Topological Sorting
-## Question
+## Problem
-- lintcode: [(127) Topological Sorting](http://www.lintcode.com/en/problem/topological-sorting/)
-- [Topological Sorting - GeeksforGeeks](http://www.geeksforgeeks.org/topological-sorting/)
+### Metadata
+
+- tags: Geeks for Geeks, Topological Sort, LintCode Copyright, BFS, DFS
+- difficulty: Medium
+- source(lintcode):
+- source(geeksforgeeks):
+
+### Description
-```
Given an directed graph, a topological order of the graph nodes is defined as follow:
-For each directed edge A -> B in graph, A must before B in the order list.
-The first node in the order can be any node in the graph with no nodes direct to it.
+- For each directed edge `A -> B` in graph, A must before B in the order list.
+- The first node in the order can be any node in the graph with no nodes direct to it.
+
Find any topological order for the given graph.
-```
-Example
-For graph as follow:
-
+#### Notice
+
+You can assume that there is at least one topological order in the graph.
+
+#### Clarification
+
+[Learn more about representation of graphs](http://www.lintcode.com/help/graph "Graph example")
+
+#### Example
+
+For graph as follow:
+
+
+
-```
The topological order can be:
-[0, 1, 2, 3, 4, 5]
-[0, 2, 3, 1, 5, 4]
-...
-Note
-You can assume that there is at least one topological order in the graph.
+ [0, 1, 2, 3, 4, 5]
+ [0, 2, 3, 1, 5, 4]
+ ...
+
+#### Challenge
-Challenge
Can you do it in both BFS and DFS?
-```
-## 题解1 - DFS and BFS
+## 题解1 - DFS(统计节点入度数)
-图搜索相关的问题较为常见的解法是用 DFS,这里结合 BFS 进行求解,分为三步走:
+图搜索相关的问题较为常见的解法是用 DFS 或者 BFS,这里我们先分析一下拓扑排序的核心要求:对于有向边 `A -> B`, A 需要出现在 B 之前。用过 Linux/MAC 的人对包管理工具肯定不陌生,如 apt-get, yum, pacman, brew 等,安装一项软件时往往要先将其所有依赖的软件包安装完,拓扑排序解决的就是此类问题。这个需求实现起来大概可以分为如下几个步骤:
-1. 统计各定点的入度——只需统计节点在邻接列表中出现的次数即可知。
-2. 遍历图中各节点,找到入度为0的节点。
-3. 对入度为0的节点进行递归 DFS,将节点加入到最终返回结果中。
+1. 找出不依赖其他顶点的顶点,即入度为0,这一定是符合要求的某一个拓扑排序的第一个顶点。
+2. 在图中去掉入度为 0 的顶点,并重新计算一次各顶点的入度,递归调用邻居节点,迭代第一步。
+
+在具体实现中,考虑到每次取出某个顶点时重新计算其余顶点的入度存在较多重复计算,我们可以将计算量缩减到只计算入度有变化的部分顶点,即所取出顶点的邻居节点。
### C++
@@ -105,27 +130,79 @@ private:
};
```
+### Java
+
+```java
+/**
+ * Definition for Directed graph.
+ * class DirectedGraphNode {
+ * int label;
+ * ArrayList neighbors;
+ * DirectedGraphNode(int x) { label = x; neighbors = new ArrayList(); }
+ * };
+ */
+
+public class Solution {
+ /*
+ * @param graph: A list of Directed graph node
+ * @return: Any topological order for the given graph.
+ */
+ public ArrayList topSort(ArrayList graph) {
+ ArrayList sorting = new ArrayList<>();
+ Map inDegreeMap = getIndegreeMap(graph);
+ for (Map.Entry degreeMap : inDegreeMap.entrySet()) {
+ if (degreeMap.getValue() == 0) {
+ dfs(inDegreeMap, degreeMap.getKey(), sorting);
+ }
+ }
+
+ return sorting;
+ }
+
+ private Map getIndegreeMap(ArrayList graph) {
+ Map inDegreeMap = new HashMap<>();
+ for (DirectedGraphNode node : graph) {
+ inDegreeMap.putIfAbsent(node, 0);
+ for (DirectedGraphNode neighbor : node.neighbors) {
+ inDegreeMap.put(neighbor, inDegreeMap.getOrDefault(neighbor, 0) + 1);
+ }
+ }
+
+ return inDegreeMap;
+ }
+
+ private void dfs(Map inDegreeMap, DirectedGraphNode node, List result) {
+
+ result.add(node);
+ inDegreeMap.put(node, inDegreeMap.get(node) - 1);
+ for (DirectedGraphNode neighbor : node.neighbors) {
+ inDegreeMap.put(neighbor, inDegreeMap.get(neighbor) - 1);
+ if (inDegreeMap.get(neighbor) == 0) {
+ dfs(inDegreeMap, neighbor, result);
+ }
+ }
+ }
+}
+```
+
### 源码分析
-C++中使用 unordered_map 可获得更高的性能,私有方法中使用引用传值。
+C++中使用 unordered_map 可获得更高的性能,私有方法中使用引用传值。在 `dfs` 递归的过程中,将节点加入到最终结果后需要对其入度减一,否则在上层循环邻居节点时会有重复。这里的 `dfs` 是剪枝过后的 DFS, 因为这里只处理入度为 0 的节点。另外在第一次求各节点的入度数时,需要先初始化为0,否则会漏掉部分入度为0的节点。
### 复杂度分析
以 V 表示顶点数,E 表示有向图中边的条数。
首先获得节点的入度数,时间复杂度为 $$O(V+E)$$, 使用了哈希表存储,空间复杂度为 $$O(V)$$. 遍历图求得入度为0的节点,时间复杂度为 $$O(V)$$. 仅在入度为0时调用 DFS,故时间复杂度为 $$O(V+E)$$.
-
-需要注意的是这里的 DFS 不是纯 DFS,使用了 BFS 的思想进行了优化,否则一个节点将被遍历多次,时间复杂度可能恶化为指数级别。
-
综上,时间复杂度近似为 $$O(V+E)$$, 空间复杂度为 $$O(V)$$.
## 题解2 - BFS
-拓扑排序除了可用 DFS 求解外,也可使用 BFS, 具体方法为:
+拓扑排序除了可用 DFS 求解外,也可使用 BFS, 相比题解1使用递归顺腾摸瓜获取入度为 0 的节点,我们还可以通过队列获取非邻居节点的其他入度为 0 的节点,即 BFS。具体方法为:
1. 获得图中各节点的入度。
-2. BFS 首先遍历求得入度数为0的节点,入队,便于下一次 BFS。
-3. 队列不为空时,弹出队顶元素并对其邻接节点进行 BFS,将入度为0的节点加入到最终结果和队列中,重复此过程直至队列为空。
+2. BFS 首先遍历求得所有入度数为0的节点,入队,便于下一次 BFS。
+3. 队列不为空时,弹出队顶元素并对其邻接节点入度数减一,将入度为0的节点加入到队列中,重复此过程直至队列为空。
### C++
@@ -147,24 +224,24 @@ public:
vector topSort(vector graph) {
vector result;
if (graph.size() == 0) return result;
-
+
map indegree;
// get indegree of all DirectedGraphNode
indeg(graph, indegree);
queue q;
// bfs
bfs(graph, indegree, q, result);
-
+
return result;
}
-
+
private:
/** get indegree of all DirectedGraphNode
*
*/
void indeg(vector &graph,
map &indegree) {
-
+
for (int i = 0; i < graph.size(); ++i) {
for (int j = 0; j < graph[i]->neighbors.size(); j++) {
if (indegree.find(graph[i]->neighbors[j]) == indegree.end()) {
@@ -175,7 +252,7 @@ private:
}
}
}
-
+
void bfs(vector &graph, map &indegree,
queue &q, vector &ret) {
@@ -185,7 +262,7 @@ private:
q.push(graph[i]);
}
}
-
+
while (!q.empty()) {
DirectedGraphNode *cur = q.front();
q.pop();
@@ -201,14 +278,142 @@ private:
};
```
+### Java
+
+```java
+/**
+ * Definition for Directed graph.
+ * class DirectedGraphNode {
+ * int label;
+ * ArrayList neighbors;
+ * DirectedGraphNode(int x) { label = x; neighbors = new ArrayList(); }
+ * };
+ */
+
+public class Solution {
+ /*
+ * @param graph: A list of Directed graph node
+ * @return: Any topological order for the given graph.
+ */
+ public ArrayList topSort(ArrayList graph) {
+ ArrayList result = new ArrayList();
+
+ if (graph == null || graph.size() == 0) return result;
+
+ Queue queue = new LinkedList<>();
+ Map map = getIndegreeMap(graph);
+ for (Map.Entry entry : map.entrySet()) {
+ if (entry.getValue() == 0) {
+ queue.offer(entry.getKey());
+ }
+ }
+
+ bfs(queue, map, result);
+
+ return result;
+ }
+
+ private Map getIndegreeMap(ArrayList graph) {
+ Map map = new HashMap<>();
+
+ for (DirectedGraphNode node : graph) {
+ map.putIfAbsent(node, 0);
+ for (DirectedGraphNode neighbor : node.neighbors) {
+ map.put(neighbor, map.getOrDefault(neighbor, 0) + 1);
+ }
+ }
+
+ return map;
+ }
+
+ private void bfs(Queue queue, Map map, ArrayList result) {
+
+ while (!queue.isEmpty()) {
+ DirectedGraphNode node = queue.poll();
+ result.add(node);
+ for (DirectedGraphNode neighbor : node.neighbors) {
+ map.put(neighbor, map.get(neighbor) - 1);
+ if (map.get(neighbor) == 0) {
+ queue.offer(neighbor);
+ }
+ }
+ }
+ }
+}
+```
+
### 源码分析
-C++中在判断入度是否为0时将对 map 产生副作用,在求入度数时只有入度数大于等于1才会出现在 map 中,故不在 map 中时直接调用 indegree 方法将产生新的键值对,初始值为0,恰好满足此题需求。
+C++中在判断入度是否为0时将对 map 产生副作用,在求入度数时只有入度数大于等于1才会出现在 map 中,故不在 map 中时直接调用 indegree 方法将产生新的键值对,初始值为0,恰好满足此题需求。与 DFS 的解法不同,在 bfs 的实现中可以不对已加入到最终结果的节点入度数减一,因为没有上一层循环了。
### 复杂度分析
同题解1 的分析,时间复杂度为 $$O(V+E)$$, 空间复杂度为 $$O(V)$$.
+## 题解3 - DFS(递归保证局部拓扑排序)
+
+与题解1和题解2中依赖先计算入度数不同,这种解法可以不必事先计算入度数,从图中任意一个节点递归,直至将其所有邻接节点入栈后再对自己入栈,这样可保证有依赖的节点一定在其父节点后面出栈,遍历完图中所有节点即可得最终有效结果。这里需要借助辅助标记 Set 或者数组。具体过程可参考 GeeksforGeeks 中的视频讲解。
+
+### Java
+
+```java
+/**
+ * Definition for Directed graph.
+ * class DirectedGraphNode {
+ * int label;
+ * ArrayList neighbors;
+ * DirectedGraphNode(int x) { label = x; neighbors = new ArrayList(); }
+ * };
+ */
+
+public class Solution {
+ /*
+ * @param graph: A list of Directed graph node
+ * @return: Any topological order for the given graph.
+ */
+ public ArrayList topSort(ArrayList graph) {
+ ArrayList result = new ArrayList<>();
+
+ if (graph == null || graph.size() == 0) return result;
+
+ Set visited = new HashSet<>();
+ Deque stack = new ArrayDeque<>();
+
+ for (DirectedGraphNode node : graph) {
+ dfs(node, visited, stack);
+ }
+
+ while (!stack.isEmpty()) {
+ result.add(stack.pop());
+ }
+
+ return result;
+ }
+
+ private void dfs(DirectedGraphNode root,
+ Set visited,
+ Deque stack) {
+
+ if (!visited.contains(root)) {
+ visited.add(root);
+ for (DirectedGraphNode neighbor : root.neighbors) {
+ dfs(neighbor, visited, stack);
+ }
+ stack.offerFirst(root);
+ }
+ }
+}
+```
+
+### 源码分析
+
+注意 Java 中栈 Stack 的使用即可,基础数据结构小节中有解析。
+
+### 复杂度分析
+
+同题解1 的分析,遍历所有节点所有边,时间复杂度为 $$O(V+E)$$, 空间复杂度为 $$O(V)$$.
+
## Reference
- [Topological Sorting 参考程序 Java/C++/Python](http://www.jiuzhang.com/solutions/topological-sorting/)
+- [Topological Sorting - GeeksforGeeks](https://www.geeksforgeeks.org/topological-sorting/)
diff --git a/zh-hans/integer_array/kth_largest_element.md b/zh-hans/integer_array/kth_largest_element.md
index 7be95d23c..fa1c9a139 100644
--- a/zh-hans/integer_array/kth_largest_element.md
+++ b/zh-hans/integer_array/kth_largest_element.md
@@ -1,6 +1,6 @@
# Kth Largest Element in an Array
-Tags: Heap, Divide and Conquer, Medium
+Tags: Quick Sort, Divide and Conquer, Medium
## Question
@@ -27,7 +27,7 @@ adding this problem and creating all test cases.
找第 K 大数,基于比较的排序的方法时间复杂度为 $$O(n)$$, 数组元素无区间限定,故无法使用线性排序。由于只是需要找第 K 大数,这种类型的题通常需要使用快排的思想解决。[Quick Sort](http://algorithm.yuanbin.me/zh-hans/basics_sorting/quick_sort.html) 总结了一些经典模板。这里比较基准值最后的位置的索引值和 K 的大小关系即可递归求解。
-### Java
+### Java - 递归求解
```java
public class Solution {
@@ -76,6 +76,62 @@ public class Solution {
递归的终止条件有两个,一个是左边界的值等于右边界(实际中其实不会有 l > u), 另一个则是索引值 `m + 1 == k`.
这里找的是第 K 大数,故为降序排列,for 循环中使用`nums[i] > nums[left]` 而不是小于号。
+### Java - 迭代求解
+
+递归代码看上去顺理成章,实际上构造递归方法的参数、返回值是需要经验技巧的,自己写起来就会发现机关重重,一次性做到 bug-free 并不容易。下面是一个迭代版的实现。
+
+```
+class Solution {
+ public int findKthLargest(int[] A, int k) {
+ if (A == null || A.length == 0 || k < 0 || k > A.length) {
+ return -1;
+ }
+
+ int lo = 0, hi = A.length - 1;
+ while (lo <= hi) {
+ int idx = partition(A, lo, hi);
+ if (idx == k - 1) {
+ return A[idx];
+ } else if (idx < k - 1) {
+ lo = idx + 1;
+ } else {
+ hi = idx - 1;
+ }
+ }
+
+ return -1;
+ }
+
+ private int partition(int[] A, int lo, int hi) {
+ int pivot = A[lo], i = lo + 1, j = hi;
+ while (i <= j) {
+ while (i <= j && A[i] > pivot) {
+ i++;
+ }
+ while (i <= j && A[j] <= pivot) {
+ j--;
+ }
+ if (i < j) {
+ swap(A, i, j);
+ }
+ }
+ swap(A, lo, j);
+
+ return j;
+ }
+
+ private void swap(int[] A, int i, int j) {
+ int tmp = A[i];
+ A[i] = A[j];
+ A[j] = tmp;
+ }
+}
+```
+
+### 源码分析
+
+`findKthLargest` 里的 `while` 循环体有种二分搜索的既视感,`partition` 就是标典型的快排分区写法。
+
### 复杂度分析
最坏情况下需要遍历 $$ n + n - 1 + ... + 1 = O(n^2)$$, 平均情况下 $$n + n/2 + n/4 + ... + 1 = O(2n)=O(n)$$. 故平均情况时间复杂度为 $$O(n)$$. 交换数组的值时使用了额外空间,空间复杂度 $$O(1)$$.
diff --git a/zh-hans/linked_list/insertion_sort_list.md b/zh-hans/linked_list/insertion_sort_list.md
index eaeb20e77..097cd1ac5 100644
--- a/zh-hans/linked_list/insertion_sort_list.md
+++ b/zh-hans/linked_list/insertion_sort_list.md
@@ -1,16 +1,44 @@
# Insertion Sort List
+Tags: Linked List, Sort, Medium
+
## Question
-- leetcode: [Insertion Sort List | LeetCode OJ](https://leetcode.com/problems/insertion-sort-list/)
-- lintcode: [(173) Insertion Sort List](http://www.lintcode.com/en/problem/insertion-sort-list/)
+- leetcode: [Insertion Sort List](https://leetcode.com/problems/insertion-sort-list/)
+- lintcode: [Insertion Sort List](https://www.lintcode.com/problem/insertion-sort-list/)
+
+### Problem Statement
-```
Sort a linked list using insertion sort.
-Example
-Given 1->3->2->0->null, return 0->1->2->3->null.
-```
+
+A graphical example of insertion sort. The partial sorted list (black)
+initially contains only the first element in the list.
+With each iteration one element (red) is removed from the input data and
+inserted in-place into the sorted list
+
+
+**Algorithm of Insertion Sort:**
+
+ 1. Insertion sort iterates, consuming one input element each repetition, and growing a sorted output list.
+ 2. At each iteration, insertion sort removes one element from the input data, finds the location it belongs within the sorted list, and inserts it there.
+ 3. It repeats until no input elements remain.
+
+
+**Example 1:**
+
+
+ **Input:** 4->2->1->3
+ **Output:** 1->2->3->4
+
+
+**Example 2:**
+
+
+ **Input:** -1->5->3->4->0
+ **Output:** -1->0->3->4->5
+
## 题解1 - 从首到尾遍历
@@ -130,9 +158,9 @@ Python 的实现在 lintcode 上会提示 TLE, leetcode 上勉强通过,这里
### 复杂度分析
-最好情况:原链表已经有序,每得到一个新节点都需要 $$i$$ 次比较和一次交换, 时间复杂度为 $$1/2O(n^2) + O(n)$$, 使用了 dummy 和 pre, 空间复杂度近似为 $$O(1)$$.
+最好情况:原链表已经逆序,每得到一个新节点仅需要一次比较, 时间复杂度为 $$O(n)$$, 使用了 dummy 和 pre, 空间复杂度近似为 $$O(1)$$.
-最坏情况:原链表正好逆序,由于是单向链表只能从前往后依次遍历,交换和比较次数均为 $$1/2 O(n^2)$$, 总的时间复杂度近似为 $$O(n^2)$$, 空间复杂度同上,近似为 $$O(1)$$.
+最坏情况:原链表正好升序,由于是单向链表只能从前往后依次遍历,交换和比较次数均为 $$1/2 O(n^2)$$, 总的时间复杂度近似为 $$O(n^2)$$, 空间复杂度同上,近似为 $$O(1)$$.
## 题解2 - 优化有序链表
diff --git a/zh-hans/linked_list/remove_nth_node_from_end_of_list.md b/zh-hans/linked_list/remove_nth_node_from_end_of_list.md
index d2f0ba840..d5a205058 100644
--- a/zh-hans/linked_list/remove_nth_node_from_end_of_list.md
+++ b/zh-hans/linked_list/remove_nth_node_from_end_of_list.md
@@ -1,88 +1,49 @@
+---
+difficulty: Easy
+tags:
+- Linked List
+- Two Pointers
+title: Remove Nth Node From End of List
+---
+
# Remove Nth Node From End of List
-## Question
+## Problem
-- lintcode: [(174) Remove Nth Node From End of List](http://www.lintcode.com/en/problem/remove-nth-node-from-end-of-list/)
+### Metadata
-```
-Given a linked list, remove the nth node from the end of list and return its head.
+- tags: Linked List, Two Pointers
+- difficulty: Easy
+- source(lintcode):
+- source(leetcode):
-Note
-The minimum number of nodes in list is n.
+### Description
-Example
-Given linked list: 1->2->3->4->5->null, and n = 2.
+Given a linked list, remove the nth node from the end of list and return its head.
-After removing the second node from the end, the linked list becomes 1->2->3->5->null.
-Challenge
-O(n) time
-```
+#### Notice
-## 题解
+The minimum number of nodes in list is *n*.
-简单题,
-使用快慢指针解决此题,需要注意最后删除的是否为头节点。让快指针先走`n`步,直至快指针走到终点,找到需要删除节点之前的一个节点,改变`node->next`域即可。
+#### Example
-### C++
+Given linked list: `1->2->3->4->5->null`, and *n* = `2`.
-```c++
-/**
- * Definition of ListNode
- * class ListNode {
- * public:
- * int val;
- * ListNode *next;
- * ListNode(int val) {
- * this->val = val;
- * this->next = NULL;
- * }
- * }
- */
-class Solution {
-public:
- /**
- * @param head: The first node of linked list.
- * @param n: An integer.
- * @return: The head of linked list.
- */
- ListNode *removeNthFromEnd(ListNode *head, int n) {
- if (NULL == head || n < 0) {
- return NULL;
- }
+After removing the second node from the end, the linked list becomes `1->2->3->5->null`.
- ListNode *preN = head;
- ListNode *tail = head;
- // slow fast pointer
- int index = 0;
- while (index < n) {
- if (NULL == tail) {
- return NULL;
- }
- tail = tail->next;
- ++index;
- }
- if (NULL == tail) {
- return head->next;
- }
+#### Challenge
- while (tail->next) {
- tail = tail->next;
- preN = preN->next;
- }
- preN->next = preN->next->next;
+Can you do it without getting the length of the linked list?
- return head;
- }
-};
-```
+## 题解
-以上代码单独判断了是否需要删除头节点的情况,在遇到头节点不确定的情况下,引入`dummy`节点将会使代码更加优雅,改进的代码如下。
+简单题,使用快慢指针解决此题,需要注意最后删除的是否为头节点。让快指针先走`n`步,直至快指针走到终点,找到需要删除节点之前的一个节点,改变`node->next`域即可。见基础数据结构部分的链表解析。
-### C++ dummy node
+### C++
-```c++
+```cpp
/**
* Definition of ListNode
* class ListNode {
@@ -129,6 +90,49 @@ public:
};
```
+### Java
+
+```java
+/**
+ * Definition for singly-linked list.
+ * public class ListNode {
+ * int val;
+ * ListNode next;
+ * ListNode(int x) { val = x; }
+ * }
+ */
+class Solution {
+ public ListNode removeNthFromEnd(ListNode head, int n) {
+ if (head == nul) return head;
+
+ ListNode dummy = new ListNode(0);
+ dummy.next = head;
+ ListNode fast = head;
+ ListNode slow = dummy;
+ for (int i = 0; i < n; i++) {
+ fast = fast.next;
+ }
+
+ while(fast != null) {
+ fast = fast.next;
+ slow = slow.next;
+ }
+
+ // gc friendly
+ // ListNode toBeDeleted = slow.next;
+ slow.next = slow.next.next;
+ // toBeDeleted.next = null;
+ // toBeDeleted = null;
+
+ return dummy.next;
+ }
+}
+```
+
### 源码分析
-引入`dummy`节点后画个图分析下就能确定`head`和`preDel`的转移关系了。
+引入`dummy`节点后画个图分析下就能确定`head`和`preDel`的转移关系了。 注意 while 循环中和快慢指针初始化的关系,否则容易在顺序上错一。
+
+### 复杂度分析
+
+极限情况下遍历两遍链表,时间复杂度为 $$O(n)$$.
diff --git a/zh-hans/math_and_bit_manipulation/task_scheduler.md b/zh-hans/math_and_bit_manipulation/task_scheduler.md
new file mode 100644
index 000000000..73c466ecd
--- /dev/null
+++ b/zh-hans/math_and_bit_manipulation/task_scheduler.md
@@ -0,0 +1,91 @@
+---
+difficulty: Medium
+tags:
+- Queue
+- Greedy
+- Facebook
+- Array
+title: Task Scheduler
+---
+
+# Task Scheduler
+
+## Problem
+
+### Metadata
+
+- tags: Queue, Greedy, Facebook, Array
+- difficulty: Medium
+- source(leetcode):
+- source(lintcode):
+
+### Description
+
+Given a char array representing tasks CPU need to do. It contains capital letters A to Z where different letters represent different tasks.Tasks could be done without original order. Each task could be done in one interval. For each interval, CPU could finish one task or just be idle.
+
+However, there is a non-negative cooling interval `n` that means between two `same tasks`, there must be at least n intervals that CPU are doing different tasks or just be idle.
+
+You need to return the `least` number of intervals the CPU will take to finish all the given tasks.
+
+#### Notice
+
+1. The number of tasks is in the range `[1, 10000]`.
+2. The integer n is in the range `[0, 100]`.
+
+#### Example
+
+Given tasks = `['A','A','A','B','B','B']`, n = `2`, return `8`.
+```
+Explanation:
+A -> B -> idle -> A -> B -> idle -> A -> B.
+```
+
+## 题解 - 填充空闲时隙
+
+TODO 分情况讨论
+
+1. 出现频率最高的字符有多个
+2. 以频率最高的字符为分界点,空闲时隙数不够所有字符放满
+3. 空闲时隙数足够所有字符放下
+
+### Java
+
+```java
+class Solution {
+ public int leastInterval(char[] tasks, int n) {
+ if (tasks == null) return -1;
+
+ int[] map = new int[26];
+ for (char c : tasks) {
+ map[c - 'A']++;
+ }
+ Arrays.sort(map);
+
+ int maxFreq = map[25];
+ int idleSlots = n * (maxFreq - 1);
+ for (int i = 24; i >= 0; i--) {
+ if (map[i] == 0) {
+ break;
+ } else if (map[i] == maxFreq) {
+ idleSlots -= maxFreq - 1;
+ } else {
+ idleSlots -= map[i];
+ }
+ }
+
+ return idleSlots > 0 ? idleSlots + tasks.length : tasks.length;
+ }
+}
+```
+
+### 源码分析
+
+此题的技巧性较强,由于出现的字符数仅为 A-Z 这26个字符,而且我们需要先进行排序,这种特殊情形的排序往往不能使用通用排序方法,我们使用数组映射再进行排序。
+
+### 复杂度分析
+
+空间复杂度 $$O(26)$$ ==> $$O(1)$$, 时间复杂度最坏情况下遍历26个元素,故为 $$O(1)$$.
+
+## Reference
+
+[Task Scheduler - Approach #3 Calculating Idle slots](https://leetcode.com/problems/task-scheduler/solution/)
\ No newline at end of file
diff --git a/zh-hans/problem_misc/longest_consecutive_sequence.md b/zh-hans/problem_misc/longest_consecutive_sequence.md
index 1af4e1542..6c08b6a8e 100644
--- a/zh-hans/problem_misc/longest_consecutive_sequence.md
+++ b/zh-hans/problem_misc/longest_consecutive_sequence.md
@@ -1,26 +1,26 @@
# Longest Consecutive Sequence
-## Question
+Tags: Array, Union Find, Hard
-- leetcode: [Longest Consecutive Sequence | LeetCode OJ](https://leetcode.com/problems/longest-consecutive-sequence/)
-- lintcode: [(124) Longest Consecutive Sequence](http://www.lintcode.com/en/problem/longest-consecutive-sequence/)
+## Question
+- leetcode: [Longest Consecutive Sequence](https://leetcode.com/problems/longest-consecutive-sequence/)
+- lintcode: [Longest Consecutive Sequence](https://www.lintcode.com/problem/longest-consecutive-sequence/)
### Problem Statement
Given an unsorted array of integers, find the length of the longest
consecutive elements sequence.
-#### Example
-
-Given `[100, 4, 200, 1, 3, 2]`,
-The longest consecutive elements sequence is `[1, 2, 3, 4]`. Return its
-length: `4`.
-
-#### Clarification
-
Your algorithm should run in O(_n_) complexity.
+**Example:**
+
+
+
+ **Input:** [100, 4, 200, 1, 3, 2]
+ **Output:** 4
+ **Explanation:** The longest consecutive elements sequence is [1, 2, 3, 4]. Therefore its length is 4.
## 题解
@@ -29,40 +29,33 @@ Your algorithm should run in O(_n_) complexity.
### Java
```java
-public class Solution {
- /**
- * @param nums: A list of integers
- * @return an integer
- */
- public int longestConsecutive(int[] num) {
- if (num == null || num.length == 0) return 0;
-
- // add number to hashset
- Set hashset = new HashSet();
- for (int n : num) {
- hashset.add(n);
+class Solution {
+ public int longestConsecutive(int[] nums) {
+ if (nums == null || nums.length <= 0) return 0;
+ Set sets = new HashSet<>(nums.length);
+ for (int num : nums) {
+ sets.add(num);
}
- int lcs = 0;
- for (int n : num) {
- int i = n, count = 1;
- hashset.remove(n);
- // i--
- while (hashset.contains(--i)) {
- count++;
- hashset.remove(i);
+ int result = 1;
+ for (int num : nums) {
+ int seq = 1;
+ int right = num, left = num;
+ // right
+ while (sets.contains(++right)) {
+ seq++;
+ sets.remove(right);
}
- // i++
- i = n;
- while (hashset.contains(++i)) {
- count++;
- hashset.remove(i);
+ // left
+ while (sets.contains(--left)) {
+ seq++;
+ sets.remove(left);
}
- // update lcs
- lcs = Math.max(lcs, count);
+ sets.remove(num);
+ if (seq > result) result = seq;
}
- return lcs;
+ return result;
}
}
```