算法练习

来源:互联网 发布:网络写手已成高危职业 编辑:程序博客网 时间:2024/05/22 03:51

算法练习 - 栈


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

练习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