算法1

作者: _有马_ | 来源:发表于2017-04-04 23:16 被阅读0次

    这里是我算法练习的一些例子,当作思维训练,题目来主要来自自剑指offer,我用python作为实现语言,个别可能没有测试完整的,欢迎通过简书交流

    二维数组查找

    描述:在二维数组中,每一行按照从左到右递增,每一列按照从上到下递增,输入一个这样的二维数组(1),和一个整数7,找到这个整数返回 True, 否则 False

    1 2 8 9

    2 4 9 12

    4 7 10 13

    思路1: 最原始的想法当然是遍历所有的元素,复杂度是 O(n^2)

    思路2: 选取右上角的数字(其实左下角也可以),然后比较与数字7的大小,如果等于,查找过程结束;如果大于,就删除整个列;如果小于,就删除整个行, 复杂度O(n)$

    简洁易懂,下面是我的python实现:

    def find_num(matrix, num):
        ct = 0
        for cols in matrix[::-1]:
            for col in cols[ct:]:
                if col == num:
                    return True
                elif col > num:
                    break  # 删除一列
                else:
                    ct += 1  # 删除一行
                    continue
    
    # test
    if __name__ == "__main__":
        m = [[1, 2, 3, 6],[2, 4, 7, 8],[8, 9, 10, 11],[9, 12, 13, 15]]
        n = 7
        find_num(m, n)
    

    从尾到头打印链表

    链表实现:使用tuple表示一个Node

    思路1: 遍历链表,存到栈里面,然后输出栈, 但其实我们不用手动实现栈,直接用递归函数就可以了

    貌似过于简单了:)

    def print_rlt(lt):  # lt: linked_table
        if lt[1] is not None:
            print_rlt(lt[1])
        print lt[0]
    
    
    # test
    if __name__ == "__main__":
        # a node => (val, next) 
        linked_table = (1, (2, (3, (4, None))))
        print_rlt(linked_table)
    
    

    思路2:上面的算法可以进行优化,递归函数在数据量大的时候会导致栈溢出, 可以用循环代替,或者尾递归优化(就是保证函数的最后一步是调用自身),但是python不支持尾递归优化,而且永远不会支持,java也是不支持的,尾递归优化貌似只在函数式语言中支持的比较好,但神奇的是es6在开启严格模式下是支持的

    重建二叉树

    描述:根据二叉树的前序遍历,中序遍历 重建整个二叉树

    思路:还是递归,前序的第一个元素就是root, 根据root我们可以找到中序左子树, 中序右子树;然后找到前序左子树,前序右子树;递归找下去… 还是看代码吧

    def rebuild(preorder, inorder):
        if not preorder:
            return None
        root_val = preorder[0]
        root_index = inorder.index(root_val)
    
        inorder_left = inorder[:root_index]
        inorder_right = inorder[root_index + 1:]
    
        preorder_left = preorder[1:len(inorder_left) + 1]
        preorder_right = preorder[len(inorder_left) + 1:]
    
        left = rebuild(preorder_left, inorder_left)   # 递归构建左子树
        right = rebuild(preorder_right, inorder_right)  # 递归构建右子树
        root = (left, root_val, right)
        return root
    
    
    # test
    if __name__ == "__main__":
        # a node => (left, val, right)
        a = [1, 2, 4, 7, 3, 5, 6, 8]
        b = [4, 7, 2, 1, 5, 3, 8, 6]
        print rebuild(a, b)
    

    用两个栈实现队列

    描述:用两个栈实现一个队列

    思路:维护两个栈,栈1负责插入操作,栈2负责删除操作,当栈2为空时,将栈1的元素都推到栈2里面

    class Queue(object):
        stack1 = []
        stack2 = []
    
        def append_tail(self, val):
            self.stack1.append(val)
    
        def delete_head(self):
            if not self.stack2:
                while self.stack1:
                    val = self.stack1.pop()
                    self.stack2.append(val)
            if not self.stack2:
                raise IndexError("empty queue")
            self.stack2.pop()
    
    
    # test
    if __name__ == "__main__":
        # stack => []
        q = Queue()
        q.append_tail(1)
        q.append_tail(2)
        q.append_tail(3)
        q.delete_head()
        q.append_tail(4)
        q.delete_head()
        q.delete_head()
        q.delete_head()
    
        q.delete_head()
    

    旋转数组的最小数

    描述:旋转数组([1,2,3,4,5,6,7] => [4,5,6,7, 1,2,3]) 把一个有序数组的部分最小值放到后面,我们的要做的就是根据这个旋转数组找出最小值 1

    思路1:从头遍历,复杂度为$O(n)$

    思路2: 类似二分查找,根据题意旋转数组的特性(最大值和最小值靠在一起的,就像这种走势 /\ ),定义两个索引,让这索引1不停靠近最大值,索引2不停靠近最小值

    def min_num(arr):
        first = 0
        end = len(arr) - 1
    
        while first != end - 1:
            mid = (end + first) / 2
            if arr[mid] > arr[first]:
                first = mid
            else:
                end = mid
    
        return arr[end]
    
    # test
    if __name__ == "__main__":
        print min_num([4, 5, 6, 7, 1, 2, 3])
        # ==> 1
    

    斐波那契数

    描述:略
    思路1:就是递归了喽,简单清晰,不过效率很低 $O(2^n)$,

    def fib(num):
        if num == 0:
            return 0
        elif num == 1:
            return 1
        else:
            return fib(num - 1) + fib(num - 2)
    
    
    # test
    if __name__ == "__main__":
        print fib(0)
        print fib(1)
        print fib(2)
        print fib(3)
        print fib(10)
        print fib(100) # 下午吃饭回来还没完,放弃了。。。
    

    思路2: 用循环代替, $O(n)$

    def fib2(n):
        if n < 2:
            return n
    
        fib_n = 0
    
        fib_one = 0
        fib_two = 1
        for i in range(n):
            fib_n = fib_one + fib_two
            fib_one = fib_two
            fib_two = fib_n
    
        return fib_n
    
    
    # test
    if __name__ == "__main__":
        import time
    
        b = int(time.time() * 1000000)
        fib2(32)
        print int(time.time() * 1000000) - b
        #=> 77
    

    二进制中的1的个数

    描述: 计算一个整数的二进制中的1的个数

    思路1: 判断最右一位是不是,记录,右移一位;...

    def count_one(n):
        count = 0
        while n:
            if n & 1:
                count += 1
            n >>= 1
        return count
    
    
    
    # test
    if __name__ == "__main__":
        print count_one(1)  # => 1
        print count_one(7346273462734697342374629346234)  # => 53
    

    思路2: 上面的算法不能判断负数的,因为负数右移,左边补的是1,所以陷入死循环

    我们可以不右移n,而是向左移动1(flag)

    def count_one_2(n):
        count = 0
    
        flag = 1
    
        for i in xrange(64):  因为python的整数没有位数限制,我们自定义自己机器的位数
            if n & flag:
                count += 1
            flag <<= 1
    
        return count
    
    
    # test
    if __name__ == "__main__":
        print count_one_2(1)  #==>
        print count_one_2(-1)  #== 64
        print count_one_2(-10)  #==> 62
    

    思路3: 主要是找到一个这样的规律 n & (n-1) = 这个数的最右一位1变成变成0, 举个例子:

    1100 & (1100 - 1) = 1000; 看到了吗,1100 => 1000,基于此的算法

    def count_one_3(n):
        count = 0
    
        while n:
            count += 1
            n &= (n - 1)
    
        return count
    
    
    # test
    if __name__ == "__main__":
        print count_one_3(1)
        print count_one_3(10) 
        # print count_one_3(-1)
        # print count_one_3(-10)  # 只能判断正数
    

    相关文章

      网友评论

          本文标题:算法1

          本文链接:https://www.haomeiwen.com/subject/tvrfottx.html