有关编程之美的试题python实现

class BSTreeNode:
    def __init__(self,value=None,left=None,right=None):
        self.value=value
        self.left=left
        self.right=right

def ConnectTreeNodes(pNodeA1, pNodeA2, pNodeA3):
    pNodeA1.left=pNodeA2
    pNodeA1.right=pNodeA3

########################################################################
##                    编程之美1.1让cpu曲线听你指挥                       ##
########################################################################
## 解法一:让cpu跑idle和busy两个不同的循环,控制时间比例,busy可用空循环,idle可
##        用sleep()
import time
def algorithm1():
    while True:
        #调整适当的n,运行时间也是0.01秒,这个n不对
        for i in range(9600000):
            pass
        time.sleep(0.01)

def algorithm2():
    busytime=0.02
    idletime=0.01
    while True:
        startTime=time.time()
        while time.time()-startTime<busytime:
            print(time.time()-startTime)
        time.sleep(idletime)

import math
def algorithm3():
    count=200#抽样频率
    busy=[0]*count
    idle=[0]*count
    for i in range(count):
        busy[i]=(1+math.sin(math.pi*i*2/count))/count#0~2之间-》0~0.01之间
        idle[i]=0.01-busy[i]
    j=0
    while True:
        startTime=time.time()
        j=j%200
        while time.time()-startTime<busy[j]:
            print(time.time()-startTime)
        time.sleep(idle[j])
        j=j+1

def test1_1():
    #algorithm1()
    #algorithm2()
    algorithm3()

########################################################################
##            编程之美1.2输出将帅的所有位置(只能用一个变量)                 ##
########################################################################
def getPosition():
    #假设A,B的位置为0~9,把B放在10位上
    #1 2 3
    #4 5 6
    #7 8 9
    for i in range(11,100):
        if (i%10==0) or i%10%3==(i//10)%3:
            continue
        print('B:{},A:{}'.format(i%10,i//10))

def test1_2():
    getPosition()

########################################################################
##                    编程之美1.3 翻转烙饼,使其有序                      ##
########################################################################
# 注意这里不是一般的排序问题,max的位置你是知道的,只能用'翻转'操作
# 考虑这样一种算法,把最大的那个烙饼翻到最下面需要2次操作,完成之后,对上面的n-1的烙饼
#                重复这一算法,这样最多只要2(n-1)次操作就可以完成


class CPrefixSorting:

    def __init__(self,pCakeArray,nCakeCnt):
        self.m_nCakeCnt=nCakeCnt#烙饼个数
        self.m_nMaxSwap=self.UpBound(nCakeCnt);#最多交换次数。根据前面的推断,这里最多为m_nCakeCnt * 2
        self.m_CakeArray=list(pCakeArray)#烙饼信息数组
        self.m_SwapArray=[0]*self.m_nMaxSwap#交换结果数组
        self.m_ReverseCakeArray=list(pCakeArray)#当前翻转烙饼信息数组
        self.m_ReverseCakeArraySwap=[0]*self.m_nMaxSwap#当前翻转烙饼交换结果数组
        self.m_nSearch=0#当前搜索次数信息


     # 计算烙饼翻转信息
     # @param
     # pCakeArray    存储烙饼索引数组
     # nCakeCnt    烙饼个数
     #
    def Run(self):
        self.Search(0)

     # 输出烙饼具体翻转的次数
    def Output(self):
        print(self.m_ReverseCakeArray)
        print(self.m_ReverseCakeArraySwap[0:self.m_nMaxSwap])
        print("\n |Search Times| : %d\n"%self.m_nSearch);
        print("Total Swap times = %d\n"%self.m_nMaxSwap);


    # 寻找当前翻转的上界
    def UpBound(self,nCakeCnt):
         return nCakeCnt*2

    # 寻找当前翻转的下界
    def LowerBound(self,pCakeArray,nCakeCnt):
        ret = 0
        # 根据当前数组的排序信息情况来判断最少需要交换多少次
        for i in range(1,nCakeCnt):
               # 判断位置相邻的两个烙饼,是否为尺寸排序上相邻的
            t = pCakeArray[i] - pCakeArray[i-1];
            if (t == 1) or (t == -1):
                pass
            else:
                ret+=1
        return ret


     # 排序的主函数
    def Search(self,step):
        #print('step:%d'%step)
        self.m_nSearch+=1

        # 估算这次搜索所需要的最小交换次数
        nEstimate = self.LowerBound(self.m_ReverseCakeArray,self.m_nCakeCnt)
        #print('lower bound:%d'%nEstimate)
        if (step + nEstimate >= self.m_nMaxSwap):
            return

        # 如果已经排好序,即翻转完成,输出结果
        if self.IsSorted(self.m_ReverseCakeArray,self.m_nCakeCnt):
            print('find min step:%d'%step)
            self.m_nMaxSwap = step;
            self.Output()
            return


        # 递归进行翻转
        # 这里是一个遍历的枚举算法,枚举出每个可能的交换方式,然后剪枝
        for i in range(1,self.m_nCakeCnt):
            #print('change:%d'%i)
            #依次交换
            self.Revert(0, i)
            self.m_ReverseCakeArraySwap[step] = i
            #递归的操作子序列
            self.Search(step + 1)
            #注意完成之后要还原,继续搜索
            self.Revert(0, i)

     # true : 已经排好序
     # false : 未排序
    def IsSorted(self,pCakeArray,nCakeCnt):
        for i in range(1,nCakeCnt):
            if(pCakeArray[i-1] > pCakeArray[i]):
               return False
        return True


    # 翻转烙饼信息
    def Revert(self, nBegin,nEnd):
        if nBegin>=nEnd:
            return
        self.m_ReverseCakeArray[nBegin:nEnd+1]=reversed(self.m_ReverseCakeArray[nBegin:nEnd+1])
        #print(self.m_ReverseCakeArray)

def test1_3():
    a=CPrefixSorting([3,2,1,6,5,4,9,8,7,0],10)
    a.Run()




########################################################################
##                    编程之美1.4 买书问题                               ##
########################################################################
# 共5种,每本8块,买2本不同的书折扣5%,3本10%,4本20%,5本25%,
# 同一种折扣规则只能使用一次:也即:
# 如果买了2本1,一本2,那么最多只能对其中一本1和一本2使用5%折扣,另一本1就不能折扣了
# 这里贪心策略行不通
# 算法一 动态规划,缩小为较小的子问题
Recount=[1,0.95,0.9,0.8,0.75]#折扣
def buyBook(inputList):
    #如果有一个<0
    sortedInputList=sorted(inputList,reverse=True)
    if sortedInputList[0]<0:
        return 99999
    #如果有0或者1
    #买得最多的那本数量
    maxBookCount=sortedInputList[0]
    if maxBookCount==0:
        return 0
    if maxBookCount==1:
        #print('buy:',end='')
        #print(sortedInputList)
        count=sortedInputList.count(1)
        return count*8*Recount[count-1]
    a,b,c,d,e=sortedInputList
    return min([buyBook([1,0,0,0,0])+buyBook([a-1,b,c,d,e]),
                buyBook([1,1,0,0,0])+buyBook([a-1,b-1,c,d,e]),
                buyBook([1,1,1,0,0])+buyBook([a-1,b-1,c-1,d,e]),
                buyBook([1,1,1,1,0])+buyBook([a-1,b-1,c-1,d-1,e]),
                buyBook([1,1,1,1,1])+buyBook([a-1,b-1,c-1,d-1,e-1])])

def test1_4():
    print(buyBook([2,2,2,1,1]))


########################################################################
##                    编程之美1.5 快速找出故障机器                        ##
########################################################################
# 如果仅有一台机器出了故障,可以使用找异或值的方法,只需要存一个变量
def findOneBroken(alist):
    result=0
    for i in range(len(alist)):
        result=alist[i]^result
    return result
# 如果两台机器出了故障且是不同的
def findTwoBrokenDiff(alist):
    tempresut=findOneBroken(alist)
    numer=findnumber(tempresut)
    result1=0
    result2=0
    #分成两类,这个位数上是一的和不是一的
    for i in range(len(alist)):
        if alist[i]&numer==0:
            result1=alist[i]^result1
        else:
            result2=alist[i]^result2
    return result1,result2

# 一个二进制数最低的一个为1的位数,并且提取出来
def findnumber(i):
    k=0
    endNumer=i&1
    while endNumer!=1:
        k+=1
        endNumer=(i>>1&1)
    return 1<<k

from functools import reduce
#使用'不变量的方法求解',总的id和减去剩下的就是坏掉的id和x+y=a
#如果两个id不同还需要一个方程可以用x*y=b
def findbrokenWithEquation(origin,now):
    originSum=sum(origin)
    nowSum=sum(now)
    originMul=reduce(lambda x,y:x*y,origin)
    nowMul=reduce(lambda x,y:x*y,now)
    a=originSum-nowSum
    b=originMul/nowMul
    return (a+math.sqrt(a**2-4*b))/2,abs(a-math.sqrt(a**2-4*b))/2


def test1_5():
    print(findOneBroken([1,1,2,2,3,4,4,5,5,6,6]))
    print(findTwoBrokenDiff([1,1,2,2,3,4,5,5,6,6]))
    print(findbrokenWithEquation([1,1,2,2,3,3,4,4,5,5,6,6],[1,1,2,2,3,4,5,5,6,6]))
    print(findbrokenWithEquation([1,1,2,2,3,3,4,4,5,5,6,6],[1,1,2,2,4,4,5,5,6,6]))
    print(findnumber(6))



########################################################################
##                    编程之美1.6 饮料供货                             ##
########################################################################
# 饮料的总容量有上限V,求一种组合使得总满意度最大
# 结果第k种饮料的数量*满意度+减去第k种饮料的最优结果
#  使用动态规划,并且可以纪录子问题的解


# 子问题的记录项表,假设从i到T种饮料中,
                                # 找出容量总和为V'的一个方案,满意度最多能够达到
                               # opt(V',i,T-1),存储于opt[V'][i],
                               # 初始化时opt中存储值为-1,表示该子问题尚未求解

opt=[]
def Cal(V,type,C,H,T):
    global ret
    print(ret)
    #最多T种饮料
    if type == T:
        if V==0:
            return 0
        else:
            return -9999

    if V < 0:
        return -9999
    elif(V == 0):
          return 0
    elif(opt[V][type] != -1):
          return opt[V][type]        # 该子问题已求解,则直接返回子问题的解;
                                     # 子问题尚未求解,则求解该子问题
    temp=0
    for i in range(C[type]):
        #去掉一种饮料算子问题的最优解
        #去掉的方法有0~C[type](这种饮料的最大值)种,取这几种结果中最大的
        temp = Cal(V-i*C[type],type-1)
        print(temp)
        if(temp != -9999):
            temp += H[type] * i
            if(temp > ret):
                ret = temp
    opt[V][type]=ret
    return ret


def memoize(f):
    memo = {}
    def helper(a,b,c,d,e):
        if (a,b) not in memo:
            memo[(a,b)] = f(a,b,c,d,e)
        return memo[(a,b)]
    return helper

def test1_6():
    V=10#总容量
    T=10#T种饮料
    type=T
    ret=0
    opt=[[-1]*(V+1)]*(T+1)
    C=[100]*T
    H=[100]*T
    m_Cal=memoize(Cal)
    print(m_Cal(1000,T,C,H,T))


########################################################################
##                    编程之美1.7 光影切割问题                          ##
########################################################################
# 此算法的核心是先寻找分块数和交点数的关系式,然后可以发现交点数就是和边界交点次序的
# 逆序数,从而问题转发为逆序数问题

#寻找逆序数
#逆序数是归并排序的副产品
num = 0
def merge_sort(data):
    if (len(data)<=1):
        return data
    index  =  len(data)//2
    lst1 = data[:index]
    lst2 = data[index:]
    left = merge_sort(lst1)
    right = merge_sort(lst2)
    return merge(left, right)

#这里merge算法不是很高效,待改
def merge(lst1, lst2):
    """to Merge two list together"""
    list = []
    while(len(lst1)>0 and len(lst2)>0):
        data1 = lst1[0]
        data2 = lst2[0]
        if (data1<=data2):
            list.append(lst1.pop(0))
        else:
            global num
            num = num + len(lst1)
            list.append(lst2.pop(0))
    if(len(lst1)>0):
        list.extend(lst1)
    else:
        list.extend(lst2)
    return list

def test1_7():
    merge_sort([4,3,2,1])
    print(num)


########################################################################
##                    编程之美1.8 电梯算法                             ##
########################################################################
# 电梯只停一层楼,停在哪里乘客要爬楼梯最少
# 此题的关键在于寻找delta函数,如果停在i层楼,要爬总数是Y层,有N1,N2,N3分别指实际目标
# 在i层之下,i层,i层之上,那么停在i+1层要爬Y+N1+N2-N3,i-1层:Y-N1+N2+N3,可以看出
# 当N1+N2-N3<0的时候应改i+1,N2+N3-N1<0->i-1;=0,i不变,因此只要找到这样的i,使得
# N1+N2-N3>=0;N2+N3-N1>=0


def findFloor(alist):
    for i in range(len(alist)):
        N1=sum(alist[:i])
        N2=alist[i]
        N3=sum(alist[i+1:])
        if N1+N2-N3>=0 and N2+N3-N1>=0:
            return i

def test1_8():
    print(findFloor([4,0,3,2,1,0,4,0,8]))


########################################################################
##                 编程之美1.9 高效率地安排见面会                        ##
########################################################################
# 如四个见面会起止时间分别为A[1,5],B[2,3],C[3,4],D[3,6]求最少需要安排多少面试地点
# (多少人来面试?)
# 最优的算法是把开始时间结束时间都排序,遍历时遇到一个B(开始时间)m+1;遇到一个E:m-1
# 这样m遍历过程中的最大值就是解
def findMinPlace(list):
    total=[]
    for item in list:
        total.append((item[0],'B'))
        total.append((item[1],'E'))
    total=sorted(total,key=lambda x:x[0])
    print(total)
    m=0
    maxm=0
    for i in range(len(total)):
        if total[i][1]=='B':
            m+=1
            if m>maxm:
                maxm=m
        else:
            m-=1

    return maxm





#如果是一对于的问题,即n个学生对m个项目中的一个或者几个感兴趣,怎样安排可以使得项目介绍 的总时间最短
# 这个是典型的图的着色问题,对于图使用最少的颜色使得对于任何一个边,边的两个顶点颜色都不一样
# 两个典型的解法:1.回溯法 2.贪心法
# 1.回溯法,输入n个点,m个颜色,m从低到高看有没有解,可以找到最小的解m

def ColorOk(k,G,color): #判断顶 点k的着⾊色是否发⽣生冲突
    for i in range(k):
        if G[k][i]==1 and color[i]==color[k]:
            return False
    return True


def GraphColor(n,m,G):
    color=[0]*n
    k=0
    while k>=0:
        color[k]=color[k]+1
        while color[k]<=m:
            if ColorOk(k,G,color):
                break
            else:
                color[k]=color[k]+1#搜索下一个颜⾊
        if color[k]<=m and k==n-1: #求解完毕,输出解
            print(color)
            return
        elif(color[k]<=m and k<n-1):
            k=k+1 #处理下⼀一个顶点
        else:
            color[k]=0;
            k=k-1; #回溯



def ColorOk2(i,k,n,G,color): #判断顶 点k的着⾊色是否发⽣生冲突
    for j in range(n):
        if G[i][j]==1 and color[j]==k:
            return False
    return True

#贪心法
def GraphColor2(n,G):
    color=[0]*n
    color[0]=1
    k=1
    while color.count(0)!=0:
        for i in range(n):
            if color[i]==0 and ColorOk2(i,k,n,G,color):
                color[i]=k
        k+=1
    print(k,color)


def test1_9():
    print(findMinPlace([[1,5],[2,3],[3,4],[3,6]]))




    G=[[1,1,1,0,0],
       [1,1,1,1,1],
       [1,1,1,0,1],
       [0,1,0,1,1],
       [0,1,1,1,1]]
    GraphColor(5,3,G)#m最小是3的时候有解,说明m最小可以是3
    GraphColor2(5,G)#找到的不是最优解



########################################################################
##                        编程之美1.11-1.13 取石头                         ##
########################################################################
#1.11
#每次取一个或者两个相邻的,求必胜策略
#取中间的一个(奇数),或中间两个(偶数),这样对手取任何石头,取他对称的石头即可

#1.12 A分堆,B先取
#关键是找出安全局面:(1,1)是安全局面,先取的输
#                =>(1,x) x>1不是安全局面,因为对手可以一步转化为自己的安全局面
#                =>(m,m) m>1 是安全局面 因为对手怎么取,都不能一步变成安全局面,循环不变式
# 所以N为偶数的时候,分成两堆相同的,可以保证自己赢
# N是奇数的时候,(1,1,1)是非安全局面
#              (1,1,x)是非安全局面
#              (1,2,x)是安全局面
#              (1,x,x)是非安全局面对手取1,(x,x)安全
#              (1,x,y)x!=y 是非安全局面,因为对手可以转换为(1,2,x)
#


#1.13 有两堆石头,可以从两堆取等数量,或者其中一堆取任意

########################################################################
##                        编程之美1.14 连连看                         ##
########################################################################
# 算法的核心是广度优先搜索,对于A一步可以到达的领域集合中如果有有A',那么A,A'距离为0转折
#  对于A一步可以到达的空格子的领域集合中如果有A',那么距离为1转折``````````



########################################################################
##                        编程之美1.15 数独                          ##
########################################################################
# 见C++程序

########################################################################
##                        编程之美1.16 24点                          ##
########################################################################
#递归算法
# 见C++程序


########################################################################
##                   编程之美2.1 求二进制数字中1的个数                    ##
########################################################################

#1.求余数的方法
def findOnes(number):
    sum_one=0
    while number:
        sum_one+=number%2
        number=number//2
    return sum_one

#2.位操作方法,思路同上,只是表述不一样
def findOnes2(number):
    sum_one=0
    while number:
        sum_one+=number&0x1
        number=number>>1
    return sum_one

#3.位操作,基于这样的常用方法:使得一个二进制数a最低的1变成0方法是;a=a&(a-1)
def findOnes3(number):
    sum_one=0
    while number:
        number=number&(number-1)
        print(number)
        sum_one+=1
    return sum_one

#4. 查表法,既然是8位二进制数,直接把256个结果存在一个字典里就可以了
# 算法略


#5 算法5 用1,不断移动来看每一位上是不是1
def findOnes5(number):
    k=1
    sum_one=0
    for i in range(1,32):
        if number&k:
            sum_one+=1
        k=k<<1
    return sum_one

def test2_1():
    print(findOnes3(7))
    print(findOnes5(7))

########################################################################
##                       编程之美2.2 不要被阶乘吓倒                      ##
########################################################################
#1.求N!二进制表示中末尾0的个数
# 分析后发现,只要求5的指数次数即可
def endZeros(numberN):
    ret=0
    while numberN:
        ret+=numberN//5
        numberN=numberN//5
    return ret

#由于python不会溢出,可以用这个算法验证结果
def endZeros2(numberN):
    ret=0
    resultN=reduce(lambda x,y:x*y,range(1,numberN+1))
    print(resultN)
    while resultN%10==0:
        ret+=1
        resultN=resultN//10
    return ret

#2.求N!二进制表示中最低位1的位置
# 首先看一个二进制数,最低的1的位置就是可以被2整除的次数+1,(不能整除说明最后一位是1了)
# 这样就转化成求N!中质因数2的个数,即N/2+N/4+```
def findLowestOne(numberN):
    ret=0
    while numberN:
        numberN=numberN>>1
        ret+=numberN
    return ret


#由于python不会溢出,可以用这个算法验证结果
def findLowestOne2(numberN):
    ret=0
    resultN=reduce(lambda x,y:x*y,range(1,numberN+1))
    while resultN%2==0:
        ret+=1
        resultN=resultN>>1
    return ret

def test2_2():
    print(endZeros(100))
    print(endZeros2(100))
    print(findLowestOne(100))
    print(findLowestOne2(100))

########################################################################
##                       编程之美2.3 寻找水王                           ##
########################################################################
# 水王发帖的数量大于N/2找到他
# 思路核心是大于N/2,那么删除掉两个不一样的,剩下的还是大于N/2,
# 如果给他计数那么,不一样的-1,一样的+1,那么水王的id一定能>1

def findMostCommon(list):
    tempResult=0
    countOfTempResult=0
    for item in list:
        if countOfTempResult==0:
            tempResult=item
        if item==tempResult:
            countOfTempResult+=1
        else:
            countOfTempResult-=1
    return tempResult

#算法二 用一个hash表保存频数
#算法三 既然>N/2,那么如果找出数组第N/2 大的数,那么这个数就是频数大于N/2的数
#所以算法就转换成了O(N)算法的寻找第k大数


import  random
def Partition(alist,start,end):
    index=random.randint(start,end)
    alist[index],alist[end]=alist[end],alist[index]
    small=start-1
    index=start
    while index<end:
        if alist[index]<alist[end]:
            small+=1
            if small!=index:
                alist[small],alist[index]=alist[index],alist[small]
        index+=1
    small+=1
    alist[small],alist[end]=alist[end],alist[small]
    return small

def findKLargest(alist,begin,end,k):
    mid=Partition(alist,begin,end)
    if mid==k:
        return alist[k]
    elif mid>k:
        return findKLargest(alist,begin,mid-1,k)
    else:
        return findKLargest(alist,mid+1,end,k-mid)


def test2_3():
    print(findMostCommon([1, 2, 3, 4, 2, 5, 2, 2, 3, 2, 5, 2]))
    print(findKLargest([1, 2, 3, 4, 2, 5, 2, 2, 3, 2, 5, 2],0,11,6))


########################################################################
##                       编程之美2.4  1的数目                           ##
########################################################################
# 找出1.....N中N个数字中1的数目:如1,2,3,4,5,6,7,8,9,10,11,12 中有5个1

# 算法1,直接遍历求解,复杂度为O(N*logN)
def findOnesToN(numberN):
    def findOnesOfN(numberN):
        ret=0
        while numberN:
            ret+=(numberN%10==1)
            numberN=numberN//10
        # print(ret)
        return ret
    ret=0
    for i in range(1,numberN+1):
        ret+=findOnesOfN(i)
    return ret


# 算法2,1的个数为个位1+十位1+...
def findOnesToN2(n):
    iCount = 0
    iFactor = 1

    iLowerNum = 0
    iCurrNum = 0
    iHigherNum = 0

    while n // iFactor:
        #低位,当前位,高位
        iLowerNum = n - (n //iFactor) * iFactor
        iCurrNum = (n // iFactor) % 10
        iHigherNum = n // (iFactor * 10)

        if iCurrNum==0:
            iCount += iHigherNum * iFactor
        elif iCurrNum== 1:
            iCount += iHigherNum * iFactor + iLowerNum + 1
        else:
            iCount += (iHigherNum + 1) * iFactor

        iFactor *= 10

    return iCount



def test2_4():
    print(findOnesToN(12))
    print(findOnesToN2(99))


########################################################################
##                    编程之美2.5 数组中k个最大的数                      ##
########################################################################
#算法一 排序
def findKMax(list,k):
    return sorted(list)[-k:]

import random
#算法二 递归算法
#递归的分成两组,Sa,Sb其中Sa中的元素都小于Sb
def patition(list):
    a=list[len(list)//2]
    Sa=[]
    Sb=[]
    for item in list:
        if item>=a:
            Sa.append(item)
        else:
            Sb.append(item)
    return (Sa,Sb)

def findKMax2(list,k):
    if k<=0:
        return []
    if len(list)<=k:
        return list
    Sa,Sb=patition(list)

    a=findKMax2(Sa,k)
    b=findKMax2(Sb,k-len(Sa))
    # print(a,b)
    return a+b


#算法三 使用一个长度为k的最小堆作为数据结构保存最大的k个数
# 保持堆性质,对于位置i
def min_heapify(list,i):
    K=len(list)
    p=i#父亲节点
    while i < K:
        # 左儿子
        q = 2 * p + 1
        if q >= K:
            break
        # 如果有右儿子而且有儿子更小
        if q < K-1  and  list[q + 1] < list[q]:
            q = q + 1;
        if list[q]< list[p]:#违反堆性质
            list[p],list[q] = list[q],list[p]#交换
            p = q
        else:
            break

def build_Heap(list):
    i=len(list)//2
    while i>=0:
        min_heapify(list,i)
        i-=1
    return list

def findKMax3(list,k):
    heap=list[:k]
    heap=build_Heap(heap)
    for item in list[k:]:
        if item<=heap[0]:
            break
        else:
            heap[0]=item
            min_heapify(heap,0)
    return heap

#算法四 桶排序,计数排序的思想
def findKMax4(list,k):
    numCount=[0]*len(list)
    for item in list:
        numCount[item]+=1
    result=[]
    sumCount=0
    i=len(numCount)-1
    while sumCount<=k:
        sumCount+=numCount[i]
        result.extend([i]*numCount[i])
        i-=1
    return result


def test2_5():
    alist=list(range(20000))
    # print(findKMax(alist,10))
    # print(findKMax2(alist,10))
    # print(build_Heap([23,2,12,2,43,5,3,6,7]))
    print(findKMax3(alist,20))
    print(findKMax4(alist,20))


########################################################################
##                    编程之美2.6 精确表示浮点数                        ##
########################################################################
# 得出推导公式 10^m*Y-Y=b1b2..bm


########################################################################
##                    编程之美2.7 最大公约数问题                        ##
########################################################################
# 基本解法:辗转相除法
def gcd(a,b):
    if b==0:
        return a
    else:
        return gcd(b,a%b)


# 解法2:大整数取模消耗较大,考虑到x>y,的时候 f(x,y)=f(x-y,y)这样可以化成小整数求解
# 但是缺点是迭代次数变多了
def bigIntGcd(a,b):
    if a<b:
        return bigIntGcd(b,a)
    if b==0:
        return a
    else:
        return bigIntGcd(a-b,b)



# 算法3:对偶数除以2,减小运算次数
def isEven(n):
    return n&1==0


def bigIntGcd2(a,b):
    if a<b:
        return bigIntGcd(b,a)
    if b==0:
        return a
    else:
        if isEven(a):
            if isEven(b):
                return bigIntGcd2(a>>1,b>>1)<<1
            else:
                return bigIntGcd2(a>>1,b)
        else:
            if isEven(b):
                return bigIntGcd2(a,b>>1)
            else:
                return bigIntGcd2(a-b,b)



def test2_7():
    print(gcd(36,27))
    # print(bigIntGcd(131111811222,2131112))
    print(bigIntGcd2(131111811222,2131112))


########################################################################
##                    编程之美2.8 找符合条件的整数                       ##
########################################################################
# 给定一个正整数N,求最小正整数M使得N*M十进制表示中只有0或1
# 遍历X

import itertools
def findM(n):
# 算法会重复遍历,待改进
    i=0x1
    while i<99999999999:
        b=int('{0:b}'.format(i))
        if b%n==0:
            return b,b//n
        i+=1

def test2_8():
    print(findM(99))


########################################################################
##               编程之美2.10 寻找数组中的最大最小值                     ##
########################################################################
# 1.如果看成独立的两个问题,至少需要2N次比较
# 2.对数组中的元素两两比较,每两个数中,大的都放在奇数位小的放在偶数位,这样需要N/2+N次比较
#   这种算法不一定要改变原数组,遍历的时候得到两位中的大的和小的,把小的和min,大的和max比较即可
# 3.分治算法,也需要3N/2次比较


########################################################################
##                  编程之美2.11 寻找最近的点对                         ##
########################################################################
# 典型的分治算法的题目
# 如果事数组,可以用桶排序,只要O(N)时间(放入桶中-N,扫瞄一遍桶-N)


########################################################################
##                  编程之美2.12 快速寻找满足条件的两个数                 ##
########################################################################
# 找出一个数组中的两个数字a,b,使得其和为c
# 先排序的两个算法:注意这里只是找一个解,如果要找出所有解,可以用算法2或者算法3变化一下
# 算法1

def findSumC(list,c):
    list=sorted(list)
    sumab=0
    for i in range(len(list)-1):
        a,b=list[i],list[i+1]
        sumab=a+b
        if sumab==c:
            return a,b
        elif sumab>c:
            break

# 算法2 首尾变化,这里已经改成了找所有解
def findSumC2(list,c):
    list=sorted(list)
    a=0
    b=len(list)-1
    resut=[]
    while a<b:
        sumab=list[a]+list[b]
        if sumab==c:
            resut.append((list[a],list[b]))
            a+=1
        elif sumab<c:
            a+=1
        else:
            b-=1


    return resut


# 算法3 使用哈希表
def findSumC3(list,c):
    adict={}
    result=[]
    for item in list:
        adict[item]=1
    # print(adict)
    for item in list:
        #?
        if c-item<item:
            break
        if c-item in adict:
            result.append((item,c-item))
    return result

def test2_12():
    print(findSumC(list(range(100)),25))
    print(findSumC2(list(range(100)),25))
    print(findSumC3(list(range(100)),25))


########################################################################
##                  编程之美2.13 求字数组的最大乘积                    ##
########################################################################
# N维数组,找一个字数组N-1维,使得数组乘积最大,只能用乘法
# 算法一,从低到高,排除那个i,计算剩下的乘积

def findMaxN_1(list):
    result=0
    for i in range(len(list)):
        temp=reduce(lambda x,y:x*y,list[:i]+list[i+1:])
        if temp>result:
            result=temp

    return result

# 算法2 计算整个乘积P,P=0;>0;<0三种情况
def findMaxN_2(list):
    temp=reduce(lambda x,y:x*y,list)
    if temp==0:
        i=list.index(0)
        temp=reduce(lambda x,y:x*y,list[:i]+list[i+1:])
        if temp<0:
            temp=0
    elif temp>0:
        i=list.index(min([item for item in list if item>0]))
        temp=reduce(lambda x,y:x*y,list[:i]+list[i+1:])
    else:
        i=i=list.index(max([item for item in list if item<0]))
        temp=reduce(lambda x,y:x*y,list[:i]+list[i+1:])

    return  temp

def test2_13():
    print(findMaxN_1([12,3,2,4,345,32,12]))
    print(findMaxN_2([12,-3,0,4,345,32,12]))


########################################################################
##                  编程之美2.14 求子数组之和的最大值                    ##
########################################################################
# 这个题都烂了,不写了,不会的面壁


########################################################################
##                  编程之美2.15 求子数组之和的最大值(二维)                ##
########################################################################
# 思路是存储部分和,把解转化为部分和(根据右上角的位置,算其和整个图的左下角组成的图形面积)的函数


########################################################################
##                  编程之美2.16 数组中的最长递增子序列                  ##
########################################################################
'待补+逆序数'











########################################################################
##                      编程之美2.17 数组循环移位                       ##
########################################################################
# [a,b,c,d,e]=>[e,a,b,c,d]
# [abcd1234]算法的核心是:交换abcd,1234的位置,如何交换呢,可以用三次逆序实现:
#  1.abcd 逆 [dcba1234]
#  2.1234 逆 [dcba4321]
#  3.整个  逆 [1234abcd]  完成


#注意这个是演示算法,实际reverse可以只用一个变量\
def leftMoveN(alist,N):
    N=N%len(alist)
    alist[:N]=list(reversed(alist[:N]))
    alist[N:]=list(reversed(alist[N:]))
    # print(alist)
    return list(reversed(alist))

def test2_17():
    print(leftMoveN([12,3,2,4,345,32,12],1))

########################################################################
##                      编程之美2.18 数组分割                           ##
########################################################################
#2n个元素的数组,找到两个子数组,n维使得其和尽量相等
#如果数组的总和是sum,那么找到一个最接近sum/2即可,不妨设<=sum/2
#使用动态规划的思想,若F[i][j][k]表示从i个元素中取j个使得其尽量接近k,那么
# F[i][j][k]=max(F[i-1][j][k],F[i-1][j-1][k-A[j]]+A[j])
#                 取了第i个元素,没取第i个元素

'这道题是背包问题,待补充背包问题的算法介绍'
def findTwoN(list,n):
    sumList=sum(list)
    print(sumList)
    F=[[[0]*(sumList//2+1)]*(n+1)]*(2*n+1)
    Path=F.copy()
    # print(F)
    nLimit=n
    for i in range(1,2*n+1):
        nLimit==min(i,n)
        for j in range(1,nLimit+1):
            for k in range(1,sumList//2+1):
                # print(i,j,k)
                F[i][j][k]=F[i-1][j][k]
                if(k>=list[i-1] and F[i][j][k]<F[i-1][j-1][k-list[i-1]]+list[i-1]):
                    F[i][j][k]=F[i-1][j-1][k-list[i-1]]+list[i-1]
                    Path[i][j][k]=1

    return F[2*n][n][sumList//2]


#可以F[i]始终与F[i-1]有关,可降维
def findTwoN2(list,n):
    sumList=sum(list)
    print(sumList)
    F=[[0]*(sumList//2+1)]*(n+1)
    # print(F)
    nLimit=n
    for i in range(1,2*n+1):
        nLimit==min(i,n)
        for j in range(1,nLimit+1):
            for k in range(1,sumList//2+1):
                # print(i,j,k)

                if(k>=list[i-1] and F[j][k]<F[j-1][k-list[i-1]]+list[i-1]):
                    F[j][k]=F[j-1][k-list[i-1]]+list[i-1]


    return F[n][sumList//2]

def test2_18():
    print(findTwoN([12,3,2,4,34,32,12,5],4))
    print(findTwoN2([12,3,2,4,34,32,12,5],4))

########################################################################
##                      编程之美3.1 字符串移位的包含问题                  ##
########################################################################
# 字符串'ABCD'循环移动之后包含'DAB'
# 此题的核心是'ABCD'=>'ABCDABCD'其中的3字符字串就是可以得到的所有3字符字串

#类似题目,移动字符串ABCD12向右移动两位,12ABCD,但是最多使用两个变量空间
# 一个做法同上拼接后切割,但是这样需要多余的空间,不符合要求,这种做法实际就是字符串复制
# 算法1:  算法 2.17 用三次旋转实现,线性时间,但是只使用一个旋转用的变量

#算法2:双指针移动法:对于一个字符串或者数组123456789,要向左移动m位,那么可以用两个指针
# p1=0;p2=p1+m;p1,p2一边向前移动一边交换p1,p2,对于末尾的递归实现上述步骤可以实现移动
# 如1234,m=2,=>3214=>3412 完成。12345,m=2 32145=》34125=》34521=》34512

#stl里的rotate算法,用到了gcd的原理
def leftRotate(astr,n,m,head,tail):

    #n 待处理部分的字符串长度,m:待处理部分的旋转长度
    #head:待处理部分的头指针,tail:待处理部分的尾指针

    # 返回条件
    if head == tail or m <= 0:
        return astr
    p1=head
    p2=head+m

    #1、左旋:对于字符串abc def ghi gk,
    #将abc右移到def ghi gk后面,此时n = 11,m = 3,m’ = n % m = 2;
    #abc def ghi gk -> def ghi abc gk

    k = (n - m) - n % m   #p1,p2移动距离,向右移六步

        #---------------------
        #解释下上面的k = (n - m) - n % m的由来:
        #以p2为移动的参照系:
        #n-m 是开始时p2到末尾的长度,n%m是尾巴长度
        #(n-m)-n%m就是p2移动的距离
        #比如 abc def efg hi
        #开始时p2->d,那么n-m 为def efg hi的长度8,
        #n%m 为尾巴hi的长度2,
        #因为我知道abc要移动到hi的前面,所以移动长度是
        #(n-m)-n%m = 8-2 = 6。
        #*/
    i=0
    while i<k:
        astr[p1],astr[p2]=astr[p2],astr[p1]
        i+=1
        p1+=1
        p2+=1
        # print (i,p1,p2)
    print (astr,n-k,n,m,p1,p2,head,tail)
    return rightRotate(astr, n - k, n % m, p1, tail)  #结束左旋,下面,进入右旋

def rightRotate(astr,n,m,head,tail):
    if head == tail or m <= 0:
        return astr

    #2、右旋:问题变成gk左移到abc前面,此时n = m’ + m = 5,m = 2,m’ = n % m 1;
    #abc gk -> a gk bc

    p1 = tail
    p2 = tail - m

    #p1,p2移动距离,向左移俩步
    k = (n - m) - n % m;
    i=0
    while i<k:
        astr[p1],astr[p2]=astr[p2],astr[p1]
        i+=1
        p1-=1
        p2-=1
    print (astr,n-k,n,m,p1,p2,head,tail)
    return leftRotate(astr, n - k, n % m, head, p1)#再次进入上面的左旋部分,
    #3、左旋:问题变成a右移到gk后面,此时n = m’ + m = 3,m = 1,m’ = n % m = 0;
    #a gk bc-> gk a bc。 由于此刻,n % m = 0,满足结束条件,返回结果。

'''
   1、对于正整数m、n互为质数的情况,通过以下过程得到序列的满足上面的要求:
 for i = 0: n-1
      k = i * m % n;
 end

    举个例子来说明一下,例如对于m=3,n=4的情况,
        1、我们得到的序列:即通过上述式子求出来的k序列,是0, 3, 2, 1。
        2、然后,你只要只需按这个顺序赋值一遍就达到左旋3的目的了:
    ch[0]->temp, ch[3]->ch[0], ch[2]->ch[3], ch[1]->ch[2], temp->ch[1]; (*)

    ok,这是不是就是按上面(*)式子的顺序所依次赋值的序列阿?哈哈,很巧妙吧。当然,以上只是特例,作为一个循环链,相当于rotate算法的一次内循环。

2、对于正整数m、n不是互为质数的情况(因为不可能所有的m,n都是互质整数对),那么我们把它分成一个个互不影响的循环链,
所有序号为 (j + i * m) % n(j为0到gcd(n, m)-1之间的某一整数,i = 0:n-1)会构成一个循环链,一共有gcd(n, m)个循环链,对每个循环链分别进行一次内循环就行了。

    综合上述两种情况,可简单编写代码如下:

//④ 所有序号为 (j+i *m) % n (j 表示每个循环链起始位置,i 为计数变量,m表示左旋转位数,n表示字符串长度),
//会构成一个循环链(共有gcd(n,m)个,gcd为n、m的最大公约数),

//每个循环链上的元素只要移动一个位置即可,最后整个过程总共交换了n次
//(每一次循环链,是交换n/gcd(n,m)次,共有gcd(n,m)个循环链,所以,总共交换n次)。

void rotate(string &str, int m)
{
    int lenOfStr = str.length();
    int numOfGroup = gcd(lenOfStr, m);
    int elemInSub = lenOfStr / numOfGroup;

    for(int j = 0; j < numOfGroup; j++)
        //对应上面的文字描述,外循环次数j为循环链的个数,即gcd(n, m)个循环链
    {
        char tmp = str[j];

        for (int i = 0; i < elemInSub - 1; i++)
            //内循环次数i为,每个循环链上的元素个数,n/gcd(m,n)次
            str[(j + i * m) % lenOfStr] = str[(j + (i + 1) * m) % lenOfStr];
        str[(j + i * m) % lenOfStr] = tmp;
    }
}
'''


## 根据上面的描述,写出算法3
def moveGcd(astr,m):
    lenS=len(astr)
    numOfGroup=gcd(lenS,m)
    elemInSub=lenS//numOfGroup

    for j in range(numOfGroup):

        #对应上面的文字描述,外循环次数j为循环链的个数,即gcd(n, m)个循环链
        temp=''
        for  i in range(elemInSub - 1):
            #内循环次数i为,每个循环链上的元素个数,n/gcd(m,n)次
            astr[(j + i * m) % lenS] = astr[(j + (i + 1) * m) % lenS]
        astr[(j + i * m) % lenS] = temp

    return  astr


def test3_1():
    astr='helloworld'
    print (leftRotate(list(astr),10,3,0,9))
    print (moveGcd(list(astr),3))



########################################################################
##                      编程之美3.2 电话号码对应英语单词                 ##
########################################################################


########################################################################
##                      编程之美3.3 计算字符串的相似度                   ##
########################################################################
# 编辑距离的问题a,b的距离=min(F(a[1:],b[:],F(a[:],b[1:],F(a[1:],b[1:])+1

class Memoize:
    def __init__(self, f):
        self.f = f
        self.memo = {}
    def __call__(self, *args):
        if not args in self.memo:
            self.memo[args] = self.f(*args)
        return self.memo[args]


def findMinDistance(a,b,aBegin,aEnd,bBegin,bEnd):
    if aBegin>aEnd:
        if bBegin>bEnd:
            return 0
        else:
            return bEnd-bBegin+1
    if bBegin>bEnd:
        if aBegin>aEnd:
            return 0
        else:
            return aEnd-aBegin+1
    if(a[bBegin]==b[bBegin]):
        return findMinDistance(a,b,aBegin+1,aEnd,bBegin+1,bEnd)
    else:
        t1=findMinDistance(a,b,aBegin+1,aEnd,bBegin+2,bEnd)
        t2=findMinDistance(a,b,aBegin+2,aEnd,bBegin+1,bEnd)
        t3=findMinDistance(a,b,aBegin+2,aEnd,bBegin+2,bEnd)
        return min(t1,t2,t3)+1

def test3_3():
    f=Memoize(findMinDistance)
    print(f('hello','world',0,4,0,4))


########################################################################
##                      编程之美3.4 无头单链表中删除节点                  ##
########################################################################
# 很巧妙的方法A->B->C->D
# 要删除B直接删除不行,因为A->这个链会被切断,那么要做的就是既能保留这个链,又能把B给
# 删除了,做法是先把C和B的数据互换,然后删除C,接上next链,B->next=C->next即可




########################################################################
##                      编程之美3.5 最短摘要的生成                      ##
########################################################################
# 即找最短包含子列表
# 使用双指针法,首尾指针移动

def isAllExisted(sourceList,targetlist,pBegin,pEnd):
        ret=True
        for item in targetlist:
            ret=ret and sourceList[pBegin:pEnd].count(item)!=0
        return ret

def findMinLengthSub(sourceList,targetlist):
    pBegin = 0                         # 初始指针
    pEnd = 0                           # 结束指针
    nTargetLen = 99999                      # 目标数组的长度为N
    nAbstractBegin = 0              # 目标摘要的起始地址
    nAbstractEnd = 0              # 目标摘要的结束地址

    while True:

    # 假设包含所有的关键词,并且后面的指针没有越界,往后移动指针
        while pEnd <len(sourceList) and not isAllExisted(sourceList,targetlist,pBegin,pEnd):
            pEnd+=1

     # 假设找到一段包含所有关键词信息的字符串
        while isAllExisted(sourceList,targetlist,pBegin,pEnd):
            if pEnd-pBegin < nTargetLen:
                nTargetLen = pEnd-pBegin
                nAbstractBegin = pBegin
                nAbstractEnd = pEnd-1
            pBegin+=1

        if pEnd >= len(sourceList):
            break
    return nTargetLen,nAbstractBegin,nAbstractEnd

def test3_5():
    f=Memoize(findMinDistance)
    print(findMinLengthSub(["ab","ef","cd","gh","ij","k","c","ef","t","ij","yz"],['ef','ij']))


########################################################################
##                   编程之美3.7 判断两个链表是否相交                     ##
########################################################################
# 方法较多 见http://blog.163.com/song_0803/blog/static/4609759720120910373784/
# 1.用hash表 2.第一条链遍历的时候标记每一个节点,第二条中有标记过的就相交(实质同1)
# 3.把其中一条首尾相连,那么另一条有环则相交  4.看两条链最后一个节点是否相同

# 求出这个交点呢?上面的方法1,2还可以使用但是需要另外的空间
#另一种方法 5.相遍历一遍两条链发现长度为a,b a>b,那么第二次遍历的
#             时候第一条先走a-b步,然后同时走,一边比较每一个节点



########################################################################
##                   编程之美3.8 队列中取最大值问题                      ##
########################################################################
# O(1)内完成,enqueue,dequeue,findmax操作,做法是使用双数组,同步更新



########################################################################
##                  编程之美3.9 求二叉树中节点的最大距离                ##
########################################################################
#简单的递归,最大距离是一个节点左右深度最大深度子树之和+1
#同时返回深度和最大距离
def findMaxDistance(root):
    if root==None:
        return 0,0
    distanceL,heightL=findMaxDistance(root.left)
    distanceR,heightR=findMaxDistance(root.right)
    return heightL+heightR+1,max(heightL,heightR)+1


#            10
#         /      \
#       5        12
#       /\
#      4  7
def Test3_9():
    pNode10 = BSTreeNode(10);
    pNode5 = BSTreeNode(5)
    pNode12 = BSTreeNode(12)
    pNode4 = BSTreeNode(4)
    pNode7 = BSTreeNode(7)
    ConnectTreeNodes(pNode10, pNode5, pNode12)
    ConnectTreeNodes(pNode5, pNode4, pNode7)

    print (findMaxDistance( pNode10))



if __name__=='__main__':
    Test3_9()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值