算法1

2017-04-04  本文已影响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)  # 只能判断正数
上一篇下一篇

猜你喜欢

热点阅读