《剑指offer》 python实现(持续更新中)

面试题3. 不修改数组找出重复的数字

在一个长度为 n+1 的数组里,所有数字都在 1~n 范围内,所以数组中至少有一个数字是重复的。请找出数组中任意一个重复的数字,但不能修改输入的数组。例如,如果输入长度为8的数组 {2,3,5,4,3,2,6,7}, 那么对应的输出是重复的2 或者3.

def getDuplication(n, s):
    if not n:
        return 0
    l = 1
    r = n       
    while l < r:
        num = 0
        mid = (l + r) // 2
        for i in range(len(s)):
            if s[i] <= mid and s[i] >= l:
                num += 1
        if num > (mid - l + 1):
            r = mid
        else:
            l = mid + 1
    return l

总的时间复杂度为O(nlogn), 空间复杂度为O(1)

面试题4. 二维数组中的查找

在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
思路:从右上角开始查找,若该值小于需要寻找的值,删除该列,大于的话,删除该行。

def Find(s, num):
    row = len(s)
    column = len(s[0])
    if row > 0 and column > 0:
        r = 0
        c = column - 1
        while r != row - 1 and c != 0:
            if s[r][c] == num:
                return True
            elif s[r][c] > num:
                c -= 1
            else:
                r += 1
        if s[row - 1][0] != num:
            return False
        else:
            return True
    return False

面试题5.替换空格

请实现一个函数,把字符串中的每个空格替换成"%20"。例如输入“We are happy.”,则输出“We%20are%20happy.”。(python中字符串是不允许修改和替换的)
思路:首先遍历一遍确定替换后的总长度,之后设置双指针从后往前进行替换。时间复杂度为O(n)

def ReplaceBlank(st):
    res = list(st)
    num = res.count(' ')
    res += [0] * 2 * num
    p = len(st) - 1
    q = len(res) - 1
    while p != q:
        if res[p] != ' ':
            res[q] = res[p]
            q -= 1
        else:
            res[q] = '0'
            res[q - 1] = '2'
            res[q - 2] = '%'
            q -= 3
        p -= 1
    return ''.join(res)

面试题6.从尾到头打印链表

输入一个链表,从尾到头打印链表每个节点的值。

def PrintListReversingly_Iteratively(head):#迭代
		pre = None
        while head:
            temp = head.next
            head.next = pre
            pre = head
            head = temp
        return pre
        # p, rev = head, None
        # while p:
        #     rev, rev.next, p = p, rev, p.next
        # return rev
def PrintListReversingly_Iteratively(p):#递归
    if not p:
        return []
    else:
        return PrintListReversingly_Iteratively(p.next) + [p.val]

面试题7.重建二叉树

根据一棵树的前序遍历与中序遍历构造二叉树。(你可以假设树中没有重复的元素)

def buildTree(self, pre, tin):
        """
        :type preorder: List[int]
        :type inorder: List[int]
        :rtype: TreeNode
        """
        if len(pre) > 0:
            root = TreeNode(pre[0])
            id = tin.index(root.val)
            root.left = self.buildTree(pre[1:id+1], tin[:id])
            root.right = self.buildTree(pre[id+1:], tin[id+1:])
            return root

面试题8.二叉树的下一个节点

给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。
思路:首先判断该节点是否有右子树,若有右子树,则寻找右子树中的最左节点,即为下一个节点;
若无右子树,则判断该节点是其父节点的左或右子树,若为左子树,则其父节点为下一个节点,若为右子树,则需向上遍历,直到找到一个节点为其父节点左子树,则下一个节点为其父节点。

'''
class TreeNode(object):
     def __init__(self, x):
         self.val = x
         self.left = None
         self.right = None
         self.par = None
'''
class Solution(object):
    def NextNode(self, s):
        """        
        :type s: TreeNode
        """
        if not s:
            return None
        
        if s.right:
            s = s.right
            while s.left:
                s = s.left
        elif s == s.par.left:
            s = s.par
        elif s == s.par.right:
            s = s.par
            if s != s.par.left:
                s = s.par
            s = s.par
        return s

面试题22.链表中倒数第K个节点

输入一个链表,输出该链表中倒数第k个结点。

def FindKthToTail(self, head, k):
    # write code here
    if not head or k == 0:
        return None
    pre = head
    post = head
    for i in range(k):#注意pre指针先走k步,pre 与 post 之间相差 k 个元素
        if not pre:
            return None
        pre = pre.next
    while pre:#最后一次判断,pre 指向指针末尾
        pre = pre.next
        post = post.next
    return post

面试题23.链表中环的入口节点

如果一个链表中包含环,如何找出环的入口节点。
思路:首先设置快慢指针,则两指针一定会在环内相遇,相遇时快指针比慢指针多走了一个环的距离,之后设置快指针指向头指针,两个指针同时运动,一次运动一步,再相遇时就是在换的入口节点处。

class Solution:
    def EntryNodeOfLoop(self, pHead):
        # write code here
        if not pHead or not pHead.next:
            return None
        fast = pHead
        slow = pHead
        while fast and fast.next:
            fast = fast.next.next
            slow = slow.next
            if fast.val == slow.val:
                fast = pHead
                while fast.val != slow.val:
                    fast = fast.next
                    slow = slow.next
                return fast
        return None```

```python

面试题24.反转链表

def ReverseList(self, pHead):
        # write code here
        if not pHead:
            return None
        head = pHead
        pre = None
        while head:
            pre, pre.next, head = head, pre, head.next
        return pre

面试题25.合并两个排序的链表

输入两个单调递增的链表,输出两个链表合成后的链表。

    def Merge(self, pHead1, pHead2):
        # write code here
        head = ListNode(None)
        cur = head
        while pHead1 and pHead2:
            if pHead1.val <= pHead2.val:
                cur.next = pHead1 
                pHead1 = pHead1.next
            else:
                cur.next = pHead2 
                pHead2 = pHead2.next
            cur = cur.next
        if pHead1:
                cur.next = pHead1
        elif pHead2:
                cur.next = pHead2
        return head.next

面试题26.树的子结构

输入两颗二叉树A和B,判断B是不是A的子结构。

class Solution:
    def HasSubtree(self, pRoot1, pRoot2):
        # write code here
        # write code here
        if not pRoot1 or not pRoot2:
            return False
        return self.is_subtree(pRoot1, pRoot2) or self.HasSubtree(pRoot1.left, pRoot2) or self.HasSubtree(pRoot1.right, pRoot2)
      
    def is_subtree(self, A, B):
        if not B:
            return True
        if not A or A.val != B.val:
            return False
        return self.is_subtree(A.left,B.left) and self.is_subtree(A.right, B.right)

面试题27.二叉树的镜像

输入一棵二叉树,输出它的镜像

class Solution:
    # 返回镜像树的根节点
    def Mirror(self, root):
        # write code here
        if not root:
            return
        if not root.left and not root.right:
            return
        else:
            root.left, root.right = root.right, root.left
        self.Mirror(root.left) 
        self.Mirror(root.right)

面试题28.对称的二叉树

请实现一个函数,用来判断一棵二叉树是不是对称的。

class Solution:
    def isSymmetrical(self, pRoot):
        # write code here
        if not pRoot:
            return True
        #if not pRoot.right and not pRoot.left:
         #   return True
        if (pRoot.right and not pRoot.left) or (not pRoot.right and pRoot.left):
            return False
        return self.is_tree(pRoot.left, pRoot.right)
        
    def is_tree(self, root1, root2):
        if not root1 and not root2:
            return True
        if (root1 and root2) and root1.val == root2.val:
            return self.is_tree(root1.left, root2.right) and self.is_tree(root1.right, root2.left)

面试题29.顺时针打印矩阵

输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。(Leetcode 54 螺旋矩阵)
思路:将已经走过的地方置0,然后拐弯的时候判断一下是不是已经走过了,如果走过了就计算一下新的方向,这里用取模运算来判断。

def printMatrix(self, matrix):
        # write code here
        res, i, j, di, dj = [], 0, 0, 0, 1
        if matrix:
            for _ in range(len(matrix) * len(matrix[0])):
                res.append(matrix[i][j])
                matrix[i][j] = 0
                if matrix[(i + di) % len(matrix)][(j + dj) % len(matrix[0])] == 0:
                    di, dj = dj, -di
                i += di
                j += dj
        return res

面试题30.包含min函数的栈

定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。
思路:构建一个辅助栈,当压入元素时,将之前最小元素和该元素之间的较小值保存到该辅助栈中,所以辅助栈中对应元素就是需要的min的值。

class Solution:
    def __init__(self):
        self.stack = []
        self.assist = []
    def push(self, node):
        if not self.assist:
            self.assist.append(node)
        else:
            self.assist.append(min(node, self.assist[-1]))
        self.stack.append(node)
    def pop(self):
        if self.stack:
            self.assist.pop()
            return self.stack.pop()
    def top(self):
        # write code here
        if self.stack:
            return self.stack[-1]
    def min(self):
        # write code here
        if self.assist:
            return self.assist[-1]

面试题31.栈的压入、弹出序列

输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)
思路:构建一个辅助栈,将第一个序列依次压入该栈。如果下一个弹出的元素恰好是栈顶元素,那么直接弹出;如果不在栈顶,则把还没入栈的数字压入辅助栈,直到把下一个需要弹出的数字压入栈顶为止;如果所有数字都压入栈顶仍然找不到下一个弹出的数字,那么可断定该序列不可能是一个弹出序列。

class Solution:
    def IsPopOrder(self, pushV, popV):
        # write code here
        stack = []
        while popV:
            if stack and stack[-1] == popV[0]:
                stack.pop()
                popV.pop(0)
            elif pushV:
                stack.append(pushV.pop(0))
            else:
                return False
        return True

面试题32.从上到下打印二叉树

从上往下打印出二叉树的每个节点,同层节点从左至右打印。
思路:构建一个队列,从树节点开始依次将左右节点放入该队列,每次取出一个节点,并将该节点的左右节点继续放入队列。

class Solution:
    # 返回从上到下每个节点值列表,例:[1,2,3]
    def PrintFromTopToBottom(self, root):
        # write code here
        if not root:
            return []
        queue = []
        result = []
        queue.append(root)
        while len(queue) > 0:
            node = queue.pop(0)
            result.append(node.val)
            if node.left:
                queue.append(node.left)
            if node.right:
                queue.append(node.right)
        return result

面试题33.二叉搜索树的后序遍历序列

输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。

# -*- coding:utf-8 -*-
class Solution:
    def VerifySquenceOfBST(self, sequence):
        # write code here
        length = len(sequence)
        if not sequence:
            return False
        if length == 1:
            return True
        root = sequence[-1]
        i = 0
        while sequence[i] < root:
            i += 1
        for j in range(i, length - 1):
            if sequence[j] < root:
                return False
        left = sequence[:i]
        right = sequence[i:length - 1]
        leftIs = True
        rightIs = True
        if len(left) > 0:
            leftIs = self.VerifySquenceOfBST(left)
        if len(right) > 0:
            rightIs = self.VerifySquenceOfBST(right)
        return leftIs and rightIs

面试题34.二叉树中和为某一值的路径

输入一颗二叉树的跟节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。
思路:当前节点为叶子节点并和整数值相等时直接输出,当前节点值大于整数值输出空,当前节点值小于整数值,则更新整数值递归下去寻找路径,注意根节点与递归得到的路径如何合并

class Solution:
    # 返回二维列表,内部每个列表表示找到的路径
    def FindPath(self, root, expectNumber):
        # write code here
        if not root or root.val > expectNumber:
            return []
        if root and not root.left and not root.right and root.val == expectNumber:
            return [[root.val]]
        res = []
        left = self.FindPath(root.left, expectNumber - root.val)
        right = self.FindPath(root.right, expectNumber - root.val)
        for i in left + right:
            res.append([root.val] + i)
        return res

面试题35.复杂链表的复制

输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。

class Solution:
    # 返回 RandomListNode
    def Clone(self, pHead):
        # write code here
        if not pHead:
            return None
        #1.把复制的结点链接在原来链表的对应结点之后
        dummy = pHead
        while dummy:
            dummynext = dummy.next 
            copynode = RandomListNode(dummy.label)
            copynode.next = dummynext
            dummy.next = copynode
            dummy = dummynext
        #2.把复制结点的Random指针指向对应结点的Random指针指向的结点
        dummy = pHead
        while dummy:
            if dummy.random:
                dummy.next.random = dummy.random.next
            dummy = dummy.next.next
        #3.把复制之后的结点拿出来组成一个新的链表
        dummy = pHead
        copyHead = dummy.next
        while dummy:
            copyNode = dummy.next
            dummynext = copyNode.next
            dummy.next = dummynext
            if dummynext:
                copyNode.next = dummynext.next
            else:
                copyNode.next = None
            dummy = dummynext
        return copyHead

面试题36二叉搜索树与双向链表

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。

class Solution:
    def Convert(self, root):
        if not root:
            return None
        if not root.left and not root.right:
            return root
        # 将左子树构建成双链表,返回链表头
        left = self.Convert(root.left)
        p = left
        # 定位至左子树的最右的一个结点
        while left and p.right:
            p = p.right
        # 如果左子树不为空,将当前root加到左子树链表
        if left:
            p.right = root
            root.left = p
        # 将右子树构造成双链表,返回链表头
        right = self.Convert(root.right)
        # 如果右子树不为空,将该链表追加到root结点之后
        if right:
            right.left = root
            root.right = right
        return left if left else root

面试题38.字符串的排列

输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。

class Solution:
#回溯算法
    def Permutation(self, ss):
       # write code here
        if not ss:
            return []
        if len(ss) == 1:
            return [ss]
        re = []
        self.helper(re, ss, '')
        return sorted(list(set(re)))
    def helper(self, re, s, tmp):
        if not s:
            re.append(tmp)
        else:
            for i in range(len(s)):
                self.helper(re, s[:i] + s[i + 1:], tmp + s[i])

数组中出现次数超过一半的数字

数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
思路:采用阵地攻守的思想:
第一个数字作为第一个士兵,守阵地;count = 1;
遇到相同元素,count ++;
遇到不相同元素,即为敌人,同归于尽,count --;当遇到count为0的情况,又以新的 i 值作为守阵地的士兵,继续下去,到最后还留在阵地上的士兵,有可能是主元素。
再加一次循环,记录这个士兵的个数看是否大于数组一般即可。

# -*- coding:utf-8 -*-
class Solution:
    def MoreThanHalfNum_Solution(self, numbers):
        # write code here
        if not numbers:
            return 0
        s = 0
        c = 0
        for i in numbers:
            if c == 0:
                s = i
                c += 1
            elif i == s:
                c += 1
            else:
                c -= 1
        if c == 0:
            return 0
        count = 0
        for j in numbers:
            if j == s:
                count += 1
        return s if count > len(numbers) / 2 else 0

面试题60.n个骰子的点数

把n个骰子扔在地上,所有骰子朝上一面的点数之和为s。输入n,打印出s的所有可能的值出现的概率。

def Solution(n):
    dp = [[0 for i in range(6*n)] for i in range(n)]#n * 6n 的矩阵,行代表的是骰子的个数,列代表的是骰子投出的点数之和)
    for i in range(6):
        dp[0][i] = 1
    # print dp
    for i in range(1,n):  #1,相当于2个骰子。
        for j in range(i,6*(i+1)):  #[0,i-1]的时候,频数为0(例如2个骰子不可能投出点数和为1)
            dp[i][j] = dp[i-1][j-6] + dp[i-1][j-5] + dp[i-1][j-4] + dp[i - 1][j - 3] + dp[i-1][j-2] + dp[i-1][j-1]
 
    count = dp[n-1]
    return count / 6 ** n  #算得骰子投出每一个点数的频数。再除以总的排列数即可得到频率

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值