算法(17)-leetcode-剑指offer1


本系列博文为题库刷题笔记,(仅在督促自己刷题)如有不详之处,请参考leetcode官网:https://leetcode-cn.com/problemset/lcof/

编程语言为python

1.面试题3-数组中的重复数字

在一个长度为n的数组里,所有的数字都在0-n-1的范围内,数组中某些数字是重复了的,但是不知道有几个数字是重复了的,也不知道每个数组的重复次数。请找出数组中任意一个重复的数字:
hash表存储遍历过的数字,如果表中已经存在,直接返回。

class Solution(object):
    def findRepeatNumber(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        vis_has={}
        for val in nums:
            if vis_has.get(val):   #访问字典的键值都用这个方法
                return val
            else:
                vis_has[val]=True

leetcode 287.寻找重复数字
给定一个包含n+1个整数的数组,其中数字都在1-n之间,可知至少存在一个重复数字。假设只存在一个重复整数,找出这个重复的数。
要求:
不能更改原数组(假设数组是只读的)。
只能使用额外的 O(1) 的空间。–不能用hash表
时间复杂度小于 O(n2) 。–暴力法的时间复杂度是o(n2)
数组中只有一个重复的数字,但它可能不止重复出现一次。
–弗洛伊德兔子乌龟。如果有重复数字,那么就会有至少两个索引指向相同的数字,这就是链表中成环的条件。检验链表入环节点就是重复的数字。
vi=nums[i],指向下一个索引

class Solution(object):
    def findDuplicate(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        t=nums[0]
        r=nums[0]
        while True:
            t=nums[t]
            r=nums[nums[r]]
            if t==r:
                break
        r=nums[0]
        while t!=r:
            t=nums[t]
            r=nums[r]
        return t

2.面试题04-二维数组中的查找

在一个nm的二维数组中,每一行都按照从左往右递增的顺序,每一行都按从左到右递增顺序排序,每一列都按照从上到下递增顺序排列,完成在二维数组中查找指定数,在返回true,不在返回false.(与leetcode240一致)
思路1:暴力搜索,忽略有序条件,时间复杂度o(n
m)
思路2:线性查找,从数组的右上角开始搜索:

  • nums[0][n-1]==target: return true
  • nums[0][n-1]<target: 去更大的区域查找,行坐标加1,
  • nums[0][n-1]>taget: 去更小的区域查找,列坐标减1
    直至行列索引不符合条件,时间复杂度o(n+m)
class Solution(object):
    def findNumberIn2DArray(self, matrix, target):
        """
        :type matrix: List[List[int]]
        :type target: int
        :rtype: bool
        """
        m=len(matrix)
        if m==0:
            return False
        n=len(matrix[0])
        i,j=0,n-1
        while(-1<i<m and -1<j<n):
            if matrix[i][j]==target:
                return True
            elif matrix[i][j]<target:
                i+=1
            else:
                j-=1
        return False

3.面试题05-替换空格

请实现一个函数,把字符串中的空格替换成“%20”。
考察的是字符串可变不可变,转义字符的输出?
直接想法:新建一个res_str,遍历原字符串,按要求复制一份。–python操作十分方便

class Solution(object):
    def replaceSpace(self, s):
        """
        :type s: str
        :rtype: str
        """
        res_str=""
        for char in s:
            if char==" ":
                res_str+="%20"
            else:
                res_str+=char
        return res_str

4.面试题06-从尾到头打印链表

输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。
思路1:先将链表反转,然后打印输出就可以了。

class Solution(object):
    def reversePrint(self, head):
        """
        :type head: ListNode
        :rtype: List[int]
        """
        if head==None:
            return []
        pre_node=None
        cur_node=head
        while(cur_node):
            next_node=cur_node.next
            cur_node.next=pre_node
            pre_node=cur_node
            cur_node=next_node
        res=[]
        new_node=pre_node
        while(new_node):
            res.append(new_node.val)
            new_node=new_node.next
        return res

思路2:将链表正序输出,将结果res逆序输出就好。

class Solution(object):
    def reversePrint(self, head):
        """
        :type head: ListNode
        :rtype: List[int]
        """
        if head==None:
            return []
        res=[]
        cur_node=head
        while(cur_node):
            res.append(cur_node.val)
            cur_node=cur_node.next
        return res[::-1]

5.面试题07-重建二叉树

leetcode 105:从前序与中序遍历序列构二叉树

6.面试题09-两个堆栈实现队列

用两个堆栈实现一个队列。请实现它的两个函数appendTail和deleteHead,分别完成在队列尾部插入整数和在队列头部删除整数(若队列中没有元素,deleteHead操作返回-1)

堆栈先进后出,队列先进先出

方案1:
两个栈
主栈:存队列。需要添加元素时,将原有的所有元素押入辅助栈,将元素押入栈底,然后将辅助栈的元素押回主栈。删除元素,直接弹出栈顶元素
辅助栈:辅助主栈实现存元素操作,这个思路主要保证:先存入的元素在栈顶。

class CQueue(object):

    def __init__(self):
        self.main_stack=[]
        self.help_stack=[]
    def appendTail(self, value):
        """
        :type value: int
        :rtype: None
        """
        if self.main_stack==[]:
            self.main_stack.append(value)
        else:
            while(self.main_stack):
                self.help_stack.append(self.main_stack.pop())
            self.main_stack.append(value)
            while(self.help_stack):
                self.main_stack.append(self.help_stack.pop())
    def deleteHead(self):
        """
        :rtype: int
        """
        if self.main_stack==[]:
            return -1
        return self.main_stack.pop()

4024ms 17.3MB

方案1主要费时在添加一个新元素的时候,需要移动主栈内的所有元素,然后将所有元素押回来,保证最先存入的元素在主栈顶。
改进方案:
插入元素:维护一个主堆栈,每次加元素,往堆栈顶添加元素就可以了
删除元素:维护一个辅助堆栈,需要弹出元素时将主栈的元素弹入辅助栈,那么第一个入主栈的元素将出现在辅助栈顶,直接将其弹出。重点:此时不需要将剩余元素弹回主栈,因为下一次删除操作对象还是在辅助栈栈顶。所以弹出操作:检查辅助栈是否为空,为空将主栈中元素弹入辅助栈中,弹出辅助栈栈顶即可。时间快了3倍数。

class CQueue(object):
    def __init__(self):
        self.main_stack=[]
        self.help_stack=[]
    def appendTail(self, value):
        """
        :type value: int
        :rtype: None
        """
        self.main_stack.append(value)
    def deleteHead(self):
        """
        :rtype: int
        """
        if self.main_stack==[] and self.help_stack==[]:
            return -1
        if self.help_stack==[]:
            while(self.main_stack):
                self.help_stack.append(self.main_stack.pop())
        return self.help_stack.pop()

1816ms 17.4MB

7.面试题10-1-斐波那契数列

写一个函数,求输入n的斐波那契数列的第n项。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

class Solution(object):
    def fib(self, n):
        """
        :type n: int
        :rtype: int
        """
        if n==0:
            return 0
        if n==1:
            return 1
        prepre_f=0
        pre_f=1
        f=0
        for i in range(2,n+1):   
            f=pre_f+prepre_f
            prepre_f=pre_f
            pre_f=f
        return f%(10**9+7)

循环求余法用于解决大数越界问题,python可以不用考虑大数越界问题。

8.面试题10-2-青蛙跳台阶问题

一只青蛙一次可以跳一阶台阶,也可以跳2阶台阶。求该青蛙跳上一个n阶台阶总共有多少总跳法。

答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

f(i)表示跳到第i个台阶的方法数,它可以由i-1个台阶跳上来,也可以从i-2个台阶跳上来。所以这两种方式所拥有的方法数相加就是跳上第i阶台阶有的方法数,即 f ( i ) = f ( i − 1 ) + f ( i − 2 ) f(i)=f(i-1)+f(i-2) f(i)=f(i1)+f(i2)。看到这个表达式,不就是斐波那契数列么?再求一次斐波那契数列就好了,唯一的区别就是初始条件prepre_f应该初始化为1,pre_f初始化为1。

class Solution(object):
    def numWays(self, n):
        """
        :type n: int
        :rtype: int
        """
        if n==0:
            return 1
        if n==1:
            return 1
        prepre_f=1
        pre_f=1
        for i in range(2,n+1):
            f=pre_f+prepre_f
            prepre_f=pre_f
            pre_f=f
        return f%(10**9+7)

9.面试题11-旋转数组的最小数字

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。

思路1:暴力找一个数组最小o(n)

class Solution(object):
    def minArray(self, numbers):
        """
        :type numbers: List[int]
        :rtype: int
        """
        n=len(numbers)
        res=float("INF")
        for val in numbers:
            res=min(res,val)
        return res

思路2:二分查找,初始化l=0,r=len(numbers),用中点mid=(l+r)//2将数组分成两个部分。判断是否两边都有序:旋转点在无序的那一边

class Solution(object):
    def minArray(self, numbers):
        """
        :type numbers: List[int]
        :rtype: int
        """
        n=len(numbers)
        if n==0:
            return None
        l,r=0,n-1
        while(l<r):
            mid=(l+r)//2
            if numbers[mid]>numbers[r]:  # 右边无序,旋转点在右边,numbers[mid]比右端点都大,最小肯定不是它
                l=mid+1
            elif numbers[mid]<numbers[r]: # 右边有序,最小可能是numbers[mid]
                r=mid
            else:
                r-=1
        return numbers[r]

10.面试题12-矩阵中的路径

请设计一个函数,用来判断在一个矩阵是否存在一条包含某字符串所有字符的路径。路径可以总矩阵的任意一个格子开始,每一步可以在矩阵中向左、向右、向上、向下移动一格。如果一条路径经过了某一格子,那么该路径不能再次进入该格子。

dfs递归遍历。以每个位子开始,寻找该匹配的字符串。有一个visted矩阵用于记录哪些格子已经遍历过了:
保护现场和恢复现场的问题

class Solution(object):
    def exist(self, board, word):
        """
        :type board: List[List[str]]
        :type word: str
        :rtype: bool
        """
        m=len(board)
        if m==0:
            return False
        n=len(board[0])
        visted=[]
        visted=[[False]*n for _ in range(m)]
        def dfs(string,i,j):
            if len(string)==1 and string[0]==board[i][j]:
                return True
            if string[0]==board[i][j]:
                visted[i][j]=True
                if i-1>-1 and visted[i-1][j]==False:
                    if  dfs(string[1:],i-1,j):
                        return True
                if i+1<m  and visted[i+1][j]==False:
                    if dfs(string[1:],i+1,j):
                         return True
                if j-1>-1 and visted[i][j-1]==False:
                    if dfs(string[1:],i,j-1):
                        return True
                if j+1<n and visted[i][j+1]==False:
                    if dfs(string[1:],i,j+1):
                        return True
                visted[i][j]=False
            return False
        for i in range(m):
            for j in range(n):
                if dfs(word,i,j):
                    return True
        return False
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值