算法例题第六章

今天总算开始整理算法了, 一直都被算法搞得瑟瑟发抖。。

写在最前面

所需的知识点:

&: 按位与运算,如果两个相位都为1,则结果为1,否则为0

|:  或 只要有一个为1,则结果为1

^: 异或, 相异为1

<<: 左移,二进制左移一位,相当于乘2

>>: 右移,二进制右移一位,相当于除2

~ :按位取反运算符,对数据的每个二进制位取反,即把1变为0,把0变为1 。~x 类似于 -x-1

 

一 基本数字运算

 

如何判断一个数是不是某个数的平方

例如 16是某个数的平方,15不是某个数的平方, 实际就是问一个数能否被开方。

下面是个比较巧妙地算法, 对n依次减1, 3, 5, 7,……如果减后数大于零,继续减;如果最后等于零,则这个数可以被开方;相减后小于零,则不能被开方。

它的时间复杂度为O(n^0.5)

def isPower(n):
    minus = 1
    while n > 0:
        n = n - minus
        # n是某个数的平方
        if n == 0:
            return True
        # n不是某个数的平方
        elif n < 0:
            return False
        # 每次减数都加2
        else:
            minus += 2
    return False


if __name__ == "__main__":
    n1 = 16
    if isPower(n1):
        print(str(n1) + "是某个数的平方")
    else:
        print(str(n1) + "不是某个数的平方")

 

如何判断一个数是否为2的n次方

1. 简单粗暴的方法, 就是把1逐渐左移一位,乘2,和传入的参数对比

时间复杂度 O(logn)

def isPower(n):
    if n < 1:
        return False
    i = 1
    while i < n:
        if i == n:
            return True
        i <<= 1
    return False

2. 这个同样很巧妙,2的n次方二进制位1, 10, 100..... 发现特点就是一个数是2的平方,它的二进制就是以唯一的1开头,后面都是0。

如果一个数二进制表示只有一个1,例如num=00010000,num-1=00001111,由于num和num-1二进制每一位都不同,

因此num&(num-1)=0,可以利用它判断是不是2的n次方。

时间复杂度O(1)

def isPower(n):
    if n < 1:
        return False
    m = n & (n-1)
    return m == 0

 

如何不使用除法操作实现两个正整数的除法

时间复杂度为O(m/n)

def divide(m, n):
    res = 0
    while m > n:
        m = m - n
        res += 1
    return "商为" + str(m) + ",余数为" + str(res)

 

如何不用加减乘除运算实现基本运算

1. 如何不用加减乘除运算实现加法

利用二进制进行加法运算

  1. 二进制先不考虑进位,不考虑进位的二进制加法可以用异或代替
  2. 计算进位, 由于1+1才会进位,因此进位的运算可以用于操作代替。先做与运算, 再把运算结果左移一位
  3. 不断对1, 2 两步得到的结果相加, 直到进位0为止
def add(n1, n2):
    sums = n1 ^ n2
    carry = (n1 & n2) << 1
    sums += carry
    return sums


if __name__ == "__main__":
    print(add(1, 1))

2. 如何不用加减乘除运算实现减法

根据~的特性,~x 类似于 -x-1, 则~b+1=-b

所以如下

def add(n1, n2):
    sums = n1 ^ n2
    carry = (n1 & n2) << 1
    sums += carry
    return sums


def sub(a, b):
    return add(a, add(~b, 1))


if __name__ == "__main__":
    print(sub(45, 1))

3. 如何不用加减乘除运算实现乘法

4. 如何不用加减乘除运算实现除法

(待议)

 

如何只用+=实现加减乘除运算

def add(a, b):
    """
    方法功能: 用+=实现加法操作(至少有一个非负数)
    进行小数加法
    """
    if a < 0 and b < 0:
        return "无法用+=实现"
    if b >= 0:
        i = 0
        while i < b:    # 实践证明换成 while i != b是不行的, 当它是小数时就gg了
            a += 1      # 相当于把b的一个1拿给a,
            i += 1
        return a
    else:
        i = 0
        while i < a:
            b += 1
            i += 1
        return b


def minus(a, b):
    """
     方法功能: 用+=实现减法操作, 被减数大于等于减数
     相当于从别的地方给减数有一点一点+1,直到和被减数相同, 最后统计加了几个1
     所以它不能进行小数减法
    """
    if a < b:
        return "无法用+=操作实现"
    result = 0
    while b != a:
        b += 1
        result += 1
    return result


def multi(a, b):
    """
    方法功能: 实现乘法操作, 两个数都为整数
    """
    if a <= 0 or b <= 0:
        return "无法用+=操作"
    result = 0
    i = 0
    while i < b:
        result = add(result, a)
        i += 1
    return result


def divide(a, b):
    """
    用+=实现除法, 两个数都为整数
    """
    if a <= 0 or b <= 0:
        return "无法用+=操作实现"
    result = 1
    while True:
        tmpMulti = multi(b, result)
        if tmpMulti <= a:
            result += 1
        else:
            break
    return result-1

 

 

如何判断1024!末尾有几个0?

看它有几个5因子就好了,时间复杂度为O(n)

def zeroCount(n):
    count = 0
    while n > 0:
        n = n//5
        count += n
    return count


if __name__ == "__main__":
    print(str(zeroCount(20)))

 

如何按要求比较两个数的大小

不能使用大于小于和if语句, 返回值为大的数

def maxs(a, b):
    return ((a+b) + abs(a-b)) / 2

 

如何把十进制的数转换为二进制和十六进制的数

def toBinary(n):
    """
    相当于短除法
    """
    result = ""
    while n != 0:
        remainder = n % 2
        result = str(remainder) + result
        # 后面两个变量位置不能变
        n >>= 1
        # 右移除以二
    return result


def toHexadecimal(n):
    result = ""
    while n != 0:
        remainder = n % 16
        if remainder < 10:
            result = str(remainder) + result
        else:
            result = str(chr(remainder - 10 + ord('A'))) + result
        n >>= 4
    return result

 

如何求二进制数中1的个数

1 用(n&1)判断最后一位是不是1, 然后右移动 时间复杂度O(N)

def countOne(n):
    count = 0
    while n > 0:
        # 判断n的二进制最后一位是不是1
        if (n & 1) == 1:   
            count += 1
        n >>= 1
        # 右移一位
    return count

2 利用n&(n-1), 其结果都会少一位1, 而且是最后一位。n=6, 二进制位110, n-1=5, 二进制位101,110&101=100,和110比少了最后一位1。

def countOne(n):
    count = 0
    while n > 0:
        # 如果不为零一直用n & (n-1)去掉最后一个1
        if n != 0:
            n = n & (n-1)
        count += 1
    return count

 

如何计算一个数的n次方

利用递归,效率高。需要判断n的奇偶

def power(d, n):
    if n == 0:
        return 1
    if n == 1:
        return d
    tmp = power(d, abs(n)//2)
    if n > 0:
        if n % 2 == 1:
            return tmp*tmp*d
        else:
            return tmp*tmp
    else:
        if n % 2 == 1:
            return 1/(tmp*tmp*d)
        else:
            return 1/(tmp*tmp)

 

如何在不能使用库函数的条件下计算n的平方根

利用公式ai+1 = (ai _n/ai)/2

def squareRoot(n, e):
    new_one = n
    last_one = 1.0
    while new_one - last_one > e:
        new_one = (new_one + last_one) / 2
        last_one = n / new_one
    return new_one


if __name__ == "__main__":
    n = 500
    e = 0.000001
    print(str(squareRoot(n, e)))

 

如何不使用^操作实现异或运算

时间复杂度O(N)

def xor(x, y):
    return (x | y) & (~x | ~y)

 

如何不使用循环打印出1到100

def prints(n):
    if n > 0:
        prints(n-1)
        print(n)

print(prints(100))

 

如何找最小的不重复数

mmp这个程序折磨我一天,总是静不下心来看,还有原代码太JB冗余了,也是懵逼的原因之一

题目描述:给定任意一个整数(0123,以0开头的不行), 求比这个数大且最小的“不重复数”。“不重复数”的含义是相邻两位数不同,例如1101是“重复数”,1121不是“重复数”

整体思路:

例如给定11099,首先对它加1,开头插入0,变成011100,从左往右找到重复字符串111, 从i=2位遍历,给它加1, 变成012100,在把

2后面的100变成010101...,于是变成012010。符合题意。

但是给的是99020这种的话,99加1要进位,要特殊考虑。即给它加上进位。如下的carry函数。

def carry(num, pos):
    """
    处理特殊情况, 像['9', '9', '0', '2', '0'], 有两个99,要进位的
    """
    while pos > 0:
        # num=['0', '9', '10', '0', '2', '1'], pos=2
        if int(num[pos]) > 9:
            num[pos] = '0'
            # 10变成0
            num[pos-1] = str(int(num[pos-1])+1)
            # 前一位加1,
        pos -= 1
        # 一直执行,直到前面没有要进位的
    return num


def findMinNonDupNum(n):
    count = 0
    # n = 990210
    char = list(str(n+1))
    # list(99020) 是这样的结构 ['9', '9', '0', '2', '0'] , 加1 变成['9', '9', '0', '2', '1']
    ch = list(char)
    ch.insert(0, '0')
    lens = len(ch)
    # 上面三行构造类似的结构 ['0', '9', '9', '0', '2', '1']
    i = 2
    # ['0', '9', '9', '0', '2', '1'], 从第2位开始遍历, 因为零位的0是后加的,一位的数不可能是0, 从第二位开始遍历效率高,否则前几次是白循环
    while i < lens:
        # 从左往右遍历
        count += 1
        # 记录循环次数
        if ch[i-1] == ch[i]:
            # 如果当前的数等于前一个数,即有重复字符串, 这里是二位的9和一位的9相等
            ch[i] = str(int(ch[i])+1)
            # 当前位的数加1, ch变成 ['0', '9', '10', '0', '2', '1']
            carry(ch, i)
            # 调用carry, 返回经过进位处理的ch
            # 下面是把i后面变成01010.....
            j = i + 1
            while j < lens:
                if (j-i) % 2 == 1:
                    # j=i+1, j-i=1, 所以i的后面一定会变成0
                    ch[j] = '0'
                else:
                    ch[j] = '1'
                j += 1
        else:
            i += 1
    print("循环次数" + str(count))
    return int(''.join(ch))


if __name__ == "__main__":
    print(findMinNonDupNum(99020))

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值