《算法图解》学习笔记

转载请注明出处:
http://blog.csdn.net/BruceHurrican/article/details/76535719

最近在学习《算法图解》,写下此文当作学习笔记了。

大O可以比较操作数,指出算法运行时间的增速。

算法运行时间是从其增速的角度度量的。

1.O(log n), 也叫对数时间,这样的算法包括二分查找法
2.O(n), 也叫线性时间,这样的算法包括简单查找法
3.O(n * log n), 快速排序——速度较快
4.O(n^2),选择排序——速度较慢
5.O(n!), 算法非常慢

二分查找法

需要数组或列表有序。
二分查找法,python2.7实现如下:

# encoding=utf-8
# 二分查找法
def binary_search(list, item):
    low = 0
    high = len(list) - 1; # 获取 list 最后一个元素索引

    while low <= high:
        mid = (low + high)/2
        guess = list[mid] # 取出列表中位置是 mid 的元素
        print guess
        if guess == item:
            print str(item) + " 在列表中的位置索引是: " + str(mid)
            return mid
        if guess > item:
            high = mid - 1
        else:
            low = mid + 1
    print str(item) + " 不在列表中"
    return None

print "====测试二分查找===="
myList =  range(1,100,2) # 列表必须是有序的
# for x in myList:
    # print "列表值: " + str(x)

binary_search(myList, 83)
# binary_search(myList, -2)
# binary_search(myList, 9)

print "================华丽丽的分隔线==================="

#================华丽丽的分隔线===================

运行结果如下:

====测试二分查找====
49
75
87
81
83
83 在列表中的位置索引是: 41
49
23
11
5
1
-2 不在列表中
49
23
11
5
7
9
9 在列表中的位置索引是: 4
================华丽丽的分隔线===================

java实现如下:

public static void main(String[] args) {
        System.out.println("===测试二分查找===");
        List list = new ArrayList<>(100);
        for (int i = 1; i <= 100 ; i+=2) {
            list.add(i);
        }
        binarySearch(list, 13);
        binarySearch(list, 34);
        binarySearch(list, 87);

        // Java 默认方法
        System.out.println("13在列表中的索引: " + Collections.binarySearch(list, 13));
        System.out.println("34在列表中的索引: " + Collections.binarySearch(list, 34));
        System.out.println("87在列表中的索引: " + Collections.binarySearch(list, 87));
    }


    /** * 二分查找法 * @param dataList 有序集合 * @param searchNum 被搜索的数值 * @return searchNume 在 dataList 中的索引 */
    private static int binarySearch(List dataList, int searchNum) {
        int low = 0;
        int high = dataList.get(dataList.size() - 1);

        while (low <= high) {
            int mid = (low + high) >>> 2;
            int guess = dataList.get(mid);
            System.out.println("guess: " + guess);
            if (guess == searchNum) {
                System.out.println(searchNum + " 在列表中的索引是: " + mid);
                return mid;
            }
            if (guess > searchNum) {
                high = mid - 1;
            } else {
                low = mid + 1;
            }
        }
        System.out.println(searchNum + " 不在列表中");
        return -1;
    }

运行结果为:

===测试二分查找===
guess: 99
guess: 49
guess: 23
guess: 11
guess: 17
guess: 13
13 在列表中的索引是: 6
guess: 99
guess: 49
guess: 23
guess: 35
guess: 29
guess: 31
guess: 33
34 不在列表中
guess: 99
guess: 49
guess: 73
guess: 85
guess: 91
guess: 87
87 在列表中的索引是: 43
13在列表中的索引: 6
34在列表中的索引: -18
87在列表中的索引: 43

从代码中可以看到,java类自带有二分查找实现,实际工作中并不需要我们编写算法实现,java自带类路径为 java.util.Collections

kotlin 实现

object KK{
    @JvmStatic fun main(args: Array){
        println("===测试二分查找===")
        val dataList = listOf(1,3,5,7,9)
        binarySearch(dataList, 3)
        binarySearch(dataList, -2)
        binarySearch(dataList, 7)

        println("3在dataList中的索引: " + dataList.binarySearch(3))
        println("-2在dataList中的索引: " + dataList.binarySearch(-2))
        println("7在dataList中的索引: " + dataList.binarySearch(7))
    }

    /*** * 二分查找 * dataList 有序集合 * searchNum 查找数值 * return searchNum 在 dataList 中的索引 */
    private fun binarySearch(dataList: List, searchNum: Int): Int {
        var low = 0
        var high = dataList.get(dataList.size - 1)

        while (low <= high) {
            var mid = (low + high) / 2
            var guess = dataList.get(mid)
            println("guess: " + guess)
            if (guess == searchNum) {
                println(String.format("%d 在列表中的索引是: %d", searchNum, mid))
                return mid
            }
            if (guess > searchNum) {
                high = mid - 1
            } else {
                low = mid + 1
            }
        }

        println(String.format("%d 不在集合中", searchNum))
        return -1
    }
}

运行结果为:

===测试二分查找===
guess: 9
guess: 3
3 在列表中的索引是: 1
guess: 9
guess: 3
guess: 1
-2 不在集合中
guess: 9
guess: 3
guess: 5
guess: 7
7 在列表中的索引是: 3
3在dataList中的索引: 1
-2在dataList中的索引: -1
7在dataList中的索引: 3

kotlin也同样提供了二分查找方法,在kotlin-stdlib-1.1.2-4包中,kotlin.collections.Collections.kt中

数据结构

  • 数组,数组支持随机访问,速度快,插入和删除慢,内存复用率低
  • 链表,随机访问效率比数组低,插入和删除快,内存复用率高
数组 链表
读取 O(1)
插入 O(n)
删除 O(n)

选择排序

是一种灵巧的算法,速度不是很快,运行时间为O(n^n)

python2.7实现如下:

# 选择数组中最小的数
def findSmallest(arr):
    smallest = arr[0]
    smallest_index = 0
    for i in range(1, len(arr)):
        if arr[i] < smallest:
            smallest = arr[i]
            smallest_index = i
    return smallest_index

# 对数组排序
def selectionSort(arr):
    newArr = []
    for i in range(len(arr)):
        smallest = findSmallest(arr)
        newArr.append(arr.pop(smallest))
    return newArr

print "===测试选择排序==="
selectionList = [54,32,1,3,4,78,65]
# selectionList.sort()
# print selectionList
print "排序后的集合为: " + str(selectionSort(selectionList))

运行结果:

===测试选择排序===
排序后的集合为: [1, 3, 4, 32, 54, 65, 78]

对于选择排序python提供了默认实现 list.sort()

java 实现:

public static void main(String[] args) {
        List testList = new ArrayList<>();
        testList.add(33);
        testList.add(14);
        testList.add(6);
        testList.add(15);
        testList.add(56);
        testList.add(84);
        testList.add(38);
        testList.add(97);
        testList.add(8);
        testList.add(19);
        System.out.println("原集合中各元素: ");
        for (int i = 0; i < testList.size(); i++) {
            System.out.print(testList.get(i) + ", ");
        }
        System.out.println();
        System.out.println("排序后");
        List newList = selectionSort(testList);
        for (int i = 0; i < newList.size(); i++) {
            System.out.print(newList.get(i) + ", ");
        }
    }

    /** * 将列表从小到大排列并生成新列表 * @param arr * @return */
    private static List selectionSort(List arr) {
        List newList= new ArrayList<>(arr.size());
        int size = arr.size();
        for (int i = 0; i < size; i++) {
            int smallIndex = findSmallestIndex(arr);
            newList.add(arr.get(smallIndex));
            arr.remove(smallIndex);
        }
        return newList;
    }

    /** * 获取最小值的索引 * @param arr * @return */
    private static int findSmallestIndex(List arr) {
        int smallest = arr.get(0);
        int smallestIndex = 0;
        for (int i = 0; i < arr.size(); i++) {
            if (arr.get(i) < smallest) {
                smallest = arr.get(i);
                smallestIndex = i;
            }
        }
        return smallestIndex;
    }

结果如下:

原集合中各元素:
33, 14, 6, 15, 56, 84, 38, 97, 8, 19,
排序后
6, 8, 14, 15, 19, 33, 38, 56, 84, 97,

快速排序

确定基线条件和递归条件是关键,比选择排序快。快速排序的性能高度依赖于选择的基准值。

python2.7实现如下:

# encoding=utf-8
# 快速排序
def quickSort(arr, keyIndex):
    if len(arr) < 2:
        return arr #基线条件,为空或只包含一个元素的数组是有序数组
    else:
        if keyIndex <= len(arr) - 1:
            pivot = arr[keyIndex]
        else:
            pivot = arr[0]
        arr.remove(pivot)
        less = [i for i in arr if i <= pivot] # 由所有小于基准值的元素组成的子数组
        greater = [i for i in arr if i> pivot] # 同所有大于基准值的元素组成的子数组
        return quickSort(less, keyIndex) + [pivot] + quickSort(greater, keyIndex)

print "===测试快速排序==="
print quickSort([34,2,133,38,45,3,15,21,98], 3)

运行结果如下:

===测试快速排序===
[2, 3, 15, 21, 34, 38, 45, 98, 133]

下面这个例子是《啊哈!算法》中的解密QQ号

# encoding=utf-8
def guessQQ(arr):
    temp = []
    while len(arr) > 1:
        temp.append(arr[0])
        a = arr[1]
        arr.remove(arr[0])
        if len(arr) > 1:
            arr.remove(arr[0])
            arr.append(a)
        else :
            temp.append(arr[0])

    return temp


qq = [6,3,1,7,5,8,9,2,4]

print "加密QQ号:" + "".join(str(x) for x in qq)
print "解密QQ号:" + "".join(str(x) for x in guessQQ(qq))

运行结果:

加密QQ号:631758924
解密QQ号:615947283

散列表

使用散列函数和数组创建的一种数据结构。当key出现冲突时,对于相同数组索引通过链表解决数据冲突。散列表的查找、插入和删除速度都很快,适合用于模拟映射关系,数据去重。

散列函数,无论给它什么数据,它都返回一个数字
要求:

  • 它必须是一致的。
  • 它应将不同的输入映射到不同的数字。

特点:
- 散列函数总是将同样的输入映射到相同的索引。
- 散列函数将不同的输入映射到不同的索引。
- 散列函数知道数组有多大,只返回有效的索引。

python2.7中提供的散列表是字典,Java中提供的是HashMap

python2.7实现如下:

# 散列表
voted = {}
def check_voter(name):
    if voted.get(name):
        print "%s has voted" % name
    else:
        print "let %s vote" % name
        voted[name] = hash(name)

check_voter("bruce")
check_voter("max")
check_voter("tom")
check_voter("bruce")
check_voter("max")

运行结果:

let bruce vote
let max vote
let tom vote
bruce has voted
max has voted

广度优先搜索

解决最短路径问题的算法被称为广度优先搜索。用于在非加权图中查找最短路径。

例子:
开发android需要掌握java、xml、json等,开发ios需要掌握object-c、swift、json等,开发h5需要掌握 css、html、javascript等。

岗位 技能1 技能2 技能3
android json java json
ios object-c swift json
h5 css html javascript

python2.7实现如下:

# encoding=utf-8
# 广度优先搜索
from collections import deque

def learnFirstLanguage(firstLanguage):
    return firstLanguage == "java"

lesson = {}
lesson["android"] = ["xml","java","json"]
lesson["ios"] = ["object-c", "swift", "json"]
lesson["h5"] = ["css", "javascript", "html"]
lesson["xml"] = []
lesson["java"] = []
lesson["json"] = []
lesson["object-c"] = []
lesson["swift"] = []
lesson["html"] = []
lesson["css"] = []
lesson["javascript"] = []

def searchFirstToLearn(target):
    searchQueue = deque()
    searchQueue += lesson[target]
    searched = []
    while searchQueue:
        subject = searchQueue.popleft()
        if not subject in searched:
            if learnFirstLanguage(subject):
                print "%s is your first lesson to learn" % subject
                return True
            else:
                searchQueue += lesson[subject]
                searched.append(subject)
    return False

searchFirstToLearn("android")

运行结果:

java is your first lesson to learn

狄克斯特拉算法

用于在加权图中查找最小的路径。仅当权重为正时,狄克斯特拉算法才有效,如果图中包含负边,应该使用贝尔曼-福德算法。

贪婪算法

四个步骤:
1.找出“最便宜”的节点,即可在最短时间内到达的节点
2.更新该节点的邻居的开销
3.重复这个过程,直到对图中的每个节点都这样做了
4.计算最终最短路径

python2.7实现如下:

# encoding=utf-8
# 贪婪算法

foods = set(["beef", "pork", "apple", "orange", "milk", "hamburger", "fried chicken legs", "sushi"])

choices = {}
choices["fruit"] = set(["apple", "orange"])
choices["meat"] = set(["beef", "pork"])
choices["fastFood"] = set(["hamburger", "fried chicken legs"])
choices["drink"] = set(["milk"])
choices["c1"] = set(["sushi"])
choices["c2"] = set(["sushi" , "hamburger"])
choices["breakfast"] = set(["apple", "milk"])
choices["dinner"] = set(["beef", "sushi"])
choices["visitor"] = set(["pork", "sushi","orange"])
choices["kids"] = set(["fried chicken legs", "milk","orange", "apple"])
choices["young"] = set(["hamburger", "milk", "apple"])

bestChoices = set()

while foods:
    best = None
    foodCovered = set()
    for a,b in choices.items():
        covered = foods & b
        if len(covered) > len(foodCovered):
            best = a
            foodCovered = covered

    foods -= foodCovered
    bestChoices.add(best)

print "最优选择: "
print bestChoices

运行结果:

最优选择:
set([‘c2’, ‘kids’, ‘meat’])

K最邻近算法

KNN(K-nearest neighbours),机器学习思路之一,能否挑选合适的特征事关KNN算法的成败。未来不确定因素太多,无法根据股市过往表现来预测股市。

冒泡排序

核心是双重嵌套循环,时间复杂度是O(N^2)。

python2.7实现如下:

# encoding=utf-8
# 冒泡排序
def bubbleSort(a):
    asize = len(a)
    for i in xrange(asize - 1):
        for j in xrange(asize - 1 - i):
            if a[j] > a[j+1]:
                a[j], a[j+1] = a[j+1], a[j]
    return a

testList = [32,12,2,156,35,67,89,73,21,9,47,82]

print "原列表:"
print testList
print "排序后:"
print bubbleSort(testList)

运行结果:

原列表:
[32, 12, 2, 156, 35, 67, 89, 73, 21, 9, 47, 82]
排序后:
[2, 9, 12, 21, 32, 35, 47, 67, 73, 82, 89, 156]

bookCode

你可能感兴趣的:(阶段总结,算法学习)