python面试题六: 剑指offer

Posted qiu-hua

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python面试题六: 剑指offer相关的知识,希望对你有一定的参考价值。

面试题3 二维数组中的查找 LeetCode
题目:二维数组中,每行从左到右递增,每列从上到下递增,给出一个数,判断它是否在数组中
思路:从左下角或者右上角开始比较

def find_integer(matrix, num):
    """
    :param matrix: [[]]
    :param num: int
    :return: bool
    """
    if not matrix:
        return False
    rows, cols = len(matrix), len(matrix[0])
    row, col = rows - 1, 0
    while row >= 0 and col <= cols - 1:
        if matrix[row][col] == num:
            return True
        elif matrix[row][col] > num:
            row -= 1
        else:
            col += 1
    return False

面试题4 替换空格
题目:把字符串中的空格替换成‘20%‘
方法1:直接使用Python字符串的内置函数

 a  b  .replace( , 20%)

面试题5 从尾到头打印单链表
方法1:使用栈,可以使用列表模拟

def print_links(links):
    stack = []
    while links:
        stack.append(links.val)
        links = links.next
    while stack:
        print stack.pop()

面试题6 重建二叉树 LeetCode
要求:用前序和中序遍历结果构建二叉树,遍历结果中不包含重复值

思路:前序的第一个元素是根结点的值,在中序中找到该值,中序中该值的左边的元素是根结点的左子树,右边是右子树,然后递归的处理左边和右边

提示:二叉树结点,以及对二叉树的各种操作,测试代码见six.py

def construct_tree(preorder=None, inorder=None):
    """
    构建二叉树
    """
    if not preorder or not inorder:
        return None
    index = inorder.index(preorder[0])
    left = inorder[0:index]
    right = inorder[index+1:]
    root = TreeNode(preorder[0])
    root.left = construct_tree(preorder[1:1+len(left)], left)
    root.right = construct_tree(preorder[-len(right):], right)
    return root


面试题7 用两个栈实现队列
要求:用两个栈实现队列,分别实现入队和出队操作 思路:一个栈负责入队,另一个负责出队,出栈为空则从入栈中导入到出栈中

class MyQueue(object):
    def __init__(self):
        self.stack = []
        self.stack2 = []

    def push(self, val):
        self.stack.append(val)

    def pop(self):
        if self.stack2:
            return self.stack2.pop()
        while self.stack:
            self.stack2.append(self.stack.pop())
        return self.stack2.pop() if self.stack2 else u队列为空

面试题8 旋转数组的最小数字
要求:把递增数组的前面部分数字移到队尾,求数组中的最小值,例如[3,4,5,6,1,2]

思路:使用二分法,但要考虑[1, 0, 0, 1]这种数据,只能顺序查找

def find_min(nums):
    if not nums:
        return False
    length = len(nums)
    left, right = 0, length - 1
    while nums[left] >= nums[right]:
        if right - left == 1:
            return nums[right]
        mid = (left + right) / 2
        if nums[left] == nums[mid] == nums[right]:
            return min(nums)
        if nums[left] <= nums[mid]:
            left = mid
        if nums[right] >= nums[mid]:
            right = mid
    return nums[0]

面试题9 斐波那契数列
思路:用生成器

def fib(num):
    a, b = 0, 1
    for i in xrange(num):
        yield b
        a, b = b, a + b

面试题10 二进制中1的个数
要求:求一个整数的二进制表示中,1的个数

思路:二进制表示中,最后的那个1被减去后,低位都变为0,高位不变,按位与就可以去掉这个1

def num_of_1(n):
    ret = 0
    while n:
        ret += 1
        n = n & n-1
    return ret

面试题11 数值的整数次方
要求:求一个数的整数次方

思路:需要考虑次方是正数、负数和0,基数是0

浮点数相等不能直接用==

def power(base, exponent):
    if equal_zero(base) and exponent < 0:
        raise ZeroDivisionError
    ret = power_value(base, abs(exponent))
    if exponent < 0:
        return 1.0 / ret
    else:
        return ret


def equal_zero(num):
    if abs(num - 0.0) < 0.0000001:
        return True


def power_value(base, exponent):
    if exponent == 0:
        return 1
    if exponent == 1:
        return base
    ret = power_value(base, exponent >> 1)
    ret *= ret
    if exponent & 1 == 1:
        ret *= base
    return ret

面试题12 打印1到最大的n位数
要求:输入n,打印出从1到最大的n位数

思路:Python中已经对大整数可以进行自动转换了,所以不需要考虑大整数溢出问题

def print_max_n(n):
    for i in xrange(10 ** n):
        print i

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

思路:如果有后续结点,后续结点的值前移,删除后续结点,如果没有,只能顺序查找了

def delete_node(link, node):
    if node == link:  # 只有一个结点
        del node
    if node.next is None:  # node是尾结点
        while link:
            if link.next == node:
                link.next = None
            link = link.next
    else:
        node.val = node.next.val
        n_node = node.next
        node.next = n_node.next
        del n_node

面试题14 调整数组顺序使奇数位于偶数前面
思路:使用两个指针,前后各一个,为了更好的扩展性,可以把判断奇偶部分抽取出来

def reorder(nums, func):
    left, right = 0, len(nums) - 1
    while left < right:
        while not func(nums[left]):
            left += 1
        while func(nums[right]):
            right -= 1
        if left < right:
            nums[left], nums[right] = nums[right], nums[left]


def is_even(num):
    return (num & 1) == 0

面试题15 链表中倒数第k个结点
要求:求单链表中的倒数第k个结点

思路:使用快慢指针,快的先走k-1步,需要考虑空链表以及k为0

def last_kth(link, k):
    if not link or k <= 0:
        return None
    move = link
    while move and k-1 >= 0:
        move = move.next
        k -= 1
    while move:
        move = move.next
        link = link.next
    if k == 0:
        return link.val
    return None

面试题16 反转链表
要求:反转链表

思路:需要考虑空链表,只有一个结点的链表

def reverse_link(head):
    if not head or not head.next:
        return head
    then = head.next
    head.next = None
    last = then.next
    while then:
        then.next = head
        head = then
        then = last
        if then:
            last = then.next
    return head

面试题17 合并两个排序的链表
要求:合并两个排序的链表

思路:使用递归

def merge_link(head1, head2):
    if not head1:
        return head2
    if not head2:
        return head1
    if head1.val <= head2.val:
        ret = head1
        ret.next = merge_link(head1.next, head2)
    else:
        ret = head2
        ret.next = merge_link(head1, head2.next)
    return ret

面试题18 树的子结构
要求:判断一棵二叉树是不是另一个的子结构

思路:使用递归

def sub_tree(tree1, tree2):
    if tree1 and tree2:
        if tree1.val == tree2.val:
            return sub_tree(tree1.left, tree2.left) and sub_tree(tree1.right, tree2.right)
        else:
            return sub_tree(tree1.left, tree2) or sub_tree(tree1.right, tree2)
    if not tree1 and tree2:
        return False
    return True

面试题19 二叉树的镜像
思路一:可以按层次遍历,每一层从右到左

思路二:使用递归

def mirror_bfs(root):
    ret = []
    queue = deque([root])
    while queue:
        node = queue.popleft()
        if node:
            ret.append(node.val)
            queue.append(node.right)
            queue.append(node.left)
    return ret


def mirror_pre(root):
    ret = []

    def traversal(root):
        if root:
            ret.append(root.val)
            traversal(root.right)
            traversal(root.left)
    traversal(root)
    return ret

面试题20 顺时针打印矩阵

def print_matrix(matrix):
    """
    :param matrix: [[]]
    """
    rows = len(matrix)
    cols = len(matrix[0]) if matrix else 0
    start = 0
    ret = []
    while start * 2 < rows and start * 2 < cols:
        print_circle(matrix, start, rows, cols, ret)
        start += 1
    print ret


def print_circle(matrix, start, rows, cols, ret):
    row = rows - start - 1  # 最后一行
    col = cols - start - 1
    # left->right
    for c in range(start, col+1):
        ret.append(matrix[start][c])
    # top->bottom
    if start < row:
        for r in range(start+1, row+1):
            ret.append(matrix[r][col])
    # right->left
    if start < row and start < col:
        for c in range(start, col)[::-1]:
            ret.append(matrix[row][c])
    # bottom->top
    if start < row and start < col:
        for r in range(start+1, row)[::-1]:
            ret.append(matrix[r][start])

面试题21 包含min函数的栈
要求:栈的push,pop,min操作的时间复杂度都是O(1)

思路:使用一个辅助栈保存最小值

class MyStack(object):

    def __init__(self):
        self.stack = []
        self.min = []

    def push(self, val):
        self.stack.append(val)
        if self.min and self.min[-1] < val:
            self.min.append(self.min[-1])
        else:
            self.min.append(val)

    def pop(self):
        if self.stack:
            self.min.pop()
            return self.stack.pop()
        return None

    def min(self):
        return self.min[-1] if self.min else None

面试题22 栈的压入弹出序列
要求:判断给定的两个序列中,后者是不是前者的弹出序列,给定栈不包含相同值

思路:使用一个辅助栈, 如果辅助栈栈顶元素不等于出栈元素,则从入栈中找改值,直到入栈为空

如果最后出栈序列为空,则是入栈的弹出序列值

 def pop_order(push_stack, pop_stack):
    if not push_stack or not pop_stack:
        return False
    stack = []
    while pop_stack:
        pop_val = pop_stack[0]
        if stack and stack[-1] == pop_val:
            stack.pop()
            pop_stack.pop(0)
        else:
            while push_stack:
                if push_stack[0] != pop_val:
                    stack.append(push_stack.pop(0))
                else:
                    push_stack.pop(0)
                    pop_stack.pop(0)
                    break
        if not push_stack:
            while stack:
                if stack.pop() != pop_stack.pop(0):
                    return False
    if not pop_stack:
        return True
    return False

面试题23 从上往下打印二叉树
思路:广度优先搜索,按层次遍历

 def bfs(tree):
    if not tree:
        return None
    stack = [tree]
    ret = []
    while stack:
        node = stack.pop(0)
        ret.append(node.val)
        if node.left:
            stack.append(node.left)
        if node.right:
            stack.append(node.right)
    return ret

面试题24 二叉搜索树的后序遍历序列
要求:判断给定的整数数组是不是二叉搜索树的后序遍历序列

整数数组中不包含重复值

整数序列的最后一个值是根结点,然后比根结点小的值是左子树,剩下的是右子树,递归左右子树

 def is_post_order(order):
    length = len(order)
    if length:
        root = order[-1]
        left = 0
        while order[left] < root:
            left += 1
        right = left
        while right < length - 1:
            if order[right] < root:
                return False
            right += 1
        left_ret = True if left == 0 else is_post_order(order[:left])
        right_ret = True if left == right else is_post_order(order[left:right])
        return left_ret and right_ret
    return False

面试题25 二叉树中和为某一值的路径
要求:输入一棵二叉树和一个值,求从根结点到叶结点的和等于该值的路径

深度优先搜索变形

 def find_path(tree, num):
    ret = []
    if not tree:
        return ret
    path = [tree]
    sums = [tree.val]

    def dfs(tree):
        if tree.left:
            path.append(tree.left)
            sums.append(sums[-1]+tree.left.val)
            dfs(tree.left)
        if tree.right:
            path.append(tree.right)
            sums.append(sums[-1] + tree.right.val)
            dfs(tree.right)
        if not tree.left and not tree.right:
            if sums[-1] == num:
                ret.append([p.val for p in path])
        path.pop()
        sums.pop()

    dfs(tree)
    return ret

面试题26 复杂链表的复制
要求:链表中除了指向后一个结点的指针之外,还有一个指针指向任意结点

分为三步完成:

一:复制每个结点,并把新结点放在老结点后面,如1->2,复制为1->1->2->2

二:为每个新结点设置other指针

三:把复制后的结点链表拆开

题目设置了复杂链表的实现,测试代码见文件twenth_six.py

class Solution(object):

    @staticmethod
    def clone_nodes(head):
        # 结点复制
        move = head
        while move:
            tmp = Node(move.val)
            tmp.next = move.next
            move.next = tmp
            move = tmp.next
        return head

    @staticmethod
    def set_nodes(head):
        # other指针设置
        move = head
        while move:
            m_next = move.next
            if move.other:
                m_next.other = move.other.next
            move = m_next.next
        return head

    @staticmethod
    def reconstruct_nodes(head):
        # 结点拆分
        ret = head.next if head else Node
        move = ret
        while head:
            head = move.next
            if head:
                move.next = head.next
                move = move.next
        return ret

    @staticmethod
    def clone_link(head):
        # 结果
        h = Solution.clone_nodes(head)
        h = Solution.set_nodes(h)
        ret = Solution.reconstruct_nodes(h)
        return ret


面试题27 二叉搜索树与双向链表
要求: 将二叉搜索树转化成一个排序的双向链表,只调整树中结点的指向

思路: 中序遍历,根结点的left指向左子树的最后一个(最大)值,right指向右子树的(最小)值

注意: 题目构造了一个普通二叉树用来测试,构造时按照二叉搜索树的顺序输入结点,空结点用None表示,详情见twenty_seven.py

class Solution(object):

    @staticmethod
    def convert(tree):
        """结点转换"""
        if not tree:
            return None
        p_last = Solution.convert_nodes(tree, None)
        while p_last and p_last.left:  # 获取链表头结点
            p_last = p_last.left
        return p_last

    @staticmethod
    def convert_nodes(tree, last):
        if not tree:
            return None
        if tree.left:
            last = Solution.convert_nodes(tree.left, last)
        if last:
            last.right = tree
        tree.left = last
        last = tree
        if tree.right:
            last = Solution.convert_nodes(tree.right, last)
        return last

面试题28 字符串的排列
要求:求输入字符串的全排列

思路:递归完成,也可以直接使用库函数

def my_permutation(s):
    str_set = []
    ret = []  # 最后的结果

    def permutation(string):
        for i in string:
            str_tem = string.replace(i, ‘‘)
            str_set.append(i)
            if len(str_tem) > 0:
                permutation(str_tem)
            else:
                ret.append(‘‘.join(str_set))
            str_set.pop()

    permutation(s)
    return ret

面试题29 数组中出现次数超过一半的数字
思路: 使用hash,key是数字,value是出现的次数

注意: 列表的len方法的时间复杂度是O(1)

def get_more_half_num(nums):
    hashes = dict()
    length = len(nums)
    for n in nums:
        hashes[n] = hashes[n] + 1 if hashes.get(n) else 1
        if hashes[n] > length / 2:
            return n

面试题30 最小的k个数
要求:求数组中出现次数超过一半的数字

思路: 使用heapq,该模块是一个最小堆,需要转化成最大堆,只要在入堆的时候把值取反就可以转化成最大堆(仅适用于数字)

思路二: 数组比较小的时候可以直接使用heapq的nsmallest方法

import heapq


def get_least_k_nums(nums, k):
    # 数组比较小的时候可以直接使用
    return heapq.nsmallest(k, nums)


class MaxHeap(object):
    def __init__(self, k):
        self.k = k
        self.data = []

    def push(self, elem):
        elem = -elem  # 入堆的时候取反,堆顶就是最大值的相反数了
        if len(self.data) < self.k:
            heapq.heappush(self.data, elem)
        else:
            least = self.data[0]
            if elem > least:
                heapq.heapreplace(self.data, elem)

    def get_least_k_nums(self):
        return sorted([-x for x in self.data])

面试题31 连续子数组的最大和
思路: 动态规划问题

def max_sum(nums):
    ret = float("-inf")  # 负无穷
    if not nums:
        return ret
    current = 0
    for i in nums:
        if current <= 0:
            current = i
        else:
            current += i
        ret = max(ret, current)
    return ret

面试题32 从1到n整数中1出现的次数
要求:求从1到n整数的十进制表示中,1出现的次数

思路: 获取每个位数区间上所有数中包含1的个数,然后分别对高位分析,然后递归的处理低位数

此题中,作者的描述我没有理解,按照自己的理解写了一下,具体内容请点击这里

def get_digits(n):
    # 求整数n的位数
    ret = 0
    while n:
        ret += 1
        n /= 10
    return ret


def get_1_digits(n):
    """
    获取每个位数之间1的总数
    :param n: 位数
    """
    if n <= 0:
        return 0
    if n == 1:
        return 1
    current = 9 * get_1_digits(n-1) + 10 ** (n-1)
    return get_1_digits(n-1) + current


def get_1_nums(n):
    if n < 10:
        return 1 if n >= 1 else 0
    digit = get_digits(n)  # 位数
    low_nums = get_1_digits(digit-1)  # 最高位之前的1的个数
    high = int(str(n)[0])  # 最高位
    low = n - high * 10 ** (digit-1)  # 低位

    if high == 1:
        high_nums = low + 1  # 最高位上1的个数
        all_nums = high_nums
    else:
        high_nums = 10 ** (digit - 1)
        all_nums = high_nums + low_nums * (high - 1)  # 最高位大于1的话,统计每个多位数后面包含的1
    return low_nums + all_nums + get_1_nums(low)

面试题33 把数组排成最小的数
要求:把数组中的值拼接,找出能产生的最小的数[321,32,3]最小的数是321323

思路: Python中不需要考虑大整数,需要自己定义一个数组排序规则,直接调用库函数就可以

def cmp(a, b):
    return int(str(a)+str(b)) - int(str(b)+str(a))


def print_mini(nums):
    print int(‘‘.join([str(num) for num in sorted(nums, cmp=cmp)]))

面试题34 丑数 LeetCode
要求:只含有2、3、5因子的数是丑数,求第1500个丑数

思路: 按顺序保存已知的丑数,下一个是已知丑数中某三个数乘以2,3,5中的最小值

class Solution(object):
    def nthUglyNumber(self, n):
        """
        :type n: int
        :rtype: int
        """
        ugly = [1]
        t2 = t3 = t5 = 0
        while len(ugly) < n:
            while ugly[t2] * 2 <= ugly[-1]:
                t2 += 1
            while ugly[t3] * 3 <= ugly[-1]:
                t3 += 1
            while ugly[t5] * 5 <= ugly[-1]:
                t5 += 1
            ugly.append(min(ugly[t2]*2, ugly[t3]*3, ugly[t5]*5))
        return ugly[-1]

面试题35 第一个只出现一次的字符
要求:求字符串中第一个只出现一次的字符

思路: 使用两个hash,一个记录每个字符穿线的次数,另一个记录每个字符第一次出现的位置

def first_not_repeating_char(string):
    if not string:
        return -1
    count = {}
    loc = {}
    for k, s in enumerate(string):
        count[s] = count[s] + 1 if count.get(s) else 1
        loc[s] = loc[s] if loc.get(s) else k
    ret = float(inf)
    for k in loc.keys():
        if count.get(k) == 1 and loc[k] < ret:
            ret = loc[k]
    return ret

面试题36 数组中的逆序对
要求:在一个数组中,前面的数字比后面的大,就是一个逆序对,求总数

思路: 归并排序,先把数组依次拆开,然后合并的时候统计逆序对数目,并排序

import copy


def get_inverse_pairs(nums):
    if not nums:
        return 0
    start, end = 0, len(nums) - 1
    tmp = copy.deepcopy(nums)
    return inverse_pairs(tmp, start, end)


def inverse_pairs(tmp, start, end):
    if start == end:  # 递归结束条件
        return 0
    mid = (end - start) / 2  # 分别对左右两边递归求值
    left = inverse_pairs(tmp, start, start+mid)
    right = inverse_pairs(tmp, start+mid+1, end)

    count = 0  # 本次逆序对数目
    l_right, r_right = start + mid, end
    t = []
    while l_right >= start and r_right >= start + mid + 1:
        if tmp[l_right] > tmp[r_right]:
            t.append(tmp[l_right])
            count += (r_right - mid - start)
            l_right -= 1
        else:
            t.append(tmp[r_right])
            r_right -= 1
    while l_right >= start:
        t.append(tmp[l_right])
        l_right -= 1
    while r_right >= start+mid+1:
        t.append(tmp[r_right])
        r_right -= 1
    tmp[start:end+1] = t[::-1]
    return count + left + right

面试题37 两个链表的第一个公共结点
思路: 先获取到两个链表的长度,然后长的链表先走多的几步,之后一起遍历

文件thirty_seven.py中包含了设置链表公共结点的代码,可以用来测试

def get_first_common_node(link1, link2):
    if not link1 or not link2:
        return None
    length1 = length2 = 0
    move1, move2 = link1, link2
    while move1:  # 获取链表长度
        length1 += 1
        move1 = move1.next
    while move2:
        length2 += 1
        move2 = move2.next
    while length1 > length2:  # 长链表先走多的长度
        length1 -= 1
        link1 = link1.next
    while length2 > length1:
        length2 -= 1
        link2 = link2.next
    while link1:  # 链表一起走
        if link1 == link2:
            return link1
        link1, link2 = link1.next, link2.next
    return None

面试题38 数字在排序数组中出现的次数
思路: 使用二分法分别找到数组中第一个和最后一个出现的值的坐标,然后相减

def get_k_counts(nums, k):
    first = get_first_k(nums, k)
    last = get_last_k(nums, k)
    if first < 0 and last < 0:
        return 0
    if first < 0 or last < 0:
        return 1
    return last - first + 1


def get_first_k(nums, k):
    left, right = 0, len(nums) - 1
    while left <= right:
        mid = (left + right) / 2
        if nums[mid] < k:
            if mid + 1 < len(nums) and nums[mid+1] == k:
                return mid + 1
            left = mid + 1
        elif nums[mid] == k:
            if mid - 1 < 0 or (mid - 1 >= 0 and nums[mid-1] < k):
                return mid
            right = mid - 1
        else:
            right = mid - 1
    return -1


def get_last_k(nums, k):
    left, right = 0, len(nums) - 1
    while left <= right:
        mid = (left + right) / 2
        if nums[mid] < k:
            left = mid + 1
        elif nums[mid] == k:
            if mid + 1 == len(nums) or (mid + 1 < len(nums) and nums[mid+1] > k):
                return mid
            left = mid + 1
        else:
            if mid - 1 >= 0 and nums[mid-1] == k:
                return mid - 1
            right = mid - 1
    return -1

面试题39 二叉树的深度
思路: 分别递归的求左右子树的深度

def get_depth(tree):
    if not tree:
        return 0
    if not tree.left and not tree.right:
        return 1
    return 1 + max(get_depth(tree.left), get_depth(tree.right))

面试题40 数组中只出现一次的数字
要求:数组中除了两个只出现一次的数字外,其他数字都出现了两遍

思路: 按位异或,在得到的值中找到二进制最后一个1,然后把数组按照该位是0还是1分为两组

def get_only_one_number(nums):
    if not nums:
        return None
    tmp_ret = 0
    for n in nums:  # 获取两个值的异或结果
        tmp_ret ^= n
    last_one = get_bin(tmp_ret)
    a_ret, b_ret = 0, 0
    for n in nums:
        if is_one(n, last_one):
            a_ret ^= n
        else:
            b_ret ^= n
    return [a_ret, b_ret]


def get_bin(num):  # 得到第一个1
    ret = 0
    while num & 1 == 0 and ret < 32:
        num = num >> 1
        ret += 1
    return ret


def is_one(num, t):  # 验证t位是不是1
    num = num >> t
    return num & 0x01

面试题41 和为s的两个数字VS和为s的连续正数序列
和为s的两个数字
要求:输入一个递增排序的数组和一个数字s,在数组中查找两个数,使其和为s

思路: 设置头尾两个指针,和大于s,尾指针减小,否砸头指针增加

def sum_to_s(nums, s):
    head, end = 0, len(nums) - 1
    while head < end:
        if nums[head] + nums[end] == s:
            return [nums[head], nums[end]]
        elif nums[head] + nums[end] > s:
            end -= 1
        else:
            head += 1
    return None

和为s的连续整数序列
要求:输入一个正数s, 打印出所有和为s的正整数序列(至少两个数)

思路: 使用两个指针,和比s小,大指针后移,比s大,小指针后移

def sum_to_s(s):
    a, b = 1, 2
    ret = []
    while a < s / 2 + 1:
        if sum(range(a, b+1)) == s:
            ret.append(range(a, b+1))
            a += 1
        elif sum(range(a, b+1)) < s:
            b += 1
        else:
            a += 1
    return ret

面试题42 翻转单词顺序与左旋转字符串
翻转单词顺序
要求:翻转一个英文句子中的单词顺序,标点和普通字符一样处理

思路: Python中字符串是不可变对象,不能用书中的方法,可以直接转化成列表然后转回去

def reverse_words(sentence):
    tmp = sentence.split()
    return  .join(tmp[::-1])  # 使用join效率更好,+每次都会创建新的字符串

左旋转字符串
思路: 把字符串的前面的若干位移到字符串的后面

def rotate_string(s, n):
    if not s:
        return ‘‘
    n %= len(s)
    return s[n:] + s[:n]

面试题43 n个骰子的点数
要求:求出n个骰子朝上一面之和s所有可能值出现的概率

思路:n出现的可能是前面n-1到n-6出现可能的和,设置两个数组,分别保存每一轮

def get_probability(n):
    if n < 1:
        return []
    data1 = [0] + [1] * 6 + [0] * 6 * (n - 1)
    data2 = [0] + [0] * 6 * n   # 开头多一个0,方便按照习惯从1计数
    flag = 0
    for v in range(2, n+1):  # 控制次数
        if flag:
            for k in range(v, 6*v+1):
                data1[k] = sum([data2[k-j] for j in range(1, 7) if k > j])
            flag = 0
        else:
            for k in range(v, 6*v+1):
                data2[k] = sum([data1[k-j] for j in range(1, 7) if k > j])
            flag = 1
    ret = []
    total = 6 ** n
    data = data2[n:] if flag else data1[n:]
    for v in data:
        ret.append(v*1.0/total)
    print data
    return ret

面试题44 扑克牌的顺子
要求:从扑克牌中随机抽取5张牌,判断是不是顺子,大小王可以当任意值

思路: 使用排序

import random


def is_continus(nums, k):
    data = [random.choice(nums) for _ in range(k)]
    data.sort()
    print data
    zero = data.count(0)
    small, big = zero, zero + 1
    while big < k:
        if data[small] == data[big]:
            return False
        tmp = data[big] - data[small]
        if tmp > 1:
            if tmp - 1 > zero:
                return False
            else:
                zero -= tmp - 1
                small += 1
                big += 1
        else:
            small += 1
            big += 1
    return True

面试题45 圆圈中最后剩下的数字
要求:0到n-1排成一圈,从0开始每次数m个数删除,求最后剩余的数

思路:当 n > 1 时: f(n,m) = [f(n-1, m)+m]%n,当 n = 1 时: f(n,m)=0,关键是推导出关系表达式

def last_num(n, m):
    ret = 0
    if n == 1:
        return 0
    for i in range(2, n+1):
        ret = (m + ret) % i
    return ret

面试题46 求1+2...+n
要求:不能使用乘除、for、while、if、else等

方法一:使用range和sum

方法二:使用reduce

def get_sum1(n):
    return sum(range(1, n+1))


def get_sum2(n):
    return reduce(lambda x, y: x+y, range(1, n+1))

面试题47 不用加减乘除做加法
要求:不用加减乘除做加法

方法一:使用位运算,Python中大整数会自动处理,因此对carry需要加个判断

方法二:使用sum

def bit_add(n1, n2):
    carry = 1
    while carry:
        s = n1 ^ n2
        carry = 0xFFFFFFFF & ((n1 & n2) << 1)
        carry = -(~(carry - 1) & 0xFFFFFFFF) if carry > 0x7FFFFFFF else carry
        n1 = s
        n2 = carry
    return n1


def add(n1, n2):
    return sum([n1, n2])

面试题48 不能被继承的类
Python中不知道怎么实现不能被继承的类。以后补充代码或者原因。


 

面试题49 把字符串转化成整数
要求:把字符串转化成整数

测试用例:正负数和0,空字符,包含其他字符

备注:使用raise抛出异常作为非法提示

def str_to_int(string):
    if not string:  # 空字符返回异常
        raise Exception(string cannot be None, string)
    flag = 0  # 用来表示第一个字符是否为+、-
    ret = 0  # 结果
    for k, s in enumerate(string):
        if s.isdigit():  # 数字直接运算
            val = ord(s) - ord(0)
            ret = ret * 10 + val
        else:
            if not flag:
                if s == + and k == 0:  # 避免中间出现+、-
                    flag = 1
                elif s == - and k == 0:
                    flag = -1
                else:
                    raise Exception(digit is need, string)
            else:
                raise Exception(digit is need, string)
    if flag and len(string) == 1:  # 判断是不是只有+、-
        raise Exception(digit is need, string)
    return ret if flag >= 0 else -ret

 

面试题50 树中两个结点的最低公共祖先
要求:求普通二叉树中两个结点的最低公共祖先

方法一:先求出两个结点到根结点的路径,然后从路径中找出最后一个公共结点

备注:文件fifty.py中包含该代码的具体测试数据

class Solution(object):

    def __init__(self, root, node1, node2):
        self.root = root  # 树的根结点
        self.node1 = node1
        self.node2 = node2  # 需要求的两个结点

    @staticmethod
    def get_path(root, node, ret):
        """获取结点的路径"""
        if not root or not node:
            return False
        ret.append(root)
        if root == node:
            return True
        left = Solution.get_path(root.left, node, ret)
        right = Solution.get_path(root.right, node, ret)
        if left or right:
            return True
        ret.pop()

    def get_last_common_node(self):
        """获取公共结点"""
        route1 = []
        route2 = []  # 保存结点路径
        ret1 = Solution.get_path(self.root, self.node1, route1)
        ret2 = Solution.get_path(self.root, self.node2, route2)
        ret = None
        if ret1 and ret2:  # 路径比较
            length = len(route1) if len(route1) <= len(route2) else len(route2)
            index = 0
            while index < length:
                if route1[index] == route2[index]:
                    ret = route1[index]
                index += 1
        return ret

 

 

 a  b  .replace( , 20%)

以上是关于python面试题六: 剑指offer的主要内容,如果未能解决你的问题,请参考以下文章

剑指Offer算法类题目[Python版]

剑指Offer算法类题目[Python版]

剑指offer面试题-Java版-持续更新

剑指Offer[Python版]

剑指Offer名企面试官精讲典型编程题pdf

剑指Offer名企面试官精讲典型编程题pdf