3 高质量的代码 3.3 代码的完整性

参考:

  1. 所有offer题目的LeetCode链接及python实现
  2. github Target offer

摘要

面试小提示:

  • 应聘者在写代码的时候,最好用完整的英文单词组合命名变量和函数,以便面试官能一眼读懂代码的意图。
  • 从功能测试、边界测试、负面测试3个方面设计测试用例,以保证代码的完整性

3.3 代码的完整性

面试题11:数值的整数次方

题目:实现函数double Power(double base, int exponent),求base的exponent次方。不得使用库函数,同时不需要考虑大数问题。

思路解析

  1. 全面考虑各种类型与边界值

    1. base输入:负数,零,正数分开考虑
    2. exponent输入:负数,零,正数分开考虑
  2. 需要注意的地方:

    • 指数为负数的时候
    • 底数为零且指数为负数的情况
    • 判断底数 base (double 类型) 是不是等于0的时候,不能直接写base==0, 因为计算机内表示小数时有误差,只能判断他们的差的绝对值是不是在一个很小的范围内
  3. 优化代码速度

    • 递归地调用本方法进行计算,只改变传递的参数 exp
    • 当n为数, a n = a n / 2 ∗ a n / 2 a^n = a^{n/2} * a^{n/2} an=an/2an/2
    • 当n为数, a n = a ( n − 1 ) / 2 ∗ a ( n − 1 ) / 2 ∗ a a^n = a^{(n-1)/2} * a^{(n-1)/2} * a an=a(n1)/2a(n1)/2a
    • 利用右移一位运算代替除以2
    • 利用位与运算代替了求余运算法%来判断一个数是奇数还是偶数
    class Solution:
        def Power(self, base, exp):
            # 处理边界值
            if exp == 0:
                return 1
            if exp == 1:
                return base
            if exp == -1:
                return 1/base
            
            res = self.Power(base, exp>>1)
            res *= res
            # 判断exp的奇偶性
            if exp & 0x1 == 1:
                # 为奇数
                res *= base
            return res
    

面试题12:打印1到最大的n位数

题目:输入数字n,按顺序打印出从1到最大的n位十进制数。比如输入3,则打印出1、2、3一直到最大的3位数即999。

思路解析

  1. 输入的n位数是否会导致溢出,因此利用字符串模拟整数的加法。定义长度为 n 的字符列表。
  2. 定义子方法,对数字进行加一运算,并且返回该数字是否溢出:
    1. 从后到前遍历每一个数字字符;
    2. 每次取最后一个数字字符,对其加一,并返回字符串形式的结果;
    3. 如果遇到 求和结果 超过10的情况,保存进位与数值结果。

注意
4. 在打印函数中,需要判断打印的数字是否是以0开头的,同时判断条件是 num[i] != “0”,不能写作 num[i] != 0,因为是使用str类型的,后面一种写法导致判断无法成功。

def Print1ToMaxOfNDigits(n):
	# 判断特殊情况
    if n <= 0:
        return
	
    number = ['0'] * n
    while not Increment(number):
        PrintNumber(number)

# 判断是否达到了最大数字:如果在最高位idx=0产生进位,则返回True
def Increment(str_num):
    # 是否在最高位溢出
    isOverflow = False
    # 是否产生进位
    nTakeOver = 0
    nLength = len(str_num)
    # 对数组中的元素从后向前遍历
    for i in range(nLength-1, -1, -1):
        # 直接调用python自带的 int 方法转换,或者字符减字符来得到数值大小
        nSum = int(str_num[i]) + nTakeOver
        if i == nLength - 1:
            nSum += 1
		# 如果产生进位
        if nSum >= 10:
            if i == 0:
                isOverflow = True
            else:
                # 产生进位
                nTakeOver = 1    
                nSum -= 10            
                number[i] = str(nSum)
        else:
        	number[i] = str(nSum)
        	break
    return isOverflow

# 依次打印字符列表中从非零元素开始的每个数字字符
def PrintNumber(number):
    isBeginning0 = True
    nLength = len(number)

    for i in range(nLength):
        if isBeginning0 and number[i] != '0':
            isBeginning0 = False
        if not isBeginning0:
        # 必须分开连续打印,结果才会是数字
            print('%c' % number[i], end='')
    print('')

Print1ToMaxOfNDigits(2)

面试题13:在O(1)时间删除链表结点

题目:给定单向链表的头指针和一个结点指针,定义一个函数在O(1)时间删除该结点。
图解

  • 在O(1)时把下一个结点的内存复制覆盖要删除的结点,并删除下一个结点
  • 对于尾结点而言,由于仍然需要顺序查找,时间复杂度是O(n)。
  • 因此总的平均时间复杂度是[(n-1)*O(1)+O(n)]/n,结果还是O(1)
class ListNode:
    def __init__(self, x=None):
        self.val = x
        self.next = None
    def __del__(self):
        self.val = None
        self.next = None

class Solution:
    def DeleteNode(self, pListHead, pToBeDeleted):
    	# 判断特殊情况,原始链表或者欲删除节点为空
        if not pListHead or not pToBeDeleted:
            return None
		# 一般情况,直接将欲删除节点用 pToBeDeleted.next 替换
        if pToBeDeleted.next != None:
            pNext = pToBeDeleted.next
            pToBeDeleted.val = pNext.val
            pToBeDeleted.next = pNext.next
            pNext.__del__()
		# 欲删除节点为头结点
        elif pListHead == pToBeDeleted:
            pToBeDeleted.__del__()
            pListHead.__del__()
		# 欲删除节点为尾节点,需要从头遍历,直到找到尾节点,然后对其替换
        else:
            pNode = pListHead
            while pNode.next != pToBeDeleted:
                pNode = pNode.next
            pNode.next = None
            pToBeDeleted.__del__()

面试题14:调整数组顺序使奇数位于偶数前面

题目:输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分。

思路梳理

维护两个指针,第一个指针初始化时指向数组的第一个数字,它只向后移动;第二个指针初始化时指向数组的最后一个数字,它只向前移动。
图解

注意:在一个函数的输入中, 输入另一个函数的写法func = self.funcName,
funcName不需要加参数括号

class Solution:
    # 一个类似于快排的方法, 只是简单的满足了奇数在前,偶数在后, 奇数的顺序发生了改变
    def reOrderArray(self, array):
        if len(array) < 1:
            return
        elif len(array) == 1:
            return array

        front = 0
        rear = len(array)-1
        while front <= rear:
            while array[front] & 0x1 == 1:
                front += 1
            while array[rear] & 0x1 == 0:
                rear -= 1
            array[front], array[rear] = array[rear], array[front]
        return array
        
    # 直接利用Python的trick, 写一个简单的排列数组, 顺序不变
    def reOrderArray2(self, array):
        left = [x for x in array if x & 1]
        right = [x for x in array if not x & 1]
        return left + right

    def reOrderArray3(self, array):
        if len(array) < 1:
            return []
        if len(array) == 1:
            return array
        arrayOdd = []
        arrayEven = []
        for num in array:
            if num & 0x1:
                arrayOdd.append(num)
            else:
                arrayEven.append(num)
        return arrayOdd+arrayEven

    # 可扩展性的解法
    # 注意在一个函数的输入中, 输入另一个函数的写法func = self.fucName, 
    # funcName不需要加括号
    def Reorder(self, pData, length, func):
        if length == 0:
            return

        pBegin = 0
        pEnd = length - 1

        while pBegin < pEnd:
            while pBegin < pEnd and not func(pData[pBegin]):
                pBegin += 1
            while pBegin < pEnd and func(pData[pEnd]):
                pEnd -= 1

            if pBegin < pEnd:
                pData[pBegin], pData[pEnd] = pData[pEnd], pData[pBegin]
        return pData

    def isEven(self, n):
        return not n & 0x1

    def ReorderOddEven(self, pData):
        length = len(pData)
        return self.Reorder(pData, length, func=self.isEven)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值