剑指 Offer Python版本 持续更新!!

目录

剑指 Offer 09. 用两个栈实现队列

剑指 Offer 30. 包含min函数的栈

 剑指 Offer 06. 从尾到头打印链表

 剑指 Offer 24. 反转链表

 剑指 Offer 05. 替换空格

 剑指 Offer 58 - II. 左旋转字符串

 剑指 Offer 03. 数组中重复的数字

剑指 Offer 53 - I. 在排序数组中查找数字 I

剑指 Offer 53 - II. 0~n-1中缺失的数字

 剑指 Offer 04. 二维数组中的查找

 剑指 Offer 50. 第一个只出现一次的字符

剑指 Offer 32 - I. 从上到下打印二叉树

剑指 Offer 32 - II. 从上到下打印二叉树 II​​​​​​​

剑指 Offer 09. 用两个栈实现队列

思路

      本道题考查的是队列使用栈去进行表示,我们都知道队列是一种先进先出的数据结构,而栈是一种先进后出的队列结构,所以如果我们想通过栈去实现队列的功能,我们就需要考虑使用两个栈去完成这样的功能。

     也就是说,现在我们输入一串数字:1234,如果我们使用的是一个栈的结构,那么输出结果应该是4321. 但是如果我们使用两个栈,第一个栈输出的结果4321作为第二个栈的输入,那么输入第二个栈的内容为4321,根据先进后出原则,第二个栈的输出为1234。所以通过这样两个栈的形式,我们就完成了队列先进先出的功能,即输入1234,输出也为1234。

      欢迎大家后台评论指正,讨论交流~!

    (这道题数据可视化之后的思路还是比较清晰易懂的,给大家附上一个链接供参考~)

class CQueue:

    def __init__(self):
        self.in_stack = []
        self.out_stack = []
    def appendTail(self, value: int) -> None:
        self.in_stack.append(value)
    def deleteHead(self) -> int:
        if not self.out_stack:
            if not self.in_stack:
                return -1 
            else:
                while self.in_stack:
                    self.out_stack.append(self.in_stack.pop())
                return self.out_stack.pop()  # 需要不断的添加
        else:
            return self.out_stack.pop()

剑指 Offer 30. 包含min函数的栈

思路

      

        Hello大家好,今天我想和大家分享一下LeetCode中剑指offer栈与队列系列的第二题​,包含min函数的栈。

    ​  根据题意我们知道我们要随着数据不断地入栈,出栈,我们要能找到栈中的最小数字,​并且:时间复杂度需要为O(1)。 那就说明往常我们用的遍历栈中的所有数字然后找到最小值的做法在我们这道题目中是不适用的,因为遍历所有内容的时间复杂度为O(N)​。

    ​  所以我想和大家分享一下,使用双栈(辅助栈)的方式去解这道题的思路,并且思路我会在代码里面备注清楚滴!

    ​  首先我们先构造一个双栈,b栈为​辅助栈。b栈的作用是不断保存最小的数字,且只有这一个功能。​模拟过程如下:

    ​   b栈中的栈顶保存着最小的数据,刚开始两个栈内容都是空的,当我们向A栈中添加一个元素的时候,我们都需要将这个元素和B栈栈顶(B.[-1])元素进行比较,如果比B.[-1]元素小的话,那么我们就需要将这个值也加入到B栈当中​。以此类推,B栈栈顶的元素永远都是所有数字里面最小的​。

class MinStack:

    def __init__(self):
        """
        initialize your data structure here.
        """
        # 构造双栈
        self.a = []
        self.b = []


    def push(self, x: int) -> None:
        # 向栈A添加元素,并且和栈B的栈顶元素进行比较
        self.a.append(x)
        if not self.b or self.b[-1] >= x:
            self.b.append(x)

    def pop(self) -> None:
        # 移出栈A的栈顶元素,如果栈A的栈顶元素和栈B的栈顶元素一样,需要移出栈B的元素保持两栈一致性
        if self.a.pop() == self.b[-1]:
            self.b.pop() 

    def top(self) -> int:
        #返回最顶端元素
        return self.a[-1]


    def min(self) -> int:
        # 返回最小元素
        return self.b[-1]


# Your MinStack object will be instantiated and called as such:
# obj = MinStack()
# obj.push(x)
# obj.pop()
# param_3 = obj.top()
# param_4 = obj.min()

 剑指 Offer 06. 从尾到头打印链表

思路

        Hello大家好,今天我想和大家分享一下剑指offer系列的06题,从尾到头打印链表。

 首先我们需要思考的问题是:何为链表?链表的数据结构是怎么样的?

(图片摘自百度) 

        我们可以发现,链表的组成分为保存的数据(值)、以及指向下一个节点的指针。那么根据题目中的例子,head数组为我们每一个节点保存的值,也就是说我们第一个节点保存的值为1,第二个节点保存的值为3,第三个节点保存的值为2,那么我们如何将这样的值反转过来输出呢?大家有没有想到使用呀?因为栈是一种先进后出,也就是进栈和出栈内容自动互为反转的数据结构,所以我们可以尝试使用栈的方式去解一下这个题:

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:
    def reversePrint(self, head: ListNode) -> List[int]:
        # 创建一个栈
        stack = []
        # 如果链表是存在的,也就是存在头节点
        while head:
            stack.append(head.val)
            head = head.next
        # 将栈反转输出,也可以使用 pop将内容输出
        return stack[::-1]

        使用栈的方式非常符合我们的题目要求,当然,我们还有另外一种方式可以去解决这道题,我们一起来看一下使用递归回溯去解决这道题:递归的思路就是将输入的链表的值,也就是132看成是除去第一个值剩下的值(32)加上第一个值,而32也可以看成除去第一个值,也就是2和第一个值(也就是3)相加起来得到的内容。所以如果我们按照这样的思路进行递归的话,那么我们输入132,也可以返回得到:231。具体代码如下:

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def reversePrint(self, head: ListNode) -> List[int]:
        if head:
            return self.reversePrint(head.next) + [head.val]
        return []

        这样也可以解决我们这道题~但是使用递归美中不足的就在于速度还有资源消耗会大于第一种方法,下面是这两种方法运行时间的对比:

首先是递归:

然后是我们的栈方法:

         相比之下栈方法的确是快一些的。那么这道题的分享就到这里啦,如果大家有什么更好的方法,欢迎评论沟通!

 剑指 Offer 24. 反转链表

思路

      Hello 大家好,今天我想和大家分享一下剑指offer的第24题,反转链表,这道题的题目如下图所示。

 

         这道题我们可以发现在题目中我们定义的函数,只要我们给定函数一个头节点,那么我们将整个链表反转,之前在最后的节点成为整个链表的新的头节点。这道题我同样想和大家分享的思路是使用栈去解决这道题~这道题我们可以发现,输入和输出都是链表,所以如果我们想用栈去解决这道题的话,我们可能还得需要去创建一个链表去将倒序过来每一个栈的元素都保存到链表里面,代码和注释如下:

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        stack = []
        # 定义一个新的节点赋值为0 
        link = ListNode(0)
        # 当如果链表存在的话,我们就将链表的每一个值压入栈中
        while head:
            stack.append(head.val)
            head = head.next
        p = link 
        while stack:
        # 新的链表保存每一个从栈中拿出的数字直到遍历完毕
            p.next = ListNode(stack.pop())
            p = p.next
        return link.next

 剑指 Offer 05. 替换空格

思路

     

        Hello 大家猴!今天我想和大家分享一下剑指offer的第05题,这道题是一道非常简单的题目,它是考察字符串的一道题,那废话不多说,现在我和大家快速的分享一下这道题的思路!

题目截图如下:

 

         这道题看到的第一眼我的思路是使用for循环和条件判断的方式去判断,如果字符串中出现了空格,就将空格替换成'%20', 如果遍历到的当前字符不是空格,则输出当前的字符,代码如下:


class Solution:
    def replaceSpace(self, s: str) -> str:
        datalist = []
        for i in s:
            if i == ' ':
                datalist.append("%20")
            else:
                datalist.append(i)
        datalist = ''.join(datalist)
        return datalist

        我在写这道题的时候是将datalist直接定义成了一个列表,所以在for循环遍历字符串的时候,我将遍历到的每一项append到列表之后,再在最后return结果的时候返回datalist的字符串形式。所以使用这种方法去解决这道题的时候,可能唯一碰到的一个小困难就是在将列表转换成字符串的这个过程上。列表转换成字符串只需要通过如下形式:

datalist = ''.join(datalist)

就可以将本来是列表的datalist转换成字符串啦!

好了,这道题我和大家的分享就到这里,我们下题见!

 剑指 Offer 58 - II. 左旋转字符串

思路

     

       Hello 大家好,今天我想和大家分享一下剑指 Offer的第58题左旋转字符串,这道题我想和大家分享两种做题的思路,在分享做题思路之前我们可以先一起看一下这道题的题目:

 

          根据题目可以知道,我们需要将索引为n之前的所有字符内容拼接到整串字符串的后面,所以我们可以采取切片的方式去实现,具体代码如下:


class Solution:
    def reverseLeftWords(self, s: str, n: int) -> str:
        return s[n:]+s[:n]

        在这里需要注意的是切片的索引是左闭右开的,但是看到有些朋友说好像这种解法在正式面试的时候不一定可以用,我们可以创建一个新的列表,然后遍历字符串中索引为n以及n的每一项,将它们一一遍历出来添加到新的列表里面,然后再将前n项添加到后面就可以了,思路也比较简单,代码如下:

class Solution:
    def reverseLeftWords(self, s: str, n: int) -> str:
        new_list = []

        for i in range(n,len(s)):
            new_list.append(s[i])
        for i in range(0,n):
            new_list.append(s[i])
        return ''.join(new_list)

好啦,这道题就和大家分享到这里,下题见!

 剑指 Offer 03. 数组中重复的数字

思路       

         Hello 大家好,今天我想和大家分享一下剑指Offer的第三题,数组中重复的数字,这是一道和数组有关的题目。

        我们先来读一下题目,题目如下图所示:

 

         看到这道题之后我的第一反应就是用字典去解决这道题~记得之前做过一道题目,这也是之前做过的一道题给予我的灵感,大致的思路如下:我们需要遍历我们的数组,将遍历到的数组每一项都保存到我们创建的字典中(放入之前需要判断放入的当前项是否在之前已经放入过了一个和它相同的值,如果判断放入过了一个相同的值,返回当前值就行)。总的来说这道题还是比较简单的,再和大家啰嗦一下就是我们要注意字典的格式,在本到题当中,字典的键保存的是我们遍历到的数组中的每一项,字典的值则是它在原数组中所对应的下标。详细代码如下:

class Solution:
    def findRepeatNumber(self, nums: List[int]) -> int:
        dict = {}
        if nums == []:
            return None 
        for i in range(len(nums)):
            if nums[i] not in dict:
                dict[nums[i]] = i 
            else:
                return nums[i]

        好啦,今天我和大家的分享就到这里,如果大家还有更好的、时间复杂度还有内存更优化的算法,欢迎大家分享在评论区,我们下道题再见!

剑指 Offer 53 - I. 在排序数组中查找数字 I

思路       

        Hello大家好,今天我想和大家分享一下剑指Offer的第53题,在排序数组中查找数字。这道题同样是一道数组排序中的题,下面我想和大家分享一下思路,题目截图如下:

 

          这道题我同样想和大家分享一下使用字典的形式去解这道题,这道和之前我们判断数组是否有重复值的题目不太相同的是这道题有了一个目标值,也就是判断这个目标数字在整个数组里面遍历之后重复了多少次。并且这道题多了一些其他的限制,而这些限制必须是在做题的过程中才能发现的。比如说当原数组nums=[]的话,那么输出的内容为0;当然,我们还要判断target出现的次数,这个时候我们就需要一个计数变量去保存出现的次数了。这道题刚开始的时候我是使用条件判断去写的这道题,所以在没有看到其他输入情况的时候可能会出现报错。我的思路可能会有一点复杂,所以导致时间复杂度有一点大,如果大家有更好的方法欢迎留言给我~

class Solution:
    def search(self, nums: List[int], target: int) -> int:
        dict = {}
        count = 1
        if nums == []:
            return 0

        for i in range(0,len(nums)):
            if nums[i] in dict and nums[i]==target:
                dict[nums[i]] = i
                count += 1
            elif nums[i] in dict:
                dict[nums[i]] = i
            elif target not in nums:
                return 0
            else:
                dict[nums[i]] = i 
        return count

        好啦这道题我和大家就分享到这里,我们下道题再见~

剑指 Offer 53 - II. 0~n-1中缺失的数字

思路         

Hello 大家好

剑指Offer的第53题是找到 0~n-1中缺失的数字,题目的截图如下:

 

        这道题感觉还是有点复杂的,因为刚开始我还是准备用老方法去解决这道题(字典,然后去遍历),但是后来发现这个方法并不可取,因为在最后报错的时候发现还是有一些特殊情况是没有办法兼顾到的。

        下面我和大家分享一下我在评论区看到的一个老哥给的解法,感觉这个思路也是不错的,代码如下:

class Solution:
    def missingNumber(self, nums: List[int]) -> int:
        result = len(nums)

        for i in range(0,len(nums)):
            if i != nums[i]:
                result = i
                break 
        return result 

        思路也不是特别的麻烦,因为数组是排序之后递增的,所以假设数组里面的每一项和数组的下标是一一对应的,所以当下标的值和数组中对应的数不同的时候,我们就可以将当前值返回啦。

 剑指 Offer 04. 二维数组中的查找

思路         

Hello 大家好

        本道题我和大家分享一下剑指Offer的第 04 题, 二维数组中的查找,题目的截图如下:

         这道题我想和大家分享两种解法,第一种解法是暴力遍历遍历二维数组中的每一个值,然后判断我们的目标值target是否在我们的二维数组中,如果目标值在二维数组中,那么我们返回True,这种思路的代码如下:     

class Solution:
    def findNumberIn2DArray(self, matrix: List[List[int]], target: int) -> bool:
        for row in matrix:
            for element in row:
                if element == target:
                    return True 
        return False

           另一种解法是通过二分法查找,因为在每一行中的元素都是按照从小到大依次排列,所以二分法也是我们可以选择的一种方式,代码如下:

class Solution:
    def findNumberIn2DArray(self, matrix: List[List[int]], target: int) -> bool:
        for row in matrix:
            idx = bisect.bisect_left(row,target)
            if idx < len(row) and row[idx] == target:
                return True 
        return False

         好啦,这道题我和大家就分享到这里,当然本道题还有很多别的解法,比如之字形解法等等,都是特别好的方法。当然,如果大家有更好的解法欢迎大家一起分享出来~谢谢大家!

 剑指 Offer 50. 第一个只出现一次的字符

思路         

Hello大家好

    第50题的题目是第一次出现一次的字符,题目截图如下:

         

        这道题同样可以使用我们之前字典键值对的形式去完成这道题。比如我们定义了一个字典 {},那么这个字典的键值对保存的内容为{'字符':'出现的次数'}。那么首先我们需要定义一个空字典,然后遍历字符串的每一项,一一添加进我们的空字典里面。在添加的时候我们需要是使用条件判断去增加我们的计数器,代码如下:

class Solution:
    def firstUniqChar(self, s: str) -> str:
    
        dict = {}
        count = 1

        for i in s:
            if i not in dict:
                dict[i] = count   
            else:
                dict[i] = count + 1
        
        for j in s:
            if dict[j] == 1:
                return j

        return " "

           另外我们也可以使用哈希表的形式去解这道题,我们同样是遍历字符串的每一项,也同样是将它们保存到我们创建的空字典里面,但是不同的是字典里面的值保存的是布尔类型的值,如果遍历到的当前字符串的内容我们判断出来(s in 字典)它已经存在在了我们的字典里面,就给对应的字符在字典中的值赋值为false,最后我们再重新遍历一下,找到第一个值为true的键就可以了,那么哈希表的解决方式如下:

class Solution:
    def firstUniqChar(self, s: str) -> str:
        dic = {}
        for c in s:
            dic[c] = not c in dic
        for c in s:
            if dic[c]: 
                return c
     
        return ' '

         OK! 那关于这道题如果大家还有什么好的思路的话欢迎和我沟通~ 那我们下题再见!

剑指 Offer 32 - I. 从上到下打印二叉树

思路         

Hello 大家好:

这次我想和大家分享一下剑指offer的第32题,从上到下,从左到右打印二叉树,题目的截图如下:

 我们根据题意可以知道,在进行二叉树搜索的时候,搜索的顺序是从上到下,从左至右的。所以我们可以使用队列的形式去模拟我们的二叉树结构,代码如下:

class Solution:
    def levelOrder(self, root: TreeNode) -> List[int]:
        if not root: return []
        res, queue = [], collections.deque()
        queue.append(root)
        while queue:
            node = queue.popleft()
            res.append(node.val)
            if node.left: queue.append(node.left)
            if node.right: queue.append(node.right)
        return res

在这里需要注意的是 collections.deque()是创建一个双向的队列,这种队列结构左右两边都可以增加元素,以及删除元素。我们可以把树的根节点作为第一个元素传入我们的队列,然后通过node.val的形式将我们子节点的值保存至我们的结果集中。

        node.left和node.right分别保存的是我们树结构的左右分支上面的值,如果左右子节点的值为null的话,那我们就直接跳过就可以了。

        好啦,今天和大家分享的内容就到这里了,我们下道题目再见。

剑指 Offer 32 - II. 从上到下打印二叉树 II

思路         

Hello 大家好:

        今天我想和大家分享一下剑指Offer的第32题,这道题和上一道和大家分享的题目比较相似,但是这次的结果并不是返回从上到下,从左到右的每一个值了,而是每一行返回一个列表,最后再将结果返回回来。

        题目的截图如下:

         和昨天的做法类似,我们仍然使用双队列的形式,然后将树传入我们创建的双向队列中,向左pop的每一个值(第一个是根节点)作为每一个结点,然后判断每一个结点是否还有左右节点值,然后将左右节点值保存到我们的队列中就可以了。这道题的精髓在于使用for循环遍历每一行,然后将每一行的值保存到结果集中就可以了,代码如下:

class Solution:
    def levelOrder(self, root: TreeNode) -> List[List[int]]:
        if not root:
            return []
        res,queue = [],collections.deque()
        queue.append(root)
        while queue:
            temp = []
            for _ in range(len(queue)):
                node = queue.popleft()
                temp.append(node.val)
                if node.left:
                    queue.append(node.left)
                if node.right:
                    queue.append(node.right)
            res.append(temp)
        return res

   好啦,这道题就和大家分享到这里,我们下题再见!拜拜

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值