算法练习 - 栈 - 1

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_28057541/article/details/72627665

算法练习 - 栈


关于栈的使用,难度从简单到难

练习1LeetCode - 496. Next Greater Element I

class Solution(object):
    # 输入:两个无重复的数列nums1和nums2,其中nums1是nums2的一个子集。找出所有的nums中元素之后行下一个比其大的元素在nums2中的位置
    # 输出:下一个较大位置的数列

    # nums1 和 nums2 中的所有数都是独一无二的
    # nums1 和 nums2 的大小不超过1000

    # 如果暴力的做,就是遍历nums1然后在nums2中去找对应的比它大一位数的位置,时间复杂度O(n2)
    # nums1 和 nums2 中的所有数都是独一无二的,那么就说明 (数-位置) 形成了映射关系,这种映射关系可以在线性时间内完成
    # 找到每个数的下一个大的数的序列,也就等于找到了位置序列
    def nextGreaterElement(self, findNums, nums):
        """
        :type findNums: List[int]
        :type nums: List[int]
        :rtype: List[int]
        """

        # 设 d_next 为nums中数对应下一大数的映射关系
        d = {}
        # 设 s 为一个空的栈
        s = []
        # 遍历 nums,当前元素为x:
        for x in nums:
        #     如果 s 不为空 且 栈顶 < x,在d中添加对应的映射关系,并将当前栈顶数出栈:
            while len(s) and s[-1] < x:
                d[s.pop()] = x
        #      否则,继续入栈
            s.append(x)
        # 设 ans 为返回序列
        ans=[]
        # 遍历 findnums ,x:
        for x in findNums:
        #     在d中查找对应的x是否存在映射关系:
        #         如果存在:
        #             ans中添加当前x的对应的下一大
        #         不存在:
        #             ans中添加-1
            ans.append(d.get(x,-1))        
        # 返回ans
        return ans

练习2 LeetCode - 173. Binary Search Tree Iterator

class BSTIterator(object):

    def __init__(self, root):
        """
        :type root: TreeNode
        """
        # 用栈来初始化
        self.s = []
        curNode = root
        while curNode:
            self.s.append(curNode)
            curNode = curNode.left

    # 判断是否存在下一个最小值
    def hasNext(self):
        """
        :rtype: bool
        """
        # 如果s不为空,则返回栈顶
        if len(self.s):
            return True
        else:
            return False

    # next 返回的是下一个最小值,也就是当前结点的左子节点
    def next(self):
        """
        :rtype: int
        """
        curTop = self.s.pop()
        # 将下一个大小的入栈 
        if curTop.right:
            if curTop.right.left:
                temp = curTop.right
                while temp:
                    self.s.append(temp)
                    temp = temp.left
            else:
                self.s.append(curTop.right)
        return curTop.val

练习3 LeetCode - 341. Flatten Nested List Iterator

class NestedIterator(object):
    # 不能直接遍历的原因,可能嵌套很多层,如果每次都指定一个新的指针来控制list的迭代,而且你不知道到底深度为多少,算法复杂度会很高
    def __init__(self, nestedList):
        """
        Initialize your data structure here.
        :type nestedList: List[NestedInteger]
        """
        # 首先逆序入栈
        self.list = []
        self.ans = []
        for i in nestedList:
            self.list.append(i)
        # 利用栈的特性将数列展开
        # 如果说队列为空,那么直接返回
        if not len(self.list):
            return
        # 否则,将当前栈顶展开入栈
        while len(self.list):
            # 如果栈顶元素存在且不是数字,而是数列
            curNested = self.list.pop()                
            if curNested and (not curNested.isInteger()):
                # 那么将数列展开,逆序入栈
                for i in curNested.getList():
                    self.list.append(i)
            # 如果栈顶元素是数字,将栈顶元素的值输入一个list
            else:
                self.ans.append(curNested.getInteger())
    def next(self):
        """
        :rtype: int
        """
        return self.ans.pop()

    def hasNext(self):
        """
        :rtype: bool
        """
        if len(self.ans):
            return True
        else:
            return False

我的方法是先全部展开,参考其他的答案,也每次获取下一个的时候展开,这样可以节约一定的空间,不过时间上的消耗也更多了

class NestedIterator(object):

    # 不能直接遍历的原因,可能嵌套很多层,如果每次都指定一个新的指针来控制list的迭代,而且你不知道到底深度为多少,算法复杂度会很高
    def __init__(self, nestedList):
        """
        Initialize your data structure here.
        :type nestedList: List[NestedInteger]
        """
        # 首先逆序入栈
        self.list = []
        for i in reversed(nestedList):
            self.list.append(i)

    def next(self):
        """
        :rtype: int
        """
        return self.list.pop()

    def hasNext(self):
        """
        :rtype: bool
        """
        # 当 栈栈元素为一个数字 的时候,才能返回true
        while len(self.list):
            # cur 为 栈顶元素
            cur = self.list[-1]
            # 如果栈顶元素为一个数字,返回返回真
            if cur.isInteger():
                return True
            # 否则 则说明当前栈顶为一个数列,展开数列
            else:
                # pop出当前的cur
                cur = self.list.pop()
                # 并且逆序的填入栈中
                for i in reversed(cur.getList()):
                    self.list.append(i)
                # 然后继续循环
                continue
        # 遍历到栈空,都没有数字,那就返回空
        return False

练习4LeetCode - 331. Verify Preorder Serialization of a Binary Tree

class Solution(object):

    # 使用先序遍历来序列化一个二叉树,遇数计数,无数记 #
    # 判断给定的一个字符串,判断是否是一个二叉树先序序列化后的

    def isValidSerialization(self, preorder):
        """
        :type preorder: str
        :rtype: bool
        """
        # 先序遍历的特点是 (自身 - 左 - 右)
        # 设 s 为一个栈
        s = []
        # 设 i 为 0
        i = 0
        # 将preorder转化为一个字符的list
        preorder = preorder.split(',')
        # 将 preorder第i个元素 入栈
        s.append(preorder[i])
        # 如果i小于preorder的size,则循环:
        for i in range(1,len(preorder)):
        #     判断不符合的情况,也就是按照出入栈规则无法继续的情况:
        #       当栈为空的时候,i 还未到 preorder 的边界
        #     这种情况下,返回false
            if len(s) == 0:
                return False
        #     如果 当前栈顶 为空 且 如果 preorder[i] 也是空,那么:
            if len(s) == 1 and s[-1] == '#':
                return False
            if preorder[i] == '#':
        #         将当前的栈顶元素pop出,然后将新的栈顶元素也pop出
                while len(s) and s[-1] == '#':
                    s.pop()
                    s.pop()
            #         并且将 空 入栈
                s.append('#')          
        #     否则:
            else:
        #         将preorder[i]入栈
                s.append(preorder[i])
        #     如果此时栈不为空,则返回false,否则返回true
        if len(s) > 1 or s[-1] != '#':
            return False
        else:
            return True

练习5 LeetCode - 503. Next Greater Element II

class Solution(object):
    """
    同样是找下一个大的数字,不过这次是一个循环列表
    那么只有数列中最大的数(或者同样大小的几个数)为-1,其他的所有数都肯定是有一个比它大的数
    并且最大数max之前的所有数肯定在一次循环之内可以找到下一个最大数,或者说在遍历到达max就可以找到下一个最大数
    而max之后的数,第二遍循环到max的时候,也可以全部找到下一个最大数
    也就是当最大的数只有一个的时候,遍历最多两遍可以找到所有下一大

    当有多个最大值的时候呢?
    想想数列被切分成了好几份,每两个最大值之间的数,一定可以找到最大值,而又是循环的,所以最后一个最大值之后的数字,都可以在第二次遍历到
    第一个max之前找到

    最重要的是如何判断要跳出循环,特别在多个最大值的时候

    直观的来收,是在第二次遍历到最大值的时候,所以应该先用O(n)的时间找到最大值
    然后记录遍历的次数,当遍历到第二遍,且已经为最大值的时候,跳出循环

    应为存在相同的数不同的位置,所以这次要使用 (位置-下一大)的,也就是说需要一个值来保存当前栈顶的位置,然后每次找到直接在数组中表示出来
    """
    def nextGreaterElements(self, nums):
        """
        :type nums: List[int]
        :rtype: List[int]
        """
        # 如果数列的大小为0,返回空
        if len(nums) == 0:
            return []
        # 如果数列的大小为1,那么直接返回[-1]
        if len(nums) == 1:
            return [-1]
        # 遍历一次数组,找到最大值 max_nums
        max_nums = max(nums)
        # 设 c = 1 为循环的次数
        c = 1
        # 设 s_nums 为一个用于保存数字栈
        s_nums = []
        # 设 s_index 为一个用于保存对应数字位置的栈
        s_index = []
        # 设 ans 为最后的输出,一个所有元素都是-1的,长度和nums相同的数列
        ans = [-1]*len(nums)
        # 设 i = 0
        i = 0
        # 遍历 nums,指针为i:
        while True:

        #     如果当前栈为空:
            if len(s_nums) == 0:
        #         s_nums,s_index直接入栈nums[i]
                s_nums.append(nums[i])
                s_index.append(i)
                i+=1
        #     否则:
            elif s_nums[-1] == nums[i] and s_nums[-1] == max_nums: 
                i+=1
            else:
        #         top 为栈顶
                top = s_nums[-1]
        #         如果 top >= nums[i]:
                if top >= nums[i]:
        #             s_nums 入栈 nums[i]
                    s_nums.append(nums[i])
        #             s_index 入栈 i
                    s_index.append(i)
        #             i += 1
                    i += 1
        #         如果 top < nums[i]:
                else:
        #             s_nums,s_index出栈当前元素 top ,top_index
                    s_nums.pop()
        #             ans中将 top_index 设为 nums[i]
                    ans[s_index.pop()] = nums[i]

        #     如果 i = len(nums):
            if i == len(nums):
        #         i = 0,回到起始点
        #         c = 2
                i,c = 0,2
        #     如果 c = 2 且 nums[i] == max_nums 且 最终的栈内应该只包含最大值一个数
            if c == 2 and nums[i] == max_nums and len(s_nums) == 1:
        #         跳出
                break
        # 返回 ans
        return ans

大神的算法瞻仰,逻辑清晰,条例分明,说明还得练啊:

def nextGreaterElements(self, nums):
        # 同样是一个栈和一个等长的res列表
        stack, res = [], [-1] * len(nums)
        # 循环了两遍,同时,简单的循环两遍,节省了很多逻辑
        for i in range(len(nums)) * 2:
            # 如果 stack 为空,且 栈顶元素 的 大小 小于 nums【i】,则出栈
            while stack and (nums[stack[-1]] < nums[i]):
                res[stack.pop()] = nums[i]
            # 它只用栈保存了位置index,然后通过nums[index]来获取对应的数,很聪明
            stack.append(i)
            # 最后栈中应该全是最大的那个数
        return res

猜你喜欢

转载自blog.csdn.net/qq_28057541/article/details/72627665
今日推荐