python常用的算法与数据结构

栈,队列,双端队列
无序链表,有序链表
二叉树,堆,二叉搜索树,AVL树

以及一些算法

coding: utf-8

u"""
线性数据结构, 栈, 队列, deques, 容器结构, 数据项之间存在相对的位置
"""

class Stack(object):
u"""
栈 先进后出
"""
def init(self):
self.items = []

def push(self, item):
    self.items.append(item) # O(1)

def pop(self):
    return self.items.pop() # O(1)

def peek(self):
    return self.items[-1]

def isEmpty(self):
    return self.items == []

def size(self):
    return len(self.items)

u"""
全括号匹配
((()))
()(())()
"""
def parChecker(symbolString):
stack = Stack()
index = 0

while index < len(symbolString):
    sym = symbolString[index]
    if sym == '(':
        stack.push(sym)
    else:
        if stack.isEmpty():
            return False
        else:
            stack.pop()

    index += 1

if stack.isEmpty():
    return True
return False

u"""
代码括号匹配
"""
def codeChecker(symbolString):
stack = Stack()
index = 0

while index < len(symbolString):
    sym = symbolString[index]
    if sym in '([{':
        stack.push(sym)
    elif sym in ')]}':
        if stack.isEmpty():
            return False
        else:
            if not matches(stack.pop(), sym):
                return False

    index += 1

if stack.isEmpty():
    return True
return False

def matches(open, close):
opens = '([{'
closes = ')]}'
return opens.index(open) == closes.index(close)

u"""
任意进制转换
"""
def baseConverter(decNumber, base):
digits = '0123456789ABCDEF'

stack = Stack()

while decNumber > 0:
    stack.push(digits[decNumber % base])
    decNumber = decNumber // base

string = ''
while not stack.isEmpty():
    string += stack.pop()

return string

class Queue(object):
u"""
队列 先进先出
"""
def __init(self):
self.items = []

def isEmpty(self):
    return self.items == []

def size(self):
    return len(self.items)

def enqueue(self, item):
    self.items.insert(0, item) # O(n)

def dequeue(self):
    return self.items.pop() # O(1)

u"""
烫手山芋 循环传递n次 淘汰一个 直到最后幸存一个
"""
def hotPotato(namelist, num):
queue = Queue()

for name in namelist:
    queue.enqueue(name)

while queue.size() > 1:
    for i in range(num):
        queue.enqueue(queue.dequeue())

    queue.dequeue()
return queue.dequeue()

class Deque(object):
u"""
双端队列 可以同时从2端 进出
"""
def init(self):
self.items = []

def isEmpty(self):
    return self.items == []

def size(self):
    return len(self.items)

def addFront(self, item):
    self.items.insert(0, item) # O(n)

def removeFront(self):
    return self.items.pop(0) # O(n)

def addRear(self, item):
    self.items.append(item) # O(1)

def removeRear(self):
    return self.items.pop() # O(1)

u"""
回文检查 字符串前后一致
"""
def palchecker(aString):
deque = Deque()

for i in aString:
    deque.addRear(i)

while deque.size() > 1:
    if deque.removeFront() != deque.removeRear():
        return False
return True

u"""
链表 节点包含数据本身,并保存指向下一个节点的指针
"""
class Node(object):
def init(self, initdata):
self.data = initdata
self.next = None

def getData(self):
    return self.data

def getNext(self):
    return self.next

def setData(self, data):
    self.data = data

def setNext(self, node):
    self.next = node

u"""
无序链表
"""
class UnorderedList(object):
def init(self):
self.head = None

def isEmpty(self): # O(1)
    return self.head == None

def add(self, item): # O(1)
    node = Node(item)
    node.setNext(self.head)
    self.head = node

def size(self): # 遍历链 O(n)
    count = 0
    current = self.head
    while current:
        count += 1
        current = current.getNext()
    return count

def search(self, item): # O(n)
    current = self.head
    while current:
        if current.getData() == item:
            return True
        current = current.getNext()
    return False

def remove(self, item): # O(n)
    previous = None
    current = self.head
    while current:
        if current.getData() == item:
            if previous is None:
                self.head = current.getNext()
            else:
                previous.setNext(current.getNext())
            return
        previous = current
        current = current.getNext()

def append(self, item): # O(n)
    node = Node(item)
    if self.head is None:
        self.head = node
        return
    current = self.head
    while current.getNext():
        current = current.getNext()
    current.setNext(node)

def index(self, item): # O(n)
    count = 0
    current = self.head
    while current:
        if current.getData() == item:
            return count
        current = current.getNext()
        count += 1

def insert(self, pos, item): # O(k)
    node = Node(item)
    current = self.head
    previous = None
    for _ in range(pos):
        previous = current
        current = current.getNext()

    node.setNext(current)
    if previous is None:
        self.head = node
    else:
        previous.setNext(node)

def pop(self, pos=None):  # O(n)
    current = self.head
    previous = None

    if pos is None:
        while current.getNext():
            previous = current
            current = current.getNext()
    else:
        for _ in range(pos):
            previous = current
            current = current.getNext()

    if previous is None:
        self.head = current.getNext()
    else:
        previous.setNext(current.getNext())
    return current.getData()

u"""
有序链表
"""
class OrderedList(UnorderedList):
def add(self, item): # O(n)
previous = None
current = self.head
while current:
if current.getData() > item:
break
previous = current
current = current.getNext()
node = Node(item)
node.setNext(current)
if previous is None:
self.head = node
else:
previous.setNext(node)

def search(self, item):  # O(n)
    current = self.head
    while current:
        if current.getData() == item:
            return True
        elif current.getData() > item:
            break
        current = current.getNext()
    return False

u"""
递归

  1. 递归算法必须具有基本情况
  2. 递归算法必须改变状态并靠近其基本情况
  3. 递归算法必须递归方式调用自身
    """

u"""
递归算法 整数转字符串
"""
def toStr(n, base):
digits = '0123456789ABCDEF'
if n < base:
return digits[n]
else:
return toStr(n // base, base) + digits[n % base]

u"""
河内塔

  1. 把上层的所有盘子移动到中间
  2. 把最后一个移动到目标
  3. 把中间的所有盘子移动到目标
    """
    def moveTower(height, fromPole, toPole, withPole):
    if height >= 1:
    moveTower(height-1, fromPole, withPole, toPole)
    moveDisk(fromPole, toPole)
    moveTower(height-1, withPole, toPole, fromPole)

def moveDisk(fp,tp):
print 'move disk from ' + fp + ' to ' + tp

u"""
探索迷宫

  1. 向北边走一格, 递归
  2. 如果向北不能走, 向南走一格, 递归
  3. 如果向南不能走, 向东走一格, 递归
  4. 如果向东不能走, 向西走一格, 递归

先走一格, 然后继续向不同方向递归, 直到找到出口

def searchFrom(maze, startRow, startColumn):
maze.updatePosition(startRow, startColumn)
# Check for base cases:
# 1. We have run into an obstacle, return false
if maze[startRow][startColumn] == OBSTACLE : # 碰到墙壁
return False
# 2. We have found a square that has already been explored
if maze[startRow][startColumn] == TRIED: # 已经来过
return False
# 3. Success, an outside edge not occupied by an obstacle
if maze.isExit(startRow,startColumn):
maze.updatePosition(startRow, startColumn, PART_OF_PATH)
return True # 判断为出口
maze.updatePosition(startRow, startColumn, TRIED) # 设置来过

# Otherwise, use logical short circuiting to try each
# direction in turn (if needed)
found = searchFrom(maze, startRow-1, startColumn) or \
        searchFrom(maze, startRow+1, startColumn) or \
        searchFrom(maze, startRow, startColumn-1) or \
        searchFrom(maze, startRow, startColumn+1)
if found:
    maze.updatePosition(startRow, startColumn, PART_OF_PATH)
else:
    maze.updatePosition(startRow, startColumn, DEAD_END)
return found

"""

u"""
动态规划

硬币找零问题 找到最小可能的硬币数
"""
def recMC(coinValueList, change):
minCoins = change
if change in coinValueList:
return 1
else:
for i in [c for c in coinValueList if c <= change]:
num = 1 + recMC(coinValueList, change-i)
if num < minCoins:
minCoins = num
return minCoins

u"""
以上算法由于计算了大量的重复数据, 所以低效, 可以对已有的数据缓存, 避免重复计算
从顶至下
"""
def recDC(coinValueList, change, knownResults):
minCoins = change
if change in coinValueList:
knownResults[change] = 1
return 1
elif knownResults[change] > 0:
return knownResults[change]
else:
for i in [c for c in coinValueList if c <= change]:
num = 1 + recDC(coinValueList, change-i, knownResults)
if num < minCoins:
minCoins = num
knownResults[change] = minCoins
return minCoins

u"""
动态规划

  1. 最小最优解
  2. 从小到大, 使用已有的计算结果, 从底至上
    """
    def dpMakeChange(coinValueList, change, minCoins, coinsUsed):
    for cents in range(change+1):
    coinCount = cents
    newCoin = 1
    for i in [c for c in coinValueList if c <= cents]:
    if minCoins[cents-i] + 1 < coinCount:
    coinCount = minCoins[cents-i] + 1
    newCoin = i
    minCoins[cents] = coinCount
    coinsUsed[cents] = newCoin
    return minCoins[change]

打印最佳组合的各个值

def printCoins(coinsUsed, change):
l = []
while change:
l.append(coinsUsed[change])
change -= coinsUsed[change]
print l

u"""
背包问题

n=5是物品的数量,c=10是书包能承受的重量,w=[2,2,6,5,4]是每个物品的重量,v=[6,3,5,4,6]是每个物品的价值
"""
def bag(n, c, w, v):
res=[[-1 for j in range(c + 1)] for i in range(n + 1)]
for j in range(c + 1):
res[0][j]=0 # 把第一列初始化 0
for i in range(1, n + 1): # 行 1~5
for j in range(1, c + 1): # 列 1~10
res[i][j] = res[i - 1][j] # 先把上一行的值赋值个当前
if j >= w[i-1] and res[i][j] < res[i-1][j - w[i-1]] + v[i - 1]:
# 如果重量大于物品的行重量且当前价值小于物品价值+前一个物品重量,则重算最大重量
res[i][j] = res[i-1][j-w[i-1]] + v[i-1]
return res

def show(n, c, w, res):
print '最大价值', res[n][c]
x = [False for _ in w]
j = c
for i in range(1, n+1):
if res[i][j] > res[i-1][j]:
x[i-1] = True
j -= w[i-1]
for i, j in enumerate(x):
if j:
print '第{}个'.format(i+1)

u"""
二分查找
"""
def binarySearch(alist, item):
first = 0
last = len(alist) - 1

found = False
while first <= last and not found:
    mid = first + (last - first) // 2
    if alist[mid] == item:
        found = True
    elif alist[mid] > item:
        last = mid - 1
    else:
        first = mid + 1
return found

递归版

def binarySearch2(alist, item): # O( log^n )
if len(alist) == 0:
return False
else:
mid = len(alist) // 2
if alist[mid] == item:
return True
elif alist[mid] > item:
return binarySearch2(alist[:mid], item)
else:
return binarySearch2(alist[mid+1:], item)

u"""
哈希表

  1. 大小为m的哈希表,有从0~m-1为编号的槽 可以用数组表示

  2. 项与编号的映射方法叫 hash 函数,简单的示例可以用取余
    * 如果多个项经过 hash 函数得到的编号相同,则称为碰撞
    * 分组求和 把项的多个数字分组求和再取m的余
    * 平方取中 算项的平方值并去中间的某几位 再取m的余

  3. 项数/表大小m 的值 称为 负载因子

  4. 冲突解决 重新散列
    * 开放寻址 如果槽已被占用,则顺序往槽的下一个槽查找,直到找到空槽(线性探测)
    * 使用链式存储,在同一个编号上存储多个项
    """
    class HashTable(object):
    def init(self):
    self.size = 11 # 要为质数
    self.slots = [None]self.size
    self.data = [None]
    self.size

    def _hash(self, key):
    return key % self.size

    def _rehash(self, oldhash):
    return (oldhash+1)%self.size

    def put(self, key, data):
    hashvalue = self._hash(key)
    if self.slots[hashvalue] is None: # 新增
    self.slots[hashvalue] = key
    self.data[hashvalue] = data
    elif self.slots[hashvalue] == key: # 修改
    self.data[hashvalue] = data
    else:
    nextslot = self._rehash(hashvalue)
    while self.slots[nextslot] != None and self.slots[nextslot] != key:
    nextslot = self._rehash(hashvalue)
    if self.slots[nextslot] is None:
    self.slots[nextslot] = key
    self.data[nextslot] = data

    def get(self, key):
    position = self._get_position(key)
    return self.data[position] if position is not None else None

    def _get_position(self, key): # 理想的情况下 O(1) 最差 O(n)
    startslot = self._hash(key)
    position = startslot
    while self.slots[position] != None:
    if self.slots[position] == key:
    return position
    else:
    position = self._rehash(position)
    if position == startslot:
    return None
    return None

    def getitem(self, key):
    return self.get(key)

    def setitem(self, key, data):
    self.put(key, data)

    def len(self):
    count = 0
    for i in self.slots:
    if i is not None:
    count += 1
    return count

    def delitem(self, key):
    position = self._get_position(key)
    if position is not None:
    self.slots[position] = None
    self.data[position] = None

    def contains(self, key):
    position = self._get_position(key)
    return False if position is None else True

u"""
冒泡排序
"""
def bubbleSort(alist): # O(n^2)
for passnum in range(len(alist)-1, 0, -1):
for i in range(passnum):
if alist[i] > alist[i+1]:
alist[i], alist[i+1] = alist[i+1], alist[i]

u"""
短冒泡排序

前一次遍历如果没有发生交换,证明整个序列以排序
"""
def shortBubbleSort(alist):
passnum = len(alist) - 1
exchange = True
while passnum > 0 and exchange:
exchange = False
for i in range(passnum):
if alist[i] > alist[i+1]:
exchange = True
alist[i], alist[i+1] = alist[i+1], alist[i]
passnum -= 1

u"""
选择排序

同冒泡排序,只是减少的交换次数
"""
def selectionSort(alist):
for passnum in range(len(alist)-1, 0, -1):
maxPositon = 0
for i in range(1, passnum+1):
if alist[i] > alist[maxPositon]:
maxPositon = i
alist[passnum], alist[maxPositon] = alist[maxPositon], alist[passnum]

u"""
插入排序 O(n^2)

维持最小的已排序序列,迭代未排序项,插入到合适的位置
"""
def insertionSort(alist):
for i in range(1, len(alist)):
current = alist[i]
position = i

    while position > 0 and alist[position-1] > current:
        alist[position] = alist[position-1]
        position -= 1

    alist[position] = current

u"""
shell 排序

通过gap间隔来划分子序列,先对所有的子序列进行插入排序,然后再对整个序列插入排序
O(n^3/2) 稍稍好于插入排序
"""
def shellSort(alist):
count = len(alist) // 2 # 选择gap
while count > 0:
for i in range(count):
gapInsertionSort(alist, i, count)

    count = count // 2

def gapInsertionSort(alist, start, gap):
for i in range(start+gap, len(alist), gap):
current = alist[i]
position = i

    while position > start and alist[position-gap] > current:
        alist[position] = alist[position-gap]
        position -= gap

    alist[position] = current

u"""
归并排序

递归算法,分治,把序列切割成最小的部分并排序,然后逐步合并已排序的结果 O(nlog^n)
"""
def mergeSort(alist):
if len(alist) > 1:
mid = len(alist)//2 # 二分 log^n
left = alist[:mid]
right = alist[mid:]

    mergeSort(left) # 递归排序,最小部分为 长度为1的列表
    mergeSort(right)

    # 合并已排序的片段 最大花费n次
    i = j = k = 0
    while i < len(left) and j < len(right):
        if left[i] < right[j]:
            alist[k] = left[i]
            i += 1
        else:
            alist[k] = right[j]
            j += 1
        k += 1

    while i < len(left):
        alist[k] = left[i]
        i += 1
        k += 1

    while j < len(right):
        alist[k] = right[j]
        j += 1
        k += 1

u"""
快速排序 O(nlogn)
"""
def quickSort(alist):
quickSortHelper(alist,0,len(alist)-1)

def quickSortHelper(alist, first, last):
if first < last:
i, j = first, last
current = alist[first]
while i < j:
while i < j and alist[j] >= current:
j -= 1
alist[i] = alist[j]

        while i < j and alist[i] <= current:
            i += 1
        alist[j] = alist[i]

    alist[i] = current

    quickSortHelper(alist,first,i-1)
    quickSortHelper(alist,i+1,last)

u"""

节点 树的基础部分 可以有附加属性,称为有效载荷 key
边 表示节点之间的联系, 除了 根节点 其它节点都有从父节点来的边(传入边)
根 没有传入边的节点
路径 由边连接的节点的有序序列
子节点 传入边为相同父节点
父节点
兄弟
子树 由父节点 与 子孙节点组成的一组节点与边
叶节点 没有子节点的节点
层数 由根到节点之间边的条数
高度 最大的叶子节点层数

定义 树由一组节点和连接节点的边组成

  • 树的一个节点指定为根节点
  • 除了根,没有节点n通过边与p相连,n是p的子节点
  • 从根遍历到某个节点的路径唯一

定义 树是由根与一系列子树组成,子数的根与根相连,递归定义
"""

u"""
列表表示 二叉树
"""
def binaryTree(r):
return [r, [], []]

def insertLeft(root,newBranch):
node = root.pop(1)
if node:
root.insert(1, [newBranch, node, []])
else:
root.insert(1, [newBranch, [], []])
return root

def insertRight(root,newBranch):
node = root.pop(2)
if node:
root.insert(2, [newBranch, [], node])
else:
root.insert(2, [newBranch, [], []])
return root

def getRootVal(root):
return root[0]

def setRootVal(root,newVal):
root[0] = newVal

def getLeftChild(root):
return root[1]

def getRightChild(root):
return root[2]

u"""
节点表示
"""
class BinaryTree(object):
def init(self, val):
self.key = val
self.left = None
self.right = None

def insertLeft(self, val):
    node = BinaryTree(val)
    if self.left is None:
        self.left = node
    else:
        node.left = self.left
        self.left = node

def insertRight(self, val):
    node = BinaryTree(val)
    if self.right is None:
        self.right = node
    else:
        node.right = self.right
        self.right = node

def getRootVal(self):
    return self.key

def setRootVal(self, val):
    self.key = val

def getLeftChild(self):
    return self.left

def getRightChild(self):
    return self.right

def preorder(self):
    print self.key
    if self.left:
        self.left.preorder()
    if self.right:
        self.right.preorder()

u"""
使用树构建数值运算
( 1 + ( 4 * 3 ) )
"""
def buildParseTree(fpexp):
fplist = fpexp.split()
pstack = Stack()
etree = BinaryTree('')
pstack.push(etree)
current = etree
for i in fplist:
if i == '(':
current.insertLeft('')
pstack.push(current)
current = current.getLeftChild()
elif i not in ['+', '-', '', '', ')']:
current.setRootVal(int(i))
current = pstack.pop()
elif i in ['+', '-', '', '']:
current.setRootVal(i)
current.insertRight('')
pstack.push(current)
current = current.getRightChild()
elif i == ')':
current = pstack.pop()
print current.getRootVal()
return etree

import operator

u"""
递归算式树求值
"""
def evaluate(parseTree):
opers = {'+':operator.add, '-':operator.sub, '*':operator.mul, '/':operator.truediv}

left = parseTree.getLeftChild()
right = parseTree.getRightChild()

if left and right:
    fn = opers[parseTree.getRootVal()]
    return fn(evaluate(left), evaluate(right))
else:
    return parseTree.getRootVal()

u"""
树的遍历

前序 返问根,递归前序遍历左子树,递归前序遍历右子树
中序 递归中序遍历左子树,返问根,递归中序遍历右子树
后序 递归后序遍历左子树,递归后序遍历右子树,返问根
"""
def preorder(tree): # 前序
if tree:
print tree.getRootVal()
preorder(tree.getLeftChild())
preorder(tree.getRightChild())

def postorder(tree): # 后序
if tree:
postorder(tree.getLeftChild())
postorder(tree.getRightChild())
print tree.getRootVal()

u"""
遍历求值
"""
def postordereval(tree):
opers = {'+':operator.add, '-':operator.sub, '*':operator.mul, '/':operator.truediv}
res1 = None
res2 = None
if tree:
res1 = postordereval(tree.getLeftChild())
res2 = postordereval(tree.getRightChild())
if res1 and res2:
return opers[tree.getRootVal()](res1, res2)
else:
return tree.getRootVal()

def inorder(tree): # 中序
if tree:
inorder(tree.getLeftChild())
print tree.getRootVal()
inorder(tree.getRightChild())

u"""
打印原始分析树
"""
def printexp(tree):
s = ''
if tree:
s = '(' + printexp(tree.getLeftChild())
s = s + tree.getRootVal()
s = printexp(tree.getRightChild()) + ')'
return s

u"""
二叉堆 最小堆

子节点的一定小于父节点,根节点最小
每层的最大节点个数为2^h,所以子节点一定是父节点的2n 2n+1
"""
class BinHeap(object):
def init(self):
self.heapList = [0]
self.currentSize = 0

def insert(self, i): # O(log2^n)
    self.heapList.append(i)
    self.currentSize += 1

    i = self.currentSize
    j = i // 2 # 找到父节点的位置
    while j > 0: # 对比交换
        if self.heapList[i] < self.heapList[j]:
            self.heapList[i], self.heapList[j] = self.heapList[j], self.heapList[i]
        i = j
        j = j // 2

def delMin(self): # O(log2^n)
    minVal = self.heapList[1]
    self.heapList[1] = self.heapList[-1]
    self.currentSize -= 1
    self.heapList.pop() # 删除最小,把最大的移动到根
    self._percDown(1) # 遍历比较,把合适的移动到根
    return minVal

def _minChild(self, i):
    if (i * 2) + 1 > self.currentSize:
        return i * 2
    elif self.heapList[i*2] > self.heapList[i*2+1]:
        return i * 2 + 1
    else:
        return i * 2

def _percDown(self, i):
    while (i * 2) <= self.currentSize:
        mc = self._minChild(i)
        if self.heapList[i] > self.heapList[mc]:
            self.heapList[i], self.heapList[mc] = self.heapList[mc], self.heapList[i]
        i = mc

def buildHeap(self, alist): # O(n)
    self.currentSize = len(alist)
    self.heapList = [0] + alist[:]
    i = self.currentSize // 2
    while i > 0:
        self._percDown(i)
        i -= 1

def isEmpty(self):
    return True if self.currentSize == 0 else False

def size(self):
    return self.currentSize

u"""
最大堆
"""
class MaxBinHeap(object):
def init(self):
self.heapList = [0]
self.currentSize = 0

def insert(self, i):
    self.heapList.append(i)
    self.currentSize += 1

    i = self.currentSize
    j = i // 2
    while j > 0:
        if self.heapList[i] > self.heapList[j]:
            self.heapList[i], self.heapList[j] = self.heapList[j], self.heapList[i]
        i = j
        j = j // 2

def delMax(self):
    maxVal = self.heapList[1]
    self.heapList[1] = self.heapList[-1]
    self.currentSize -= 1
    self.heapList.pop()
    self._percUp(1)
    return maxVal

def _maxChild(self, i):
    if (i * 2) + 1 > self.currentSize:
        return i * 2
    elif self.heapList[i*2] < self.heapList[i*2+1]:
        return i * 2 + 1
    else:
        return i * 2

def _percUp(self, i):
    while (i * 2) <= self.currentSize:
        mc = self._maxChild(i)
        if self.heapList[i] < self.heapList[mc]:
            self.heapList[i], self.heapList[mc] = self.heapList[mc], self.heapList[i]
        i = mc

def buildHeap(self, alist):
    self.currentSize = len(alist)
    self.heapList = [0] + alist[:]
    i = self.currentSize // 2
    while i > 0:
        self._percUp(i)
        i -= 1

def isEmpty(self):
    return True if self.currentSize == 0 else False

def size(self):
    return self.currentSize

u"""
堆排序

构建最大堆
把根交换到最后
递归n-1构建最大堆,交换 O(nlogn)
"""
def heap_sort(lst):
def sift_down(start, end):
root = start
while 1:
child = 2 * root + 1
if child > end:
break
if child + 1 < end and lst[child] < lst[child+1]:
child = child + 1
if lst[root] < lst[child]:
lst[root], lst[child] = lst[child], lst[root]
root = child
else:
break
i = len(lst) // 2 # 构建最大堆
while i >= 0:
sift_down(i, len(lst) - 1)
i -= 1

for end in range(len(lst) - 1, 0, -1):
    lst[0], lst[end] = lst[end], lst[0] # 交换,重复构建
    sift_down(0, end-1)

u"""
二叉搜索树

节点的左子节点必须小于节点的key
节点的右子节点必须大于节点的key

get put delete 都是遍历高度 O(log2^n)
如果输入是有序的,那么就是最坏的结果O(n), 因为只会遍历左边或者右边
"""
class BinarySearchTree(object):
def init(self):
self.root = None
self.size = 0

def length(self):
    return self.size

def __len__(self):
    return self.size

def __iter__(self):
    return self.root.__iter__()

def put(self, key, val): # 复杂度在于树的高度 O(log2^n)
    if self.root is None:
        self.root = TreeNode(key, val)
    else:
        self._put(key, val, self.root)
    self.size += 1

def _put(self, key, val, current):
    if key > current.key:
        if current.hasRightChild():
            self._put(key, val, current.right)
        else:
            current.right = TreeNode(key, val, parent=current)
    elif key < current.key:
        if current.hasLeftChild():
            self._put(key, val, current.left)
        else:
            current.left = TreeNode(key, val, parent=current)
    else:
        current.playload = val # key 相同时 直接替换值

def __setitem__(self, k, v):
    self.put(k, v)

def get(self, key): # O(log2^n)
    if self.root:
        node = self._get(key, self.root)
        if node:
            return node.playload
    return None

def _get(self, key, current):
    if not current:
        return None
    elif key > current.key:
        return self._get(key, current.right)
    elif key < current.key:
        return self._get(key, current.left)
    else:
        return current

def __getitem__(self, k):
    return self.get(k)

def __contains__(self, k):
    return bool(self._get(k, self.root))

def __delitem__(self, k):
    self.delete(k)

def delete(self, key): # 虽然很复杂, 但是实际上向下遍历找到合适的节点, 所有最大还是高度 O(log2^n)
    if self.size > 1:
        node = self._get(key, self.root)
        if node:
            self.removeNode(node)
            self.size -= 1
        else:
            raise KeyError(key)
    elif self.size == 1 and self.root.key == key:
        self.root = None
        self.size -= 1
    else:
        raise KeyError(key)

def removeNode(self, current):
    if current.isLeaf(): # 如果是叶子节点,直接删除
        if current.isLeftChild():
            current.parent.left = None
        else:
            current.parent.right = None
    elif current.hasBothChildren(): # 有2个子节点
        succ = current.findSuccessor()
        succ.spliceOut()
        current.key = succ.key
        current.playload = succ.key

    else: # 有1个子节点
        if current.hasLeftChild(): # 有左节点
            if current.isLeftChild():
                current.parent.left = current.left
                current.left.parent = current.parent
            elif current.isRightChild():
                current.parent.right = current.left
                current.left.parent = current.parent
            else: # 根节点,直接使用子节点的数据替换自己
                current.replaceNodeData(current.left.key, current.left.playload,
                    current.left.left, current.left.right)
        else:
            if current.isLeftChild():
                current.parent.left = current.right
                current.right.parent = current.parent
            elif current.isRightChild():
                current.parent.right = current.right
                current.right.parent = current.parent
            else: # 根节点
                current.replaceNodeData(current.right.key, current.right.playload,
                    current.right.left, current.right.right)

class TreeNode(object):
def init(self, key, val, left=None, right=None, parent=None):
self.key = key
self.playload = val
self.left = left
self.right = right
self.parent = parent
self.balanceFactor = 0

def hasLeftChild(self):
    return self.left

def hasRightChild(self):
    return self.right

def isLeftChild(self):
    return self.parent and self.parent.hasLeftChild() == self

def isRightChild(self):
    return self.parent and self.parent.hasRightChild() == self

def isRoot(self):
    return not self.parent

def isLeaf(self):
    return not (self.left and self.right)

def hasAnyChildren(self):
    return self.left or self.right

def hasBothChildren(self):
    return self.left and self.right

def replaceNodeData(self, key, value, lc, rc):
    self.key = key
    self.playload = value
    self.left = lc
    self.right = rc
    if self.hasLeftChild():
        self.left.parent = self
    if self.hasRightChild():
        self.right.parent = self

def findSuccessor(self):
    succ = None
    if self.hasRightChild():
        return self.right.findMin()
    else:
        if self.parent:
            if self.isLeftChild():
                succ = self.parent
            else: # 如果没有右节点, 且自己是右节点, 父节点小于自己, 遍历向上找
                self.parent.right = None # 可以肯定的是节点 右边节点一定会大于节点本身
                succ = self.parent.findSuccessor()
                self.parent.right = self
    return succ

def findMin(self):
    current = self
    while current.hasLeftChild():
        current = current.left
    return current

def spliceOut(self):
    if self.isLeaf():
        if self.isLeftChild():
            self.parent.left = None
        else:
            self.parent.right = None
    elif self.hasLeftChild():
        if self.isLeftChild():
            self.parent.left = self.left
        else:
            self.parent.right = self.left
        self.left.parent = self.parent
    else:
        if self.isLeftChild():
            self.parent.left = self.right
        else:
            self.parent.right = self.right
        self.right.parent = self.parent

def __iter__(self):
    if self:
        if self.hasLeftChild():
            for ele in self.left:
                yield ele
        yield self.key
        if self.hasRightChild():
            for ele in self.right:
                yield ele

u"""
平衡二叉树
AVL树 自动保持平衡的树

平衡 根的左子树与右子树的高度差不超过1
"""
class AvlTree(BinarySearchTree):
def _put(self, key, val, current):
if key > current.key:
if current.hasRightChild():
self._put(key, val, current.right)
else:
current.right = TreeNode(key, val, parent=current)
self.updateBalance(current.right)
elif key < current.key:
if current.hasLeftChild():
self._put(key, val, current.left)
else:
current.left = TreeNode(key, val, parent=current)
self.updateBalance(current.left)
else:
current.playload = val

def updateBalance(self, node):
    u"""
    1. 递归调用到树的根,直到找到平衡因子大于1的节点
    2. 父节点的平衡因子已调整为零。你应该说服自己,一旦一个子树的平衡因子为零,那么它的祖先节点的平衡不会改变。
    """
    if node.balanceFactor > 1 or node.balanceFactor < -1:
        self.rebalance(node) # 通过旋转可以保证节点平衡因子为0
        return
    if node.parent != None:
        if node.isLeftChild():
            node.parent.balanceFactor += 1
        elif node.isRightChild():
            node.parent.balanceFactor -= 1
        if node.parent.balanceFactor != 0:
            self.updateBalance(node.parent)

def rebalance(self, node):
    if node.balanceFactor < 0:
        if node.right.balanceFactor > 0:
            self.rotateRight(node.right)
        self.rotateLeft(node)
    elif node.balanceFactor > 0:
        if node.left.balanceFactor < 0:
            self.rotateLeft(node.left)
        self.rotateRight(node)

def rotateLeft(self, rotRoot): # 左旋转
    u"""
    1. 提升右孩子(B)成为子树的根。
    2. 将旧根(A)移动为新根的左子节点。
    3. 如果新根(B)已经有一个左孩子,那么使它成为新左孩子(A)的右孩子。
       注意:由于新根(B)是A的右孩子,A 的右孩子在这一点上保证为空。
       这允许我们添加一个新的节点作为右孩子,不需进一步的考虑。
    """
    newRoot = rotRoot.right # 右节点为新根
    rotRoot.right = newRoot.left # 新根的左节点称为旧根的右节点
    if newRoot.hasLeftChild():
        newRoot.left.parent = rotRoot
    newRoot.parent = rotRoot.parent
    if rotRoot.isRoot():
        self.root = newRoot
    else:
        if rotRoot.isLeftChild():
            rotRoot.parent.left = newRoot
        elif rotRoot.isRightChild():
            rotRoot.parent.right = newRoot
    newRoot.left = rotRoot
    rotRoot.parent = newRoot
    rotRoot.balanceFactor = rotRoot.balanceFactor + 1 - min(newRoot.balanceFactor, 0)
    newRoot.balanceFactor = newRoot.balanceFactor + 1 + max(rotRoot.balanceFactor, 0)

def rotateRight(self, rotRoot): # 右旋转
    newRoot = rotRoot.left
    rotRoot.left = newRoot.right
    if newRoot.hasRightChild():
        newRoot.right.parent = rotRoot
    newRoot.parent = rotRoot.parent
    if rotRoot.isRoot():
        self.root = newRoot
    else:
        if rotRoot.isLeftChild():
            rotRoot.parent.left = newRoot
        elif rotRoot.isRightChild():
            rotRoot.parent.right = newRoot
    newRoot.right = rotRoot
    rotRoot.parent = newRoot
    rotRoot.balanceFactor = rotRoot.balanceFactor + 1 - min(newRoot.balanceFactor, 0)
    newRoot.balanceFactor = newRoot.balanceFactor + 1 + max(rotRoot.balanceFactor, 0)

u"""

顶点 同树中的节点
边 连接2个顶点,可以是多个方向的,如果图中边都只有一个方法,则称为有向图
权重 边可以被加权,表示顶点到顶点过去的成本
路径 由边连接的顶点的序列, 未加权的路径长度n-1, 加权的为路径所有边权重的和
循环 有向图的循环是同一顶点为起点和终点的路径, 没有循环的图称为非循环图形, 没有循环的有向图称为无环图 DAG

图 是由顶点集合与边集合组成的 G = (V, E) G 图 V 顶点集合 E 边的集合
"""

u"""
邻接矩阵

行与列存储了图的顶点,行与列的单元格存了边的加权值,如果2个顶点间的单元格值,则2个顶点相邻
由于存在很多的空单元格,大部分情况下矩阵是稀疏的,但是对于边很多的情况下,领接矩阵很适合

领接表

适用于边很稀疏的情况
对应于列的顶点列表,对每个顶点维护一个边的列表,保存了顶点与权重
空间更高效,并且保存了顶点直接连接的其它顶点
"""
import sys

class Vertex:
def init(self,num):
self.id = num
self.connectedTo = {}
self.color = 'white' # 是否已访问 white 未访问 gray 正在访问 black 已访问
self.dist = sys.maxsize
self.pred = None # 上级顶点
self.disc = 0 # 距离开始顶点的距离或者顶点标识为 gray 的时间
self.fin = 0 # 顶点标识为 black 的时间

def addNeighbor(self,nbr,weight=0):
    self.connectedTo[nbr] = weight

def setColor(self,color):
    self.color = color

def setDistance(self,d):
    self.dist = d

def setPred(self,p):
    self.pred = p

def setDiscovery(self,dtime):
    self.disc = dtime

def setFinish(self,ftime):
    self.fin = ftime

def getFinish(self):
    return self.fin

def getDiscovery(self):
    return self.disc

def getPred(self):
    return self.pred

def getDistance(self):
    return self.dist

def getColor(self):
    return self.color

def getConnections(self):
    return self.connectedTo.keys()

def getWeight(self,nbr):
    return self.connectedTo[nbr]

def getId(self):
    return self.id

class Graph(object):
def init(self):
self.vertList = {}
self.numVertices = 0

def addVertex(self, key):
    ver = Vertex(key)
    self.numVertices += 1
    self.vertList[key] = ver
    return ver

def getVertex(self, n):
    if n in self.vertList:
        return self.vertList[n]
    return None

def __contains__(self, n):
    return n in self.vertList

def addEdge(self, f, t, cost=0):
    if f not in self.vertList:
        self.addVertex(f)
    if t not in self.vertList:
        self.addVertex(t)
    self.vertList[f].addNeighbor(self.vertList[t], cost)

def getVertices(self):
    return self.vertList.keys()

def __iter__(self):
    return iter(self.vertList.values())

u"""
构建字梯图
"""
def buildGraph(words): # O(n^2)
d = {}
g = Graph()
for word in words:
for i in range(len(word)):
bucket = word[:i] + '_' + word[i+1:]
if bucket in d:
d[bucket].append(word)
else:
d[bucket] = [word]
for bucket in d.keys():
for word1 in d[bucket]:
for word2 in d[bucket]:
if word1 != word2:
g.addEdge(word1, word2)
return g

u"""
广度优先 BFS
"""
def bfs(start): # O(V) 每个顶点只会访问一次
start.setDistance(0)
start.setPred(None)
vertQueue = Queue()
vertQueue.enqueue(start)
while (vertQueue.size() > 0):
currentVert = vertQueue.dequeue()
for nbr in currentVert.getConnections():
if (nbr.getColor() == 'white'):
nbr.setColor('gray')
nbr.setDistance(currentVert.getDistance() + 1)
nbr.setPred(currentVert)
vertQueue.enqueue(nbr)
currentVert.setColor('black')

def traverse(y): # 反向追踪找到路径
x = y
while x.getPred():
print x.getId()
x = x.getPred()

u"""
骑士之旅

棋盘上的每个单元格都需要访问到
"""
def knightGraph(bdSize):
graph = Graph()
for row in range(bdSize):
for col in range(bdSize):
nodeId = posToNodeId(row, col, bdSize)
newPositions = genLegalMoves(row, col, bdSize)
for e in newPositions:
nid = posToNodeId(e[0], e[1], bdSize)
graph.addEdge(nodeId, nid)
return graph

def posToNodeId(row, column, board_size):
return (row * board_size) + column

def genLegalMoves(x, y, bdSize):
newMoves = []
moveOffsets = [(-1,-2),(-1,2),(-2,-1),(-2,1),
( 1,-2),( 1,2),( 2,-1),( 2,1)]
for i in moveOffsets:
newX = x + i[0]
newY = y + i[1]
if legalCoord(newX, bdSize) and
legalCoord(newY, bdSize):

        newMoves.append((newX, newY))
return newMoves

def legalCoord(x, bdSize):
if x >= 0 and x < bdSize:
return True
return False

u"""
深度优先 DFS
"""
def knightTour(n, path, u, limit):
u"""
n: 搜索树中当前深度
path: 搜索路径节点列表
u: 希望找到的顶点
limit: 路径中的节点数,最大的深度
"""
u.setColor('gray')
path.append(u)
if n < limit:
# nbrList = list(u.getConnections())
nbrList = orderByAvail(u)
i = 0
done = False
while i < len(nbrList) and not done:
if nbrList[i].getColor() == 'white':
done = knightTour(n+1, path, nbrList[i], limit)
i = i + 1
if not done: # prepare to backtrack
path.pop()
u.setColor('white') # 回溯,遍历下一个邻居
else:
done = True
return done

u"""
启发式

倾向与先遍历到有较少的可选的节点,这样骑士会先覆盖棋盘周边的方格,然后向中间,
避免在中间路径过多的选择, 由中间向周边发散会产生更多的短路径
"""
def orderByAvail(n):
resList = []
for v in n.getConnections():
if v.getColor() == 'white':
c = 0
for w in v.getConnections():
if w.getColor() == 'white':
c = c + 1
resList.append((c,v))
resList.sort(key=lambda x: x[0])
return [y[1] for y in resList]

class DFSGraph(Graph):
def init(self):
super(DFSGraph, self).init()
self.time = 0

def dfs(self):
    for aVertex in self:
        aVertex.setColor('white')
        aVertex.setPred(-1)
    for aVertex in self:
        if aVertex.getColor() == 'white':
            self.dfsvisit(aVertex)

def dfsvisit(self, startVertex): # O(V + E) 覆盖每个顶点,并且每个边都会过一次
    u"""
    遍历过后的所有其它节点都能找到到开始节点的路径,构成深度优先森林
    """
    startVertex.setColor('gray')
    self.time += 1
    startVertex.setDiscovery(self.time) # 开始遍历的时间
    for nextVertex in startVertex.getConnections():
        if nextVertex.getColor() == 'white':
            nextVertex.setPred(startVertex)
            self.dfsvisit(nextVertex)
    startVertex.setColor('black')
    self.time += 1
    startVertex.setFinish(self.time) # 遍历完成的时间

u"""
拓扑排序

有向无环图

  1. 对于某些图 g 调用 dfs(g)。我们想要调用深度优先搜索的主要原因是计算每个顶点的完成时间。
  2. 以完成时间的递减顺序将顶点存储在列表中。
  3. 返回有序列表作为拓扑排序的结果。得到正确的完成步骤
    """

u"""
强联通分量

C 是 G 的子集, 并且 C 的每个顶点都是互通的(强联通)

  1. 调用dfs计算G的每个顶点的完成时间
  2. 计算G^T 复制G,并翻转每个边的方向
  3. 为G^T调用dfs, 但在 DFS 的主循环中,以完成时间的递减顺序探查每个顶点。
  4. 在步骤 3 中计算的森林中的每个树是强连通分量。输出森林中每个树中每个顶点的顶点标识组件。
    """
    def buildSCC():
    g = DFSGraph()
    g.addEdge('A', 'B')
    g.addEdge('B', 'C')
    g.addEdge('C', 'F')
    g.addEdge('F', 'H')
    g.addEdge('H', 'I')
    g.addEdge('I', 'F')
    g.addEdge('B', 'E')
    g.addEdge('E', 'D')
    g.addEdge('D', 'G')
    g.addEdge('G', 'E')
    g.addEdge('E', 'A')
    g.addEdge('D', 'B')
    g.dfs()
    return g

def buildGT(g):
newG = DFSGraph()
for vert in g: # 构建G^T
for nbr in vert.connectedTo.keys():
newG.addEdge(nbr.getId(), vert.getId())
verts = sorted(newG.vertList.values(), key=lambda vert: g.getVertex(vert.getId()).getFinish(), reverse=True)
for aVertex in verts:
aVertex.setColor('white')
aVertex.setPred(-1)
for aVertex in verts:
if aVertex.getColor() == 'white':
newG.dfsvisit(aVertex) # 构建的子树即为强连通分量
return newG

u"""
最短路径问题

针对加权边的图
"""
class PriorityQueue(object):
u"""
优先级队列
"""
def init(self):
self.heapArray = [(0,0)]
self.currentSize = 0

def buildHeap(self,alist):
    self.currentSize = len(alist)
    self.heapArray = [(0,0)]
    for i in alist:
        self.heapArray.append(i)
    i = len(alist) // 2
    while (i > 0):
        self.percDown(i)
        i = i - 1

def percDown(self,i):
    while (i * 2) <= self.currentSize:
        mc = self.minChild(i)
        if self.heapArray[i][0] > self.heapArray[mc][0]:
            tmp = self.heapArray[i]
            self.heapArray[i] = self.heapArray[mc]
            self.heapArray[mc] = tmp
        i = mc

def minChild(self,i):
    if i*2 > self.currentSize:
        return -1
    else:
        if i*2 + 1 > self.currentSize:
            return i*2
        else:
            if self.heapArray[i*2][0] < self.heapArray[i*2+1][0]:
                return i*2
            else:
                return i*2+1

def percUp(self,i):
    while i // 2 > 0:
        if self.heapArray[i][0] < self.heapArray[i//2][0]:
           tmp = self.heapArray[i//2]
           self.heapArray[i//2] = self.heapArray[i]
           self.heapArray[i] = tmp
        i = i//2

def add(self,k):
    self.heapArray.append(k)
    self.currentSize = self.currentSize + 1
    self.percUp(self.currentSize)

def delMin(self):
    retval = self.heapArray[1][1]
    self.heapArray[1] = self.heapArray[self.currentSize]
    self.currentSize = self.currentSize - 1
    self.heapArray.pop()
    self.percDown(1)
    return retval

def isEmpty(self):
    if self.currentSize == 0:
        return True
    else:
        return False

def decreaseKey(self,val,amt):
    # this is a little wierd, but we need to find the heap thing to decrease by
    # looking at its value
    done = False
    i = 1
    myKey = 0
    while not done and i <= self.currentSize:
        if self.heapArray[i][1] == val:
            done = True
            myKey = i
        else:
            i = i + 1
    if myKey > 0:
        self.heapArray[myKey] = (amt,self.heapArray[myKey][1])
        self.percUp(myKey)

def __contains__(self,vtx):
    for pair in self.heapArray:
        if pair[1] == vtx:
            return True
    return False

u"""
dijkstra算法, 类似于bfs, 只是使用优先级队列, 按权重最小排列
最终算出其它顶点与开始顶点间最短距离
只有权重为正才能正常运行,否则会死循环
"""
def dijkstra(aGraph, start): # O((V + E)log^V )
pq = PriorityQueue()
start.setDistance(0)
pq.buildHeap([(v.getDistance(), v) for v in aGraph]) # 所有的都放进去了
while not pq.isEmpty():
currentVert = pq.delMin()
for nextVert in currentVert.getConnections():
newDist = currentVert.getDistance()
+ currentVert.getWeight(nextVert)
if newDist < nextVert.getDistance():
nextVert.setDistance(newDist)
nextVert.setPred(currentVert)
pq.decreaseKey(nextVert, newDist) # 移动到队列的前面

u"""
最小权重生成树

Prim生成树算法

使用最小权重的边重写顶点的路径, 贪婪算法
"""
def prim(G, start):
pq = PriorityQueue()
for v in G:
v.setDistance(sys.maxint)
v.setPred(None)
start.setDistance(0)
pq.buildHeap([(v.getDistance(), v) for v in G])
while not pq.isEmpty():
currentVert = pq.delMin()
for nextVert in currentVert.getConnections():
newCost = currentVert.getWeight(nextVert) + currentVert.getDistance()
if nextVert in pq and newCost < nextVert.getDistance():
nextVert.setPred(currentVert)
nextVert.setDistance(newCost)
pq.decreaseKey(nextVert, newCost

转自:
https://zhu327.github.io/2017/03/14/%E7%94%A8python%E8%A7%A3%E5%86%B3%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95%E9%97%AE%E9%A2%98/

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271

推荐阅读更多精彩内容

  • Lua 5.1 参考手册 by Roberto Ierusalimschy, Luiz Henrique de F...
    苏黎九歌阅读 13,517评论 0 38
  • 用于python面试整理,主要来源于http://www.cnblogs.com/skiler/p/6952707...
    十里江城阅读 2,247评论 0 13
  • 她姓苏, 她不爱哭。 可是我却见过她哭。 因为求而不得而哭, 因为自己的付出而哭, 因为自己很想他而哭。 我不忍看...
    李木只阅读 240评论 9 5
  • 流连反复忘, 夏日怀旧金。 生无可恋情, 生生爱风疯。
    豌豆_1249阅读 273评论 1 2
  • 最近看电视剧《锦绣未央》,生生地被李常茹贱倒了,可细细想来自己周遭这样的人好像还真的有啊!只想说我此生学不会,来世...
    你我123阅读 217评论 0 0