Python算法(部分)

359 阅读8分钟

1 算法合集


import random


# 1 斐波拉契数列
def fib(k):
    if k in [0, 1]:
        return 1
    a, b = 1, 1
    for _ in range(3, k + 1):
        a, b = b, a + b
    return b


print(fib(3))


# 2 二分查找
def two_search(data_list, target):
    left = 0
    right = len(data_list) - 1

    n = 0
    while left <= right:
        n += 1
        mid = int((left + right) / 2)
        if data_list[mid] == target:
            print("找到了,执行了{}次".format(n))
            return
        elif data_list[mid] < target:
            left = mid + 1
        elif data_list[mid] > target:
            right = mid - 1
    print("没有在列表中找到")


# 3 冒泡排序
def pop_sort(data):
    for i in range(len(data)):
        for j in range(i + 1, len(data)):
            if data[i] > data[j]:
                data[i], data[j] = data[j], data[i]
    return data


# 4 快速排序
def quick_sort(data):
    if data == []:
        return []
    else:
        q_first = data[0]
        q_less = quick_sort([i for i in data[1:] if i < q_first])
        q_more = quick_sort([i for i in data[1:] if i >= q_first])
        return q_less + [q_first] + q_more


# 5 选择排序
def select_sort(data):
    for i in range(len(data)):
        x = i
        for j in range(i, len(data)):
            if data[x] > data[j]:
                x = j
        data[i], data[x] = data[x], data[i]
    return data


# 6 希尔(插入)排序

def insert_sort(data):
    for i in range(1, len(data)):
        x = i
        while (x > 0 and data[x] < data[x - 1]):
            data[x], data[x - 1] = data[x - 1], data[x]

            x -= 1
    return data

2-1 什么是递归

  1. 递归不是算法,但是用途很大
  2. 递归的定义:在函数内部调用函数本身
  3. 递归这种技术在很多算法中都有存在:回溯法,动态规划,分治法等
  4. 递归分为两个过程:递,归,这些都是自动完成的
  5. 递归一定要终止,怎么写终止条件很重要

2-2 :使用递归实现斐波拉契数列

def fib_test(k):
    """课程中"""
    if k in [1, 2]: return 1
    return fib_test(k - 2) + fib_test(k - 1)


class Solution:
    """
    letcode中
    https://leetcode-cn.com/problems/fei-bo-na-qi-shu-lie-lcof/submissions/
    """

    def __init__(self):
        self.d = {}

    def fib(self, k: int) -> int:
        if k <= 0: return 0
        if k == 1: return 1
        if f"f_{k - 2}" not in self.d:
            f_2 = self.fib(k - 2)
            self.d[f"f_{k - 2}"] = f_2
        else:
            f_2 = self.d[f"f_{k - 2}"]

        if f"f_{k - 1}" not in self.d:
            f_1 = self.fib(k - 1)
            self.d[f"f_{k - 1}"] = f_1
        else:
            f_1 = self.d[f"f_{k - 1}"]
        # else:
        #     return self.fib(k-2)+self.fib(k-1)
        return (f_2 + f_1) % 1000000007



def fib(self, n: int):
    a, b = 0, 1
    for _ in range(n):
        a, b = b, a + b
    return a % 1000000007


if __name__ == '__main__':
    d = {}
    # s = fib_test(3)
    # print(s)
    # print(l)
    # s = Solution()
    # print(s.fib(1000000008))

2-3 非递归方式实现斐波拉契数列

def fib_for(n):
    if n <= 0: return 0
    n_1, n_2 = 1, 0
    for _ in range(n):
        n_2, n_1 = n_1, n_1 + n_2
    return n_2 % 1000000007

print(fib_for(10))

使用递归往往很耗时,因为重复计算的次数很多

2-4 斐波拉契数列的类似题目

"""
爬樓梯問題分析
一阶 1
二街 2
三阶 3
四阶 5
五阶 8


"""

def pa_lou_ti(n):
    f_1 = 1
    f_2 = 2
    if n == 1: return 1
    if n == 2: return 2
    for _ in range(3,n):
        f_1,f_2 = f_2,f_1+f_2

    return f_2


"""
有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少(单位:对)
第一个月 1 (1小 0 中 0 大)
第二个月 1 (0小 1 中 0 大)
第三个月 2 (1小 0 中 1 大)
第四个月 3 (1小 1 中 1 大)
第五个月 5 (2小 1 中 2 大)
第六个月 8 (3小 2 中 3 大)
所以是一个典型的斐波拉契数列问题
"""

def accrue_rabbit(n):
    n_1 = 1
    n_2 = 1
    if n == 1: return 1
    if n == 2: return 2
    for _ in range(2,n):
        n_1,n_2 = n_2,n_1+n_2

    return n_2

2-5 循环和递归实现二分查找

二分查找就是限定一个查找值,看这个值是否在有顺序的列表中,将列表分为两部分,将左边的和右边的列表分别和该数值比较,若该数值在某一半中,便在次在新的列表中重复上述过程

# data = [1,7,18,18]
import random


def get_random_list(n=20):
    """获得一个n位的从小打到的随机数列"""
    data = [random.randint(1, 100) for i in range(n)]
    data = sorted(data, key=lambda x: x, reverse=False)
    return data


def bin_search_by_xunhuan(data,s):
    """二分查找,循环法"""
    left = 0
    right = len(data)-1
    n = 0
    while left<=right:
        n+=1
        mid = (left+right)//2
        if data[mid] == s:
            return '数据已找到,循环了{1}次,索引为{0}'.format(mid,n)
        elif s<data[mid]:
            right = mid-1

        elif s>data[mid]:
            left = mid+1
    return 'error!!!\n待查找数据不在原列表中'

def bin_search_by_digui(left,right,data,s):
    """二分查找,递归法"""
    if left > right:
        return '目标数据不在该列表中'
    mid = (left+right)//2
    if data[mid] == s:
        return '数据已找到索引为{0}'.format(mid)
    if data[mid] < s:
        return bin_search_by_digui(mid+1,right,data,s)
    elif data[mid] > s:
        return bin_search_by_digui(left,mid-1,data,s)





if __name__ == '__main__':
    r = get_random_list()
    data = r
    # data = [5, 8, 9, 9, 9, 15, 18, 28, 39, 44, 44, 51, 53, 56, 64, 65, 75, 77, 88, 90]
    # r = bin_search_by_xunhuan(data,15)
    # print(r)
    s = random.sample(data,1)[0]  # 在列表中随机找一个元素
    print(s)
    r = bin_search_by_digui(0,len(data)-1,data, s)
    print(r) 

2-6 :汉诺塔实现

阶乘

def jc(n):
    """阶乘"""
    if n ==1:return 1
    else:
        return n*jc(n-1)


if __name__ == '__main__':
    r = jc(2)
    print(r)
    r = jc(5)
    print(r)
    r = jc(10)
    print(r)

汉诺塔

#1. 大傻: 只搬动一号盘
#2. 1. 叫谁来做,: 1. 从哪个柱子搬动到哪个柱子,中间柱子
#2. 2. 搬动自己负责的柱子 3. 从哪个柱子搬动到哪个柱子,中间柱子


def move(index, start, mid, end):
    if index == 1:
        print("{}-->{}".format(start,end))
        return
    else:
        move(index-1, start, end, mid)
        print("{}-->{}".format(start, end))
        move(index-1, mid, start, end)


if __name__ == "__main__":
    move(5, "A", "B", "C")

"""
优点:
    简单,回溯法
    递归都能通过非递归的方式完成
缺点:
    1. 递归由于是函数调用自身,而函数调用是有时间和空间的消耗的:
    每一次函数调用,都需要在内存栈中分配空间以保存参数、返回地址以及临时变量,而往栈中压入数据和弹出数据都需要时间。->效率
    2. 递归中很多计算都是重复的,由于其本质是把一个问题分解成两个或者多个小问题,
    多个小问题存在相互重叠的部分,则存在重复计算,如fibonacci斐波那契数列的递归实现。->效率
    3. 调用栈可能会溢出,其实每一次函数调用会在内存栈中分配空间,而每个进程的栈的容量是有限的,当调用的层次太多时,就会超出栈的容量,从而导致栈溢出。->性能
"""

冒泡排序

冒泡排序:它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成

时间复杂度:O(n²)

空间复杂度:O(1)

稳定性:稳定

def bubble_sort(blist):
    count = len(blist)
    for i in range(count):
        for j in range(i + 1, count):
            if blist[i] > blist[j]:
                blist[i], blist[j] = blist[j], blist[i]

    return blist


blist = bubble_sort([4, 5, 6, 7, 3, 2, 6, 9, 8])
print(blist)

快速排序

快速排序:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列

时间复杂度:O(nlog₂n)

空间复杂度:O(nlog₂n)

稳定性:不稳定

def quick_sort(qlist):
    if qlist == []:
        return []
    else:
        qfirst = qlist[0]
        qless = quick_sort([l for l in qlist[1:] if l < qfirst])
        qmore = quick_sort([m for m in qlist[1:] if m >= qfirst])
        return qless + [qfirst] + qmore


qlist = quick_sort([4, 5, 6, 7, 3, 2, 6, 9, 8])
print(qlist)

选择排序

选择排序:第1趟,在待排序记录r1 ~ r[n]中选出最小的记录,将它与r1交换;第2趟,在待排序记录r2 ~ r[n]中选出最小的记录,将它与r2交换;以此类推,第i趟在待排序记录r[i] ~ r[n]中选出最小的记录,将它与r[i]交换,使有序序列不断增长直到全部排序完毕

时间复杂度:O(n²)

空间复杂度:O(1)

稳定性:不稳定

def select_sort(slist):
    for i in range(len(slist)):
        x = i
        for j in range(i, len(slist)):
            if slist[j] < slist[x]:
                x = j
        slist[i], slist[x] = slist[x], slist[i]
    return slist


slist = select_sort([4, 5, 6, 7, 3, 2, 6, 9, 8])
print(slist)

Python算法和数据结构

1 Python算法常考题

排序+查找 ,重中之重
	1 常考排序算法:冒泡排序,快速排序,归并排序,堆排序
    2 线性查找,二分查找
    3 能独立实现代码(手写),能够分析时间空间复杂度
    

2 Python数据结构常考题

python web后端常考数据结构

1 常见的数据结构链表,队列,栈,二叉树,堆
2 使用内置结构实现高级数据结构,比如内置的list.deque实现栈
3 leetcode或者《剑指offer》

常考数据结构值链表

链表有单链表,双链表,循环双端链表
1 如何使用Python来表示链表结构
2 实现链表常见操作,比如插入节点,反转链表,合并多个链表等
3 Leetcode联系常见的题

3 链表反转

class Solution(object):
class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        def helper(last, cur):
            if cur == None: return cur
            next = cur.next
            cur.next = last
            if next == None: return cur
            return helper(cur, next)
        return helper(None, head)


4 常考数据结构之队列

1 如何使用Python实现队列
2 实现队列的append和pop操作,如何做到先进先出
3 使用Python的list或者collections.deque实现队列

python实现队列,(队列是先进先出)

from collections import deque
class Queue:
    def __init__(self):
        self.items = deque()

    def append(self, val):
        return self.items.append(val)

    def pop(self):
        return self.items.popleft()

    def empty(self):
        return len(self.items) == 0


def test_queue():
    q = Queue()
    q.append(0)
    q.append(1)
    q.append(2)
    q.append(3)
    print(q.pop())
    print(q.pop())
    print(q.pop())
    print(q.pop())


test_queue()

5 常考数据结构之栈(后进先出)

栈(stack)是后进先出结构

from collections import deque


class Stack(object):

    def __init__(self):
        self.items = deque()

    def push(self, val):
        return self.items.append(val)

    def pop(self):
        return self.items.pop()
stack = Stack()
stack.push(1)
stack.push(2)
stack.push(3)
print(stack.pop())
print(stack.pop())
print(stack.pop())

6 常考数据结构之字典与集合

1 Python dict/set底层都是哈希表
2 根据哈希函快速定位一个元素,平均查找O(1),非常快
3 不断加入元素会引起哈希表重新开辟空间,拷贝之前元素到新数组

7 哈希表如何解决冲突

链接法和开放寻址法

1 元素key冲突之后使用一个链表填充相同key的元素
2 开放寻址发是冲突之后根据一种方式(二次探查)寻找下一个可用的槽
3 cPython使用的二次探查

8 常考数据结构之二叉树

先序,中序,后序遍历

  • 先(根)序:先处理根,之后是左子树,然后是右子树

  • 中(根)序:先处理左子树,然后是根,然后是右子树

  • 后根序:先处理左子树,然后是右子树,最后是根

9 常考数据结构之堆

使用python的heapq实现堆

import heapq


class Topk:
    """
    获取大量元素,topk个大元素,固定内存
    思路:
    1 先放入元素前k个建立一个最小堆
    2 迭代剩余元素
        如果当前元素小于堆顶元素,跳过该元素(肯定不是钱k大)
        否则替换堆顶元素为当前元素,并重新调整堆
    """

    def __init__(self, iterable, k):
        self.minheap = []
        self.capacity = k
        self.iterable = iterable

    def push(self, val):
        if len(self.minheap) >= self.capacity:
            min_val = self.minheap[0]
            if val < min_val:  # 当然你可以直接if val > min_val操作,这里只是显示指出跳出这个元素
                pass
            else:
                heapq.heapreplace(self.minheap, val)  # 返回并且pip堆顶对小值,推入新的val值并调整堆
        else:
            heapq.heappush(self.minheap, val)

    def get_topk(self):
        for val in self.iterable:
            self.push(val)
        return self.minheap


def test():
    import random
    i = list(range(1000))
    print(len(i))
    random.shuffle(i)
    print(len(i))
    # print(i)
    _ = Topk(i, 10)
    print(_.get_topk())


test()

10 Python白板编程(手写代码)

11 链表

链表涉及到指针操作较为复杂,容易出错,经常用作常考题

  • 熟悉链表的定义和常见操作
  • 常考题:删除一个链表节点
  • 常考题:合并两个有序链表

删除链表中的节点

# https://leetcode-cn.com/problems/delete-node-in-a-linked-list/
class Solution:
    def deleteNode(self, node):
        """
        :type node: ListNode
        :rtype: void Do not return anything, modify node in-place instead.
        """

        nextnode = node.next
        after_next_node = nextnode.next
        node.val = nextnode.val
        node.next = after_next_node
        

合并两个有序链表

# https://leetcode-cn.com/problems/merge-two-sorted-lists/
class Solution:
    def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
        if not l1: return l2
        if not l2: return l1
        if l1.val <= l2.val:
            l1.next = self.mergeTwoLists(l1.next, l2)
            return l1
        else:
            l2.next = self.mergeTwoLists(l2.next, l1)
            return l2

12 Python数据结构常考题之二叉树

  • 二叉树的操作很多可以用递归的方式解决

二叉树的镜像

# https://leetcode-cn.com/problems/er-cha-shu-de-jing-xiang-lcof/
class Solution:
    def mirrorTree(self, root: TreeNode) -> TreeNode:
        if root:
            root.left, root.right = root.right, root.left
            self.mirrorTree(root.left)
            self.mirrorTree(root.right)

        return root


二叉树的层序遍历

# https://leetcode-cn.com/problems/binary-tree-level-order-traversal/
class Solution:
    def levelOrder(self, root: TreeNode) -> List[List[int]]:
        if not root:return []
        res=[]
        cur_nodes = [root]
        next_nodes = []
        res.append([i.val for i in cur_nodes])
        while cur_nodes or next_nodes:
            for node in cur_nodes:
                if node.left:
                    next_nodes.append(node.left)
                if node.right:
                    next_nodes.append(node.right)
            if next_nodes:
                res.append(
                    [i.val for i in next_nodes]
                )
            cur_nodes = next_nodes
            next_nodes = []

        return res

13 栈和队列

后进先出 vs先进先出

  • 熟练掌握用Python的list或者 collections.deque()实现栈和队列
  • 常考题:用栈实现队列
  • leetcode implement-queue-using-stacks
# https://leetcode-cn.com/problems/implement-queue-using-stacks/
from collections import deque


class Stack:
    def __init__(self):
        self.items = deque()

    def push(self, val):
        return self.items.append(val)

    def pop(self):
        return self.items.pop()

    def top(self):
        return self.items[-1]

    def empty(self):
        return len(self.items) == 0


class MyQueue:

    def __init__(self):
        """
        Initialize your data structure here.
        """
        self.s1 = Stack()
        self.s2 = Stack()

    def push(self, x: int) -> None:
        """
        Push element x to the back of queue.
        """
        self.s1.push(x)

    def pop(self) -> int:
        """
        Removes the element from in front of queue and returns that element.
        """
        if not self.s2.empty():
            return self.s2.pop()
        while not self.s1.empty():
            val = self.s1.pop()
            self.s2.push(val)

        return self.s2.pop()

    def peek(self) -> int:
        """
        Get the front element.
        """
        if not self.s2.empty():
            return self.s2.top()
        while not self.s1.empty():
            val = self.s1.pop()
            self.s2.push(val)
        return self.s2.top()

    def empty(self) -> bool:
        """
        Returns whether the queue is empty.
        """
        return self.s1.empty() and self.s2.empty()

14 数据结构之堆

堆常考题基本围绕在合并多个有序(数组、链表):Topk问题

1 理解堆的概念,堆是完全二叉树,有最大堆和最小堆
2 会使用Python内置的heapq模块实现堆的操作
3 常考题:合并k个有序链表leetcode merge-k-sorted-list

合并K个排序链表

# Definition for singly-linked list.
from heapq import heappop, heapify


class ListNode:
    def __init__(self, x):
        self.val = x
        self.next = None


class Solution:
    def mergeKLists(self, lists: ListNode):

        h = []
        for node in lists:
            while node:
                h.append(node.val)
                node = node.next
        if not lists: return
        heapify(h)
        root = ListNode(heappop(h))
        curnode = root
        while h:
            curnode.next = ListNode(heappop(h))
            curnode = curnode.next
        return root

15 Python字符串常考算法题

了解常用的字符串操作

1 Python内置了很多字符串操作,比如split,upper,repalce
2 反转一个字符串
3 如何判断一个数字是否是回文数(双端变化法)
# https://leetcode-cn.com/problems/reverse-string/
# 反转字符串
class Solution:
    def reverseString(self, s: List[str]) -> None:
        """
        Do not return anything, modify s in-place instead.
        """
        # s.reverse()
        beg = 0
        last = len(s)-1
        while beg<last:
            s[beg],s[last] = s[last], s[beg]
            beg +=1
            last -= 1
        

回文数

# https://leetcode-cn.com/problems/palindrome-number/
class Solution:
    def isPalindrome(self, x: int) -> bool:
        if x<0:return False
        # x = [i for i in str(x)]
        x = str(x)
        beg,last = 0, len(x) - 1
        while beg < last:
            if x[beg] != x[last]:
                return False
            beg += 1
            last -=1
        return True

16 算法与数据结构练习题:反转链表

# https://leetcode-cn.com/problems/reverse-linked-list/
# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def reverseList(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        def helper(last,cur):
            if cur == None: return cur
            next = cur.next
            cur.next = last
            if next == None:return cur
            return helper(cur,next)

        return helper(None, head)