Skip to content

Commit

Permalink
初始一些翻译
Browse files Browse the repository at this point in the history
  • Loading branch information
andyRon committed Aug 26, 2017
1 parent 6c9ab62 commit 5f8eadd
Show file tree
Hide file tree
Showing 30 changed files with 1,891 additions and 2 deletions.
30 changes: 30 additions & 0 deletions Algorithm Design.markdown
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Algorithm design techniques

What to do when you're faced with a new problem and you need to find an algorithm for it.

### Is it similar to another problem?

If you can frame your problem in terms of another, more general problem, then you might be able to use an existing algorithm. Why reinvent the wheel?

One thing I like about [The Algorithm Design Manual](http://www.algorist.com) by Steven Skiena is that it includes a catalog of problems and solutions you can try. (See also his [algorithm repository](http://www3.cs.stonybrook.edu/~algorith/).)

### It's OK to start with brute force

Naive, brute force solutions are often too slow for practical use but they're a good starting point. By writing the brute force solution, you learn to understand what the problem is really all about.

Once you have a brute force implementation you can use that to verify that any improvements you come up with are correct.

And if you only work with small datasets, then a brute force approach may actually be good enough on its own. Don't fall into the trap of premature optimization!

### Divide and conquer

>"When you change the way you look at things, the things you look at change."</br>
>Max Planck, Quantum theorist and Nobel Prize Winner
Divide and conquer is a way of dealing with a large problem by breaking it down into bits and pieces and working your way up towards the solution.

Instead of seeing the whole problem as a single, huge and complex task you divide the problem in relatively smaller problems that are easier to understand and deal with.

You solve smaller problems and aggregate the solution until you are left with the solution only. At each step the problem at hand shrinks and the solution gets mature until you have the final correct solution.

Solving the smaller task and applying the same solution repetitively ( or often times recursively) to other chunks give you the result in less time.
24 changes: 24 additions & 0 deletions Big-O Notation.markdown
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# 大 O 表示法

知道某个算法的运行速度和占用的内存空间,对于选择正确的算法来解决问题非常有帮助。

大 O 表示法能让你对一个算法的运行时间和占用空间有个大概概念。当有人说,“这个算法在最糟情况下的运行时间是 **O(n^2)** 而且占用了 **O(n)** 大小的空间”时,他的意思是这个算法有点慢,不过没占多大空间。

要知道一个算法的大 O 表示法通常要通过数学分析。在这里我们不会涉及具体的数学,不过知道不同的值意味着什么会很有用。所以这里有一张方便的表。**n** 在这里代表的意思是数据的个数。举个例子,当对一个有 100 个元素的数组进行排序时,**n = 100**

Big-O | 名字 | 描述
------| ---- | -----------
**O(1)** | 常数级 | **最好的**。不论输入数据量有多大,这个算法的运行时间总是一样的。例子: 基于索引取出数组中对应的元素。
**O(log n)** | 对数级 | **相当好**。这种算法每次循环时会把需要处理的数据量减半。如果你有 100 个元素,则只需要七步就可以找到答案。1000 个元素只要十步。100,0000 元素只要二十步。即便数据量很大这种算法也非常快。例子:二分查找。
**O(n)** | 线性级 | **还不错**。如果你有 100 个元素,这种算法就要做 100 次工作。数据量翻倍那么运行时间也翻倍。例子:线性查找。
**O(n log n)** | 线性对数级 | **还可以**。比线性级差了一些,不过也没那么差劲。例子:最快的通用排序算法。
**O(n^2)** | 二次方级 | **有点慢**。如果你有 100 个元素,这种算法需要做 100^2 = 10000 次工作。数据量 x 2 会导致运行时间 x 4 (因为 2 的 2 次方等于 4)。例子:循环套循环的算法,比如插入排序。
**O(n^3)** | 三次方级 | **特别慢**。如果你有 100 个元素,那么这种算法就要做 100^3 = 100,0000 次工作。数据量 x 2 会导致运行时间 x 8。例子:矩阵乘法。
**O(2^n)** | 指数级 | **超级慢**。这种算法你要想方设法避免,但有时候你就是没得选。加一点点数据就会把运行时间成倍的加长。例子:旅行商问题。
**O(n!)** | 阶乘级 | **比蜗牛还慢**!不管干什么都要跑个 N 年才能得到结果。

大部分情况下你用直觉就可以知道一个算法的大 O 表示法。比如说,如果你的代码用一个循环遍历你输入的每个元素,那么这个算法就是 **O(n)**。如果是循环套循环,那就是 **O(n^2)**。如果 3 个循环套在一起就是 **O(n^3)**,以此类推。

注意,大 O 表示法只是一种估算,当数据量大的时候才有用。举个例子,[插入排序](Insertion Sort/)的最糟情况运行时间是 **O(n^2)**。 理论上来说它的运行时间比[归并排序](Merge Sort/)要慢一些。归并排序是 **O(n log n)**。但对于小数据量,插入排序实际上更快一些,特别是那些已经有一部分数据是排序好的数组。

如果你看完没懂,也不要太纠结了。这种东西仅仅在比较两种算法哪种更好的时候才有点用。但归根结底,你还是要实际测试之后才能得出结论。而且如果数据量相对较小,哪怕算法比较慢,在实际使用也不会造成太大的问题。
47 changes: 47 additions & 0 deletions Queue/Queue-Optimized.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
First-in first-out queue (FIFO)
New elements are added to the end of the queue. Dequeuing pulls elements from
the front of the queue.
Enqueuing and dequeuing are O(1) operations.
*/
public struct Queue<T> {
fileprivate var array = [T?]()
fileprivate var head = 0

public var isEmpty: Bool {
return count == 0
}

public var count: Int {
return array.count - head
}

public mutating func enqueue(_ element: T) {
array.append(element)
}

public mutating func dequeue() -> T? {
guard head < array.count, let element = array[head] else { return nil }

array[head] = nil
head += 1

let percentage = Double(head)/Double(array.count)
if array.count > 50 && percentage > 0.25 {
array.removeFirst(head)
head = 0
}

return element
}

public var front: T? {
if isEmpty {
return nil
} else {
return array[head]
}
}
}
36 changes: 36 additions & 0 deletions Queue/Queue-Simple.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
First-in first-out queue (FIFO)
New elements are added to the end of the queue. Dequeuing pulls elements from
the front of the queue.
Enqueuing is an O(1) operation, dequeuing is O(n). Note: If the queue had been
implemented with a linked list, then both would be O(1).
*/
public struct Queue<T> {
fileprivate var array = [T]()

public var count: Int {
return array.count
}

public var isEmpty: Bool {
return array.isEmpty
}

public mutating func enqueue(_ element: T) {
array.append(element)
}

public mutating func dequeue() -> T? {
if isEmpty {
return nil
} else {
return array.removeFirst()
}
}

public var front: T? {
return array.first
}
}
66 changes: 66 additions & 0 deletions Queue/Queue.playground/Contents.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@

// last checked with Xcode 9.0b4
#if swift(>=4.0)
print("Hello, Swift 4!")
#endif

/*
Queue
A queue is a list where you can only insert new items at the back and
remove items from the front. This ensures that the first item you enqueue
is also the first item you dequeue. First come, first serve!
A queue gives you a FIFO or first-in, first-out order. The element you
inserted first is also the first one to come out again. It's only fair!
In this implementation, enqueuing is an O(1) operation, dequeuing is O(n).
*/

public struct Queue<T> {
fileprivate var array = [T]()

public var isEmpty: Bool {
return array.isEmpty
}

public var count: Int {
return array.count
}

public mutating func enqueue(_ element: T) {
array.append(element)
}

public mutating func dequeue() -> T? {
if isEmpty {
return nil
} else {
return array.removeFirst()
}
}

public var front: T? {
return array.first
}
}

// Create a queue and put some elements on it already.
var queueOfNames = Queue(array: ["Carl", "Lisa", "Stephanie", "Jeff", "Wade"])

// Adds an element to the end of the queue.
queueOfNames.enqueue("Mike")

// Queue is now ["Carl", "Lisa", "Stephanie", "Jeff", "Wade", "Mike"]
print(queueOfNames.array)

// Remove and return the first element in the queue. This returns "Carl".
queueOfNames.dequeue()

// Return the first element in the queue.
// Returns "Lisa" since "Carl" was dequeued on the previous line.
queueOfNames.front

// Check to see if the queue is empty.
// Returns "false" since the queue still has elements in it.
queueOfNames.isEmpty
4 changes: 4 additions & 0 deletions Queue/Queue.playground/contents.xcplayground
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<playground version='5.0' target-platform='ios'>
<timeline fileName='timeline.xctimeline'/>
</playground>

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file not shown.
Loading

0 comments on commit 5f8eadd

Please sign in to comment.