#Page111 队列 击鼓传花
from pythonds.basic.queue import Queue #import对应模块

def hotPotato(namelist, num):#参数为人队列和次数
    simqueue = Queue()#创建新的队列
    for name in namelist:#对于namelist中的每个元素
        simqueue.enqueue(name)#把其压入此队列中
#压入所有姓名
    while simqueue.size() > 1:#如果不到最后(因为最后要求只剩一个人)
        for i in range(num):#
            simqueue.enqueue(simqueue.dequeue())#把列头pop出来,再塞到队尾,重复num次
        simqueue.dequeue()#每完成Num次,永久弹出列头,直到simqueue.size()=1
    return simqueue.dequeue()#直到最终把剩下的那个人的名字展示出来

#page122 回文检查
from pythonds.basic.deque import Deque #import双端队列模块

def palchecker(aString):#输入是一串字符串
    chardeque = Deque()#创建双端队列

    for ch in aString:
        chardeque.addRear(ch)#把输入的字符串每个字符依次添加到队尾

    stillEqual = True

    while chardeque.size() > 1 and stillEqual:#双闸控制的while语句
        first = chardeque.removeFront()#弹出头部
        last = chardeque.removeRear()#弹出尾部
        if first != last:
            stillEqual = False#不相等的话就直接跳出循环

    return stillEqual#表示是否回文匹配完全成功

#时间复杂度为O(n)的两列表合并且排序算法
for k = 1 to n
if B(i)<C(i)
    D(k)=B(i)
    i++
else [C(j)<B(i)]
    D(k)=C(j)
    j++
End

#进制任意转化
def toStr(n,base):#参数是十进制数字,以及要转换到的进制
    convertString = '0123456789ABCDEF'
    if n < base:#如果此数小于要转换成的进制,那么就直接
        return convertString[n]
    else:
        return toStr(n//base,base) + convertString[n%base]
#利用栈的特性进行进制转化
from pythonds.basic.stack import Stack#导入stack模块
rStack = Stack()

def toStr(n,base):
    convertString = '0123456789ABCDEF'
    while n > 0:#控制语句,当输入的数字大于0才有意义
        if n < base:#如果n小于要转换成的进制
            rStack.push(convertString[n])#就直接压入栈中
        else:
            rStack.push(convertString[n%base])#否则就压入余数
        n = n//base #没压制一次余数之后,都要利用\\运算符,因为此循环总要有个控制结束的语句
    res = ''#创建空字符串
    while not rStack.isEmpty():#当此栈为非空时
        res = res + str(rStack.pop())#依次弹出,这样即利用了栈的性质,因为第一个压入的实际上是结果的最后一位
    return res

#可视化递归 turtle模块
import turtle
myTurtle = turtle.Turtle()
myWin = turtle.Screen()

def drawSpiral(myTurtle,lineLen):
    if lineLen > 0:
        myTurtle.forward(lineLen)
        myTurtle.right(90)
        drawSpiral(myTurtle,lineLen-5)#呈正方形螺旋湮灭,最后不满足条件了就停止

drawSpiral(myTurtle,100)
myWin.exitonclick()

import turtle

def tree(branchLen,t):#到达最深层再退回来,退回来,在执行左边,在退回来
    if branchLen>5:
        t.forward(branchLen)
        t.right(20)
        tree(branchLen-15,t)#执行第一个递归,即重复这个递归之上的语句,直到最底层(或者说最深层)
        t.left(40)
        tree(branchLen-15,t)#
        t.right(20)
        t.backward(branchLen)#从后面的执行过程可以看出,这种递归是先

def main():
    t = turtle.Turtle()
    myWin = turtle.Screen()
    t.left(90)
    t.up()
    t.backward(100)
    t.down()
    t.color('green')
    tree(75,t)
    myWin.exitonclick()
main()

#汉诺塔
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('moving disk from',fp,'to',tp)#虽然具体实现过程还是迷迷糊糊的,但是通过证明此代码完全没问题
moveTower(3,1,2,3)

#迷宫的设计与其蕴含的递归思想
#代码数量繁杂,因此暂时不写

#二分搜索 binary_search递归版本
def binary_search(arr,start,end,hkey):#四个参数 要搜寻的已经排过序的数列,数列的开始index和结束index,要搜寻的目标元素
	if start > end:#如果此数列未经过排序,是不行的
		return -1
	mid = start + (end - start) / 2#如果通过了第一步,那么就找中点的index值
	if arr[mid] > hkey:#如果index mid在数列arr中大于要搜寻的目标,证明在左半区
		return binary_search(arr, start, mid - 1, hkey)#递归调用,将end改为mid-1
	if arr[mid] < hkey:#如果大于,则是右半区
		return binary_search(arr, mid + 1, end, hkey)
	return mid#最后结果输出是mid值?这个二分搜索必须是连续的整数?还是说这段code的本意就是定位此元素所在的位置?

#二分查找的非递归版本
def binarySearch(alist,item):
    first = 0
    last = len(alist)-1
    found = False

    while first<=last and not found:#如果队列没结束而且也没有找到匹配,就执行以下
        midpoint = (first+last)//2
        if alist[midpoint] == item:
            found = True
        else:
            if item < alist[midpoint]:#说明在右半段
                last = midpoint-1#将结束搜索点前移
            else:
                first = midpoint+1#说明在左半段#将开始搜索点后移
    return found

#硬币找零问题(目的是找出所需硬币总数最小的找零方案)
#初级code
def recMC(coinValueList,change):#相当于倒序查找,人脑想先比配最大的,比如25美分,但是程序用1美分开始,减去一美分,看看余下的是不是能被25,10,5整除,不能的话再减去一美分,再试,能的话就减去整除所消耗的结果。其实刚开始我们就要看能不能整除,不能的话在执行减一的迭代操作。
    minCoins = change
    if change in coinValueList:#如果是直接在里面,一个硬币就够了
        return 1
    else :
        for i in [c for c in coinValueList if c<=change]:#如果至少需要的change多于一个,对coinValueList进行遍历
            numCoins = 1 + recMC(coinValueList,change-i)
            if numCoins < minCoins:#如果找零硬币的个数小于要找零的金额
                minCoins = numCoins#先执行迭代,然后检查接下来的语句?迭代难道不是个无底洞吗,直到迭代到最基层,但是最基层的控制语句在哪里体现呢?
    return minCoins

print(recMC[1,5,10,25],63)
#但是上述方法有些繁杂,所以不妨记住一些过去的结果,可以避免重新计算以前的结果:what's that supposed to mean?
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]:
            numCoins = 1 + recDC(coinValueList,change-i,knownResults)
            if numCoins < minCoins:
                minCoins = numCoins
                knownResults[change] = minCoins
    return minCoins#真让人头大,看不懂
#更优的解,采用动态规划算法来解决找零问题:很明显,这个函数没有用到递归,所以递归虽好,有时候还是比不上迭代哦
def dpMakeChange(coinValueList,change,minCoins):#假设change=63
    for cents in range(change+1):#cents的数额从0到63增加
        coinCount = cents
        for j in [c for c in coinValueList if c <=cents]:#当coinValueList中的值小于逐步增长的c时,这个cent=1234的时候只有1满足,cent=56789的时候有1和5满足
            if minCoins[cents-j]+1 < coinCount:#minCoins到底是什么东西?
                coinCount[cents] = minCoins[cents-j]+1
        minCoins[cents] = coinCount
    return minCoins[change]#返回从0到找零值的所有值的解

#双闸控制的while语句,很实用
pos = 0
found = False
while pos < len(alist) and not found:
    if alist == item:
        found = True
    else:
        pos = pos+1
        #以上就是遍历搜索的两个控制条件:第一是搜索完全长,那么停止,要么是找到了所需的项目,那么也停止

#冒泡排序
def bubbleSort(alist):#按从小到大排列
    for passnum in range(len(alist)-1,0,-1):#倒序输入alist长度的range
        for i in range(passnum):#进行第一次遍历,长度为n-1,第二次为n-2...
            if alist[i]>alist[i+1]:#如果前一项大于后一项
                temp=alist[i]
                alist[i]=alist[i+1]
                alist[i+1]=temp #调换顺序
alist=[54,26,93,17,77,31,44,55,20]
bubbleSort(alist)
print(alist)#证明排序之后,改变了原列表顺序

#短冒泡排序
def shortBubbleSort(alist):
    exchanges = True
    passnum = len(alist)-1
    while passnum > 0 and exchanges:
        exchanges = False
        for i in range(passnum):#第一次遍历,的确是n-1次
            if alist[i]>alist[i+1]:#如果有需要交换的,如果这次遍历从头到尾不需要交换,那么exchanges这个变量就保持false状态。这次遍历不需要交换说明这次遍历中遍历的数据都已经排好,再加上后面已经排好的,整个list就完全排好了,直接结束while 语句,输出alist即可。
                exchanges = True
                temp=alist[i]
                alist[i]=alist[i+1]
                alist[i+1]=temp
        passnum = passnum -1
alist = [17, 20, 26, 31, 44, 54, 55, 77, 93]
shortBubbleSort(alist)
print(alist)

#选择排序
def selectionSort(alist):
    for fillslot in range(len(alist)-1,0,-1):
        positionOfMax = 0#每次遍历开始前都要初始化一下
        for location in range(1,fillslot+1):#遍历每一个位置,找到其中最大的,赋值给positionOfMax
            if alist[location]>alist[positionOfMax]:
                positionOfMax = location

        temp = alist[fillslot]
        alist[fillslot] = alist[positionOfMax]
        alist[positionOfMax] = temp#每次遍历完一遍,就把这个最大值所对应的位置的元素跟index=fillslot的元素进行调换(注意,fillslot每遍历一遍都会减一)
alist=[54,26,93,17,77,31,44,55,20]
selectionSort(alist)
print(alist)

#插入排序
def insertSort(alist):
    for index in range(1,len(alist)):#目前要被插入的队列只有最前面的那个元素,即Index=0

        currentValue = alist[index]#依次取index=1,2....的值
        position = index#初始化position至当前index

        while position>0 and alist[position-1]>currentValue:#如果没到开头且前一个值大于当前值,(遍历前面已经排好序的列表,直到到开头或者找到一个合适的位置)
            alist[position]=alist[position-1]#就把前一个值赋值给当前位置
            position = position-1#将当前定位减一,即依次向前移

        alist[position]=currentValue#把这个要插入的值排在前面已经拍好序列的position的位置
alist=[54,26,93,17,77,31,44,55,20]
insertSort(alist)
print(alist)
#希尔排序
def shellSort(alsit):
    sublistcount = len(alist)//2
    while sublistcount > 0 :

        for startposition in range(sublistcount):
            gapInsertionSort(alist,startposition,sublistcount)
        print('After increment of size',sublistcount,'The list is',alist)

        sublistcount = sublistcount//2

def gapInsertionSort(alist,start,gap):
    for i in range(start+gap,len(alist),gap):

        currentvalue = alist[i]
        position = i

        while position>=gap and alist[position-gap]>currentvalue:
            alist[position]=alist[position-gap]
            position = position-gap

        alist[position]=currentvalue

alist=[54,26,93,17,77,31,44,55,20]
shellSort(alist)
print(alist)

#归并排序(Merge Sort Method)
def mergeSort(alist):
    print('Splitting ',alist)
    if len(alist)>1:
        mid = len(alist)//2
        lefthalf = alist[:mid]
        righthalf = alist[mid:]#分裂的过程,分成左右半边

        mergeSort(lefthalf)
        mergeSort(righthalf)#执行递归的操作,将会一直分裂下去,直到成1

        i=0
        j=0
        k=0
        while i <len(lefthalf) and j <len(righthalf):#控制条件是i和j只要有一个遍历完成结束
            if lefthalf[i] < righthalf[j]:#如果左边列第i个小于右边列第J个,就将较小的赋值给合并后排序集合,用完了这个i就i=i+1
                alist[k] = lefthalf[i]
                i = i+1
            else:#右边j元素小,就赋值j,然后用完了这个j,就j=j+1
                alist[k]=righthalf[j]
                j = j+1
            k =k+1

        while i <len(lefthalf):#由于上一个控制语句最后返回要么是i已经最大,或者j已经到达最大,所以这个和下一个控制语句都是做结尾的工作(因为lefthalf和righthalf 都是已经排好序的,所以谁要是被剩下了,就把剩下的元素直接添加到alist结尾即可
            alist[k]=lefthalf[i]
            i=i+1
            k=k+1

        while j<len(righthalf):
            alist[k]=righthalf[j]
            j=j+1
            k=k+1
        print('Merging ',alist)
alist=[54,26,93,17,77,31,44,55,20]
mergeSort(alist)
print(alist)#由最后输出的结果可以很好的得出各语句执行的顺序

#快速排序
def quickSort(alist):#只接受一个参数,但是接下来把要用到的其他二级参数都转化了出来,就不用用户手动输入这些数据了
    quickSortHelper(alist,0,len(alist)-1)

def quickSortHelper(alist,first,last):#这个函数的用意是每次在splitpoint处将alist递归分割
    if first<last:

        splitpoint = partition(alist,first,last)#splitpoint计算过程,这个partition函数返回pivot插入点所在的index,作为分裂点

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

def partition(alist,first,last):
    pivotvalue = alist[first]#由于最开始是零,因此第一个选取的pivot点是第一个元素

    leftmark = first + 1#左指针定在了povit后面
    rightmark = last#last值为len(alist)-1,即最后一个元素的index号

    done = False
    while not done:

        while leftmark<=rightmark and alist[leftmark]<=pivotvalue:#如果左指针对应的这个数小于枢纽数,那么就位置不变,指针向下一位移动(向右移)否则就进行不下去。最终的状态是直到移到不能移,即需要swap的位置
            leftmark = leftmark +1

        while alist[rightmark]>=pivotvalue and rightmark>=leftmark:#如果右指针对应的这个数大于枢纽数,位置不变,指针向下一位移动(向左移)
            rightmark = rightmark-1

        if rightmark<leftmark:#说明全长不需要排列就好了或者经过swap后全长都排列好了
            done = True#结束while not done 循环
        else:#这个是一般情况,就swap一下
            temp = alist[leftmark]
            alist[leftmark] = alist[rightmark]
            alist[rightmark] = temp

    temp = alist[first]#把rightmark对应的元素和pivot置换一下,即把pivot换到合适的位置
    alist[first] = alist[rightmark]
    alist[rightmark] = temp

    return rightmark#每次使用此partition函数都相当于遍历了一遍然后返回rightmark的index
alist=[54,26,93,17,77,31,44,55,20]
quickSort(alist)
print(alist)

#树的结构
myTree = ['a',['b',['d',[],[]],['e',[],[]],['c',['f',[],[]],[]]]]
print(myTree)
print('left subtree = ',myTree[1])
print('root=',myTree[0])
print('right subtree =',myTree[2])

def BinaryTree(r):
    return [r,[],[]]#简单的构造一个具有根节点和两个子列表为空的列表
#插入左子节点
def insertLeft(root,newBranch):
    t= root.pop(1)#树就像是一个栈,弹出root第二个元素,左子节点
    if len(t) > 1:#如果内容长度大于1
        root.insert(1,[newBranch,t,[]])
    else:
        root.insert(1,[newBranch,[],[]])
    return root#注意,要插入一个左子节点,我们首先获得与当前左子节点对应的列表。然后我们添加新的左子树,添加旧的左子树作为新子节点的左子节点
#插入右子节点
def insertRight(root,newBranch):
    t= root.pop(2)#树就像是一个栈,弹出root第三个元素,右子节点
    if len(t) > 1:#如果有内容的话,就插入节点然后再安回来
        root.insert(2,[newBranch,t,[]])
    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]
##################以上是列表表示树的结构###############################

##################以下是节点表示树的结构###############################
class BinaryTree:
    def __init__(self,rootObj):
        self.key = rootObj
        self.leftChild = None
        self.rightChild = None

    def insertLeft(self,newNode):
        if self.leftChild ==None:#插入左子节点,如果左子节点没内容,就直接将新节点赋值
            self.leftChild = BinaryTree(newNode)
        else:#若已经有内容,则插入这个节点并将现有子节点放到树的下一层
            t = BinaryTree(newNode)
            t.leftChild = self.leftChild
            self.leftChild = t

    def insertRight(self,newNode):
        if self.rightChild ==None:#插入左子节点,如果左子节点没内容,就直接将新节点赋值
            self.rightChild = BinaryTree(newNode)
        else:#若已经有内容,则插入这个节点并将现有子节点放到树的下一层
            t = BinaryTree(newNode)
            t.rightChild = self.rightChild
            self.rightChild = t

    def getRightChild(self):
        return self.rightChild

    def getLeftChild(self):
        return self.leftChild

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

    def getRootVal(self):
        return self.key

r = BinaryTree('a')
print(r.getRootVal())
print(r.getLeftChild())

#利用栈和双叉树,创建四则运算分析树
from pythonds.basic.stack import Stack
from pythonds.trees.binaryTree import BinaryTree#导入Stack和BinaryTree模块

def buildParseTree(fpexp):#参数是正常的数学表达式
    fplist = fpexp.split()
    pStack = Stack()
    eTree = BinaryTree('')#创建空的二叉树
    pStack.push(eTree)#把此二叉树推到栈中
    currentTree = eTree
    for i in fplist:#下面是四则运算的实现过程
        if i == '(':
            currentTree.insertLeft('')#插入空的左子节点
            pStack.push(currentTree)#推到栈中
            currentTree = currentTree.getLeftChild()#把指针移到左子节点上
        elif i not in ['+','-','*','/',')']:#如果i是操作数
            currentTree.setRootVal(i)#那么就将这个操作数作为一个object,写入当前节点
            currentTree.insertRight('')#并且插入父节点的右节点
            pStack.push(currentTree)#将完成这些操作 的currentTree压入栈中
            currentTree = currentTree.getRightChild()#指针指到右子节点
        elif i == ')':#如果是此操作符
            currentTree = pStack.pop()#将pstack弹出,返回到current里面,使得当前节点返回父节点
        else:
            raise ValueError
    return eTree#返回etree

pt = buildParseTree('((10+5)*3)')
#pt.postorder()

def evaluate(parseTree):#对树所进行的递归算法基本情况是检查叶节点
    opers = {'+':operator.add,'-':operator.sub,'*':operator.mul,'/':operator.truediv}

    leftC = parseTree.getLeftChild()
    rightC = parseTree.getRightChild()

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

#遍历树结构方法一 前序遍历
def preorder(tree):#(这种方式更好)
    if tree:#基本情况只是检查树是否存在
        print(tree.getRootVal())
        preorder(tree.getLeftChild())
        preorder(tree.getRightChild())
#或者把preorder作为BinaryTree类的一个方法
def preorder(self):
    print(self.key)
    if self.leftChild:
        self.leftChild.preorder()
    if self.rightChild:
        self.rightChild.preorder()
#遍历数结构方法二 后序遍历
def preorder(tree):#(这种方式更好)
    if tree != None:#基本情况只是检查树是否存在
        preorder(tree.getLeftChild())
        preorder(tree.getRightChild())
        print(tree.getRootVal())
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 preorder(tree):#(这种方式更好)
    if tree != None:#基本情况只是检查树是否存在
        preorder(tree.getLeftChild())
        print(tree.getRootVal())
        preorder(tree.getRightChild())
def printexp(tree):#实例,恢复表达式
    sVal = ''
    if tree:
        sVal = '(' + printexp(tree.getLeftChild())#获取左子树
        sVal = sVal + str(tree.getRootVal())#获取根节点
        sVal = sVal + printexp(tree.getRightChild())+')'#再获取右子树
    return sVal

#实现二叉树的最小堆结构
from pythonds.trees.binheap import BinHeap
bh = BinHeap()
bh.insert(5)
bh.insert(7)
bh.insert(3)
bh.insert(11)

print(bh.delMin())
print(bh.delMin())
print(bh.delMin())
print(bh.delMin())

#二叉堆类的定义
class BinHeap:
    def __init__(self):
        self.heapList = [0]#这个零放在这里为了之后简单的整数除法
        self.currentSize = 0

    def percUp(self,i):
        while i//2 >0:#也就是说,i不取0不取1都可以,即向上swap 到最顶层为止
            if self.heapList[i] < self.heapList[i//2]:#子节点小于父节点
                tem = self.heapList[i//2]#将执行swap 操作
                self.heapList[i//2] = self.heapList[i]
                self.heapList[i] = tmp
            i = i//2#子节点大于父节点,就不执行任何操作

    def insert(self,k):
        self.heapList.append(k)
        self.currentSize = self.currentSize + 1
        self.percUp(self.currentSize)#我们写Insert函数的时候当然是先写insert,困难的部分由辅助函数处理,但是运行的时候辅助函数要先表示出来

    def percDown(self,i):#i 是父节点,i*2是其子节点 本函数确保最大的子节点总是沿着树向下移动
        while (i*2)<= self.currentSize:
            mc = self.minChild(i)#minChild返回最小的节点的那个Index
            if self.heapList[i] > self.heapList[mc]:#若父节点大于子节点,那么就交换
                tmp = self.heapList[i]#将执行swap 操作
                self.heapList[i] = self.heapList[mc]
                self.heapList[mc] = tmp
            i = mc
    def minChild(self,i):
        if i*2 +1 >self.currentSize:#如果子节点编号超出了树的大小,说明不存在
            return i*2#返回左节点
        else:
            if self.heapList[i*2]<self.heapList[i*2+1]:#如果左节点小于右节点,返回左节点
                return i*2
            else:#否则返回右节点
                return i*2+1

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

    def buildHeap(self,alist):#从键列表构建一个新的堆
        i = len(alist)//2
        self.currentSize = len(alist)
        self.heapList = [0] + alist[:]
        while (i>0):
            self.percDown(i)
            i = i-1   #都不带返回值的?什么意思?

bh = BinHeap()
bh.buildHeap([9,5,6,2,3])#直接利用添加列表构建一个最小堆二叉树

print(bh.delMin())#先被删除的是2
print(bh.delMin())#再被删除的是3

#二叉搜索树


class TreeNode:
    def __init__(self,key,val,left=None,right=None,parent=None):
        self.key = key
        self.payload =val
        self.leftChild = left
        self.rightChild = right
        self.parent =parent

    def hasLeftChild(self):
        return self.leftChild

    def hasRightChild(self):
        return self.rightChild

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

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

    def isRoot(self):
        return not self.parent#返回boolean

    def isLeaf(self):
        return not (self.rightChild or self.leftChild)#返回boolean

    def hasAnyChildren(self):
        return self.rightChild or self.leftChild

    def hasBothChildren(self):
        return self.rightChild and self.leftChild

    def replaceNodeData(self,key,value,lc,rc):
        self.key = key
        self.payload = value
        self.leftChild = lc
        self.rightChild = rc
        if self.hasLeftChild():
            self.leftChild.parent = self
        if self.hasRightChild():
            self.rightChild.parent = self

class BinarySearchTree:

    def __init__(self):
        self.root = None
        self.size = 0

    def length(self):
        return self.size

    def __len__(self):
        return self.size

    def put(self,key,val):
        if self.root:
            self._put(key,val,self.root)
        else:
            self.root = TreeNode(key,val)
        self.size = self.size + 1

    def _put(self,key,val,currentNode):
        if key<currentNode.key:
            if currentNode.hasLeftChild():
                self._put(key,value,currentNode.leftChild)
            else:
                currentNode.leftChild = TreeNode(key,val,parent=currentNode)
        else:
            if currentNode.hasRightChild():
                self._put(key,val,currentNode.rightChild)
            else:
                currentNode.rightChild = TreeNode(key,val,parent=currentNode)

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

    def get(self,key):
        if self.root:
            res = self._get(key,self.root)
            if res :
                return res.payload
            else:
                return None
        else:
            return None

    def _get(self,key,currentNode):
        if not currentNode:
            return None
        elif currentNode.key ==key:
            return currentNode
        elif key< currentNode.key:
            return self._get(key,currentNode.leftChild)
        else:
            return self._get(key,currentNode.rightChild)



    def __iter__(self):
        return self.root.__iter__()
#################y有时间再补全####################

#邻接表的实现
class Vertex:#Vertex Shell
    def __init__(self,key):
        self.id = key
        self.connectedTo = {}

    def addNeighbor(self,nbr,weight = 0):#添加邻接点和权重
        self.connectedTo[nbr] = weight

    def __str__(self):
        return str(self.id +'connectedTo: ' + str([x.id for x in self.connectedTo]))

    def getConnections(self):#看connect到的另外几个节点是什么
        return self.connectedTo.keys()

    def getId(self):#获取有无此id的key
        return self.id

    def getWeight(self,nbr):#获取目标的权重
        return self.connectedTo[nbr]

class Graph:#graph类实现了添加顶点,获取节点,添加边,in方法,遍历的magic method
    def __init__(self):
        self.vertList = {}
        self.numVertices = 0

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

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

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

    def addEdge(self,f,t,cost=0):
        if f not in self.vertList:
            nv = self.addVertex(f)
        if t not in self.vertList:
            nv = 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())

g = Graph()
for i in range(6):
    g.addVertex(i)
print(g.vertList)

g.addEdge(0,1,5)
g.addEdge(0,5,2)
g.addEdge(1,2,4)
g.addEdge(2,3,9)
g.addEdge(3,4,7)
g.addEdge(3,5,3)
g.addEdge(4,0,1)
g.addEdge(5,4,8)
g.addEdge(5,2,1)
for v in g:
    for w in v.getConnections():
        print('(%s,%s)'%(v.getId(),w.getId()))

#字梯问题 构建图
from pythonds.graphs import Graph

def buildGraph(wordFile):
    d = {}
    g = Graph()
    wfile = open(wordFile,'r')#以读取方式打开文件
    for line in wfile:
        word = line[:-1]
        for i in range(len(word)):#创建buckets of words that differ by one letter
            bucket = word[:i]+'-'+word[i+1:]#对每个位置都创建一个空格
            if bucket in d:
                d[bucket].append(word)#把初始的附在每个桶里
            else:
                d[bucket] = [word]
    for bucket in d.keys():#add vertices and edges for words in the same bucket
        for word1 in d.keys():
            for word2 in d[bucket]:
                if word1 != word2:
                    g.addEdge(word1,word2)
    return g

#现在我们可以把注意力转向最短解
from pythonds.graphs import Graph,Vertex
from pythonds.basic import Queue

def bfs(g,start):
    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')

#骑士之旅 深度优先搜索
from pythonds.graphs import Graph

def knightGraph(bdSize):#在整个板上进行一次遍历
    ktGraph = Graph()
    for row in range(bdSize):
        nodeId = posToNodeId(row,col,bdSize)
        newPositions = genLegalMoves(row,col,bdSize)#为板上的位置创建一个移动列表,所有的移动在图形中转化为边
        for e in newPositions:
            nid = posToNodeId(e[0],e[1],bdSize)
            ktGraph.addEdge(nodeId,nid)
    return ktGraph

def posToNodeId(row,column,board_size):#把位置转化成node ID
    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
    else:#否则不行
        return False

#深度优先搜索算法一 解决骑士问题
from pythonds.graphs import Graph,Vertex
def knightTour(n,path,u,limit):#四个参数,n表示树中的当前深度。path,表示到此为止访问过的顶点的列表 u,图中希望探索的顶点。limit 路径中的节点数  这个函数是递归的
    u.setColor('gray')#未访问的节点是白色的,访问过是灰色的
    path.append(u)
    if n < limit:
        nbrList = list(u.getConnections())
        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:
            path.pop()
            u.setColor('white')
    else:#如果返回一个正好包含所有节点的路径,就说明成功了
        done = True
    return done

#最短加权路径的Dijkstra算法
from pythonds.graphs import PriorityQueue, Graph, Vertex
def dijkstra(aGraph, start):#参数是图 和 给定的开始节点
    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)#使用优先级队列类中的decreaseKey方法

#贪心算法のprim算法:
from pythonds.graphs import PriorityQueue, Graph, Vertex

def prim(G,start):
    pq = PriorityQueue()
    for v in G:
        v.setDistance(sys.maxsize)
        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)
            if nextVert in pq and newCost<nextVert.getDistance():
                nextVert.setPred(currentVert)
                nextVert.setDistance(newCost)
                pq.decreaseKey(nextVert,newCost)