常用数据结构和算法(python实现版)

一、数据结构

1、数组

my_array = [1, 2, 3, 4, 5]
print(my_array)  # 输出 [1, 2, 3, 4, 5]
  • 数组是计算机中常用的数据结构之一,它是一种线性结构,由一组具有相同类型的元素组成,这些元素在内存中是连续存储的。
  • 数组可以通过下标进行访问和操作,下标从0开始。数组的长度是固定的,一旦创建,就无法再改变其大小。
  • 数组可以用于存储一组有序的数据,例如整型、浮点型、字符型等。使用数组可以方便地进行数据的存取、修改、排序、查找等操作。
  • 数组是其他数据结构的基础,例如队列、堆栈等。在算法设计中,数组是常用的数据结构之一,可以用于解决各种问题,例如排序、查找、最大子序列和等。

2、链表

class Node:
    '''链表的节点
    包含一个值和一个指向下一个节点的指针
    '''
    def __init__(self, val):
        self.val = val
        self.next = None
        
class LinkedList:
    '''链表
    包含一个头指针,即链表的第一个节点
    '''
    def __init__(self):
        self.head = None
        
    def add(self, val):
        if not self.head:
            self.head = Node(val)
        else:
            curr = self.head
            while curr.next:
                curr = curr.next
            curr.next = Node(val)
  • 链表是一种线性数据结构,它的每个节点包含了一个值和一个指向下一个节点的指针。
  • 与数组相比,链表的插入和删除操作更为高效,因为它们不需要移动其他元素。但是,链表的访问操作比较耗时,因为它们需要从头开始遍历链表才能找到指定的元素。
  • 链表有多种类型,包括单链表、双向链表和循环链表等。
  • 链表的使用场景非常广泛,比如在实现哈希表、队列、栈等数据结构时,都可以使用链表来实现。在算法中,链表经常用来解决一些需要删除或插入元素的问题。

3、栈

class Stack:
    '''栈
    包含一个数组
    '''
    def __init__(self):
        self.items = []
    # 检查栈是否为空
    def is_empty(self):
        return len(self.items) == 0
    # 入栈
    def push(self, item):
        self.items.append(item)
    # 出栈
    def pop(self):
        return self.items.pop()
    # 获取栈顶元素
    def peek(self):
        return self.items[-1]
    # 获取栈内的元素数
    def size(self):
        return len(self.items)
  • 栈是一种线性数据结构,遵循后进先出(LIFO)的原则。
  • 栈有两个基本操作:push和pop。push操作将一个元素压入栈顶,而pop操作则将栈顶元素弹出。
  • 栈有一个重要的特性是它只能从栈顶访问元素,因此访问其他位置的元素需要先弹出栈顶的元素。
  • 栈在计算机科学中有广泛的应用,比如函数调用栈、括号匹配、表达式求值等。

4、队列

class Queue:
    '''队列
    包含一个数组
    '''
    def __init__(self):
        self.items = []
    # 入队
    def enqueue(self, item):
        self.items.append(item)
    # 出队
    def dequeue(self):
        if not self.is_empty():
            return self.items.pop(0)
    # 检查队列是否为空
    def is_empty(self):
        return len(self.items) == 0
    # 获取队列的元素数
    def size(self):
        return len(self.items)
  • 队列是一种线性数据结构,遵循先进先出(FIFO)的原则。
  • 队列有多种实现方式,包括数组实现、链表实现等。在使用队列时,我们需要关注队列的性质,如是否为空、是否已满,以及如何处理队列中的元素。
  • 队列常用于异步处理、消息队列等场景,如生产者-消费者模型。

5、树

class Node:
    '''二叉树节点
    包含一个值、一个左节点指针、一个右节点指针
    '''
    def __init__(self, value):
        self.value = value
        self.left = None
        self.right = None

class BinaryTree:
    '''二叉树
    包含一个根节点,即二叉树的第一个节点
    '''
    def __init__(self, root):
        self.root = Node(root)
    # 输出树的内容
    def print_tree(self, traversal_type):
        if traversal_type == "preorder":
            # 前序遍历
            return self.preorder_print(tree.root, "")
        elif traversal_type == "inorder":
            # 中序遍历
            return self.inorder_print(tree.root, "")
        elif traversal_type == "postorder":
            # 后序遍历
            return self.postorder_print(tree.root, "")
        else:
            print("Traversal type " + str(traversal_type) + " is not supported.")
            return False
    # 前序遍历
    def preorder_print(self, start, traversal):
        if start:
            traversal += (str(start.value) + "-")
            traversal = self.preorder_print(start.left, traversal)
            traversal = self.preorder_print(start.right, traversal)
        return traversal
    # 中序遍历
    def inorder_print(self, start, traversal):
        if start:
            traversal = self.inorder_print(start.left, traversal)
            traversal += (str(start.value) + "-")
            traversal = self.inorder_print(start.right, traversal)
        return traversal
    # 后序遍历
    def postorder_print(self, start, traversal):
        if start:
            traversal = self.postorder_print(start.left, traversal)
            traversal = self.postorder_print(start.right, traversal)
            traversal += (str(start.value) + "-")
        return traversal

# 构造一颗二叉树
tree = BinaryTree(1)
tree.root.left = Node(2)
tree.root.right = Node(3)
tree.root.left.left = Node(4)
tree.root.left.right = Node(5)

# 前序遍历
print("前序遍历:", tree.print_tree("preorder"))
# 中序遍历
print("中序遍历:", tree.print_tree("inorder"))
# 后序遍历
print("后序遍历:", tree.print_tree("postorder"))
  • 树是一种非线性的数据结构,由节点和边组成。
  • 树的一个节点可以有多个子节点,但每个节点只有一个父节点,除了根节点没有父节点。
  • 树的基本操作包括遍历、搜索、插入、删除等。
  • 在计算机科学中,树被广泛应用于各种算法和数据结构,例如搜索树、堆、哈夫曼树等。

6、堆

class Heap:
    '''堆,最小堆
    包含一个列表
    _sift_up() 和 _sift_down() 方法用于维护堆的性质
    '''
    def __init__(self):
        self.heap = []

    def push(self, val):
        # 将一个元素加入到堆中
        self.heap.append(val)
        # 将插入的元素向上移动,直到满足堆的性质
        self._sift_up(len(self.heap) - 1)

    def pop(self):
        # 将堆中的最小元素弹出
        if len(self.heap) == 0:
            raise IndexError("pop from an empty heap")
        val = self.heap[0]
        # 将弹出的元素的子节点中较小的元素向上移动,直到满足堆的性质
        self.heap[0] = self.heap[-1]
        del self.heap[-1]
        self._sift_down(0)
        return val

    def _sift_up(self, index):
        # 用于将元素向上移动,直到满足堆的性质
        parent = (index - 1) // 2
        if index > 0 and self.heap[index] < self.heap[parent]:
            self.heap[index], self.heap[parent] = self.heap[parent], self.heap[index]
            self._sift_up(parent)

    def _sift_down(self, index):
        # 用于将元素向下移动,直到满足堆的性质。
        left_child = 2 * index + 1
        right_child = 2 * index + 2
        smallest = index
        if left_child < len(self.heap) and self.heap[left_child] < self.heap[smallest]:
            smallest = left_child
        if right_child < len(self.heap) and self.heap[right_child] < self.heap[smallest]:
            smallest = right_child
        if smallest != index:
            self.heap[index], self.heap[smallest] = self.heap[smallest], self.heap[index]
            self._sift_down(smallest)
  • 堆是一种完全二叉树,满足堆性质。堆分为最大堆和最小堆两种类型,最大堆中父节点的值大于或等于子节点的值,最小堆中父节点的值小于或等于子节点的值。
  • 堆是一种特殊的二叉树,因此使用列表来表示堆的数据结构时,列表的下标可以表示堆中节点的位置。
  • 堆的常用操作有插入元素、删除最小元素、查找最小元素等。
  • 堆被广泛应用于各种算法中,如堆排序、最短路径算法、图像处理等。

7、图

class Graph:
    '''简单图
    包含一个邻接矩阵以及其容量
    '''
    def __init__(self, num_vertices):
        self.num_vertices = num_vertices
        self.adj_matrix = [[0] * num_vertices for _ in range(num_vertices)]
    # 构建v1与v2的关系
    def add_edge(self, v1, v2):
        self.adj_matrix[v1][v2] = 1
        self.adj_matrix[v2][v1] = 1
    # 解除v1与v2的关系
    def remove_edge(self, v1, v2):
        self.adj_matrix[v1][v2] = 0
        self.adj_matrix[v2][v1] = 0
  • 图是由节点和边构成的一种数据结构,可以用来表示各种实际问题。
  • 在图中,每个节点都有一个唯一的标识符,而边则表示节点之间的关系。另外,图还可以包含权重,用于表示不同节点之间的距离或者代价。
  • 图可以分为有向图和无向图,有向图中的边有方向,而无向图中的边没有方向。
  • 图的遍历和搜索是图算法的重要部分,包括深度优先搜索和广度优先搜索等算法。图还有很多其他的应用,例如在社交网络分析、路径规划、网络路由和图像处理等领域。

8、哈希表

class HashTable:
    '''哈希表
    包含一个二维数组和哈希表大小
    '''
    def __init__(self):
        self.size = 10
        self.hash_table = [None] * self.size
    # 哈希函数
    def hash_func(self, key):
        # hash(key)是Python内置的哈希函数,它将给定的键key转换为一个哈希值
        # hash(key) % self.size 表示将哈希值映射到哈希表中的一个索引位置,保证索引位置在哈希表的范围内。
        # 返回值是一个整数,表示给定键的哈希值在哈希表中的索引位置。
        return hash(key) % self.size
    
    def add(self, key, value):
        hash_key = self.hash_func(key)
        if self.hash_table[hash_key] is None:
            self.hash_table[hash_key] = [(key, value)]
        else:
            # key的哈希值相同时,它们将会被存储在同一个一维数组中
            for pair in self.hash_table[hash_key]:
                if pair[0] == key:
                    pair[1] = value
                    return
            self.hash_table[hash_key].append((key, value))

    def get(self, key):
        hash_key = self.hash_func(key)
        if self.hash_table[hash_key] is not None:
            for pair in self.hash_table[hash_key]:
                if pair[0] == key:
                    return pair[1]
        return None
  • 哈希表是一种基于哈希函数实现的数据结构,通过哈希函数将数据映射到固定大小的数组中,以实现快速的数据查找和插入。
  • 哈希表的关键操作是哈希函数的设计,它应该能够将数据均匀地映射到数组中,并且具有尽可能小的冲突。
  • 哈希表在许多应用中都有广泛的应用,例如字典、缓存等。

二、算法

1、排序算法

排序的稳定性:对于相等元素的顺序没有影响。具体来说,在排序过程中,当遇到相等的元素时,不会改变它们的相对位置,即相等元素的先后顺序不变。
  • 冒泡排序
    def bubble_sort(arr):
        n = len(arr)
        for i in range(n):
            for j in range(0, n-i-1):
                if arr[j] > arr[j+1]:
                    arr[j], arr[j+1] = arr[j+1], arr[j]
    
    # 测试
    arr = [64, 34, 25, 12, 22, 11, 90]
    bubble_sort(arr)
    print("排序后的数组:")
    for i in range(len(arr)):
        print("%d" % arr[i], end=" ")
    
    • 冒泡排序是一种简单的排序算法,它重复地遍历要排序的数组,每次比较相邻的两个元素,如果它们的顺序错误就交换它们。
    • 每一趟都能把最大(或最小)的元素放在数组的末尾,使得数据像泡泡一样从底部冒出来,所以称为冒泡排序。
    • 冒泡排序的时间复杂度为O(n^2),在数据量较小的情况下效率还是比较高的。
  • 快速排序
    def quick_sort(array):
        if len(array) <= 1:
            return array
        else:
            # 选定一个基准值
            pivot = array[0]
            # 获取都小于基准值的元素构成左边部分
            left = [x for x in array[1:] if x < pivot]
            # 获取都大于等于基准值的元素构成右边部分
            right = [x for x in array[1:] if x >= pivot]
            # 对左右两个部分分别递归地进行快速排序,最后将排好序的左右两个部分合并起来
            return quick_sort(left) + [pivot] + quick_sort(right)
    
    • 快速排序是一种基于分治思想的排序算法。它的基本思想是:选定一个基准值(通常为数组中的第一个元素),然后将数组中的元素分成左右两个部分,使得左边部分的所有元素都小于等于基准值,右边部分的所有元素都大于等于基准值。接着对左右两个部分分别递归地进行快速排序,最后将排好序的左右两个部分合并起来即可。
    • 快速排序的时间复杂度为O(nlogn),不需要额外的内存空间。
    • 在最坏情况下(即数组已经排好序或者逆序),快速排序的时间复杂度将达到O(n^2),因此需要采用随机化等优化技术来避免最坏情况的出现。
  • 归并排序
    def merge_sort(arr):
        if len(arr) <= 1:
            return arr
        # 分割
        mid = len(arr) // 2
        left = arr[:mid]
        right = arr[mid:]
        # 递归
        left = merge_sort(left)
        right = merge_sort(right)
        # 合并
        return merge(left, right)
    
    def merge(left, right):
        result = []
        i = j = 0
        # 将2个排序好的数组合并成一个排序好的数组
        while i < len(left) and j < len(right):
            if left[i] <= right[j]:
                result.append(left[i])
                i += 1
            else:
                result.append(right[j])
                j += 1
        result += left[i:]
        result += right[j:]
        return result
    
    • 归并排序的基本思路是将数组切割成更小的子数组,直到每个子数组都只有一个元素,然后开始递归合并相邻的子数组以生成排序好的大数组。
    • 归并排序的时间复杂度为 O(nlogn),空间复杂度为 O(n)。
    • 归并排序具有稳定性,即在排序过程中相同大小的元素相对位置不会改变。
  • 插入排序
    def insertion_sort(arr):
        for i in range(1, len(arr)):
            key = arr[i]
            j = i - 1
            while j >= 0 and arr[j] > key:
                arr[j + 1] = arr[j]
                j -= 1
            arr[j + 1] = key
        return arr
    
    • 插入排序的基本思路是将一个待排序的序列分成两部分,即有序部分和无序部分。遍历无序部分,将其元素插入到有序部分中,以此类推,直到整个序列有序。
    • 插入排序的时间复杂度为 O ( n 2 ) O(n^2) O(n2),不需要额外的空间。
    • 在面对基本有序的序列时,插入排序的性能很优秀,因为只需要进行少量的比较和移动操作。
    • 插入排序是一种稳定排序算法,是一种原地排序算法。

2、查找算法

  • 二分查找
    def binary_search(arr, target):
        left = 0
        right = len(arr) - 1
    
        while left <= right:
            mid = (left + right) // 2
    
            if arr[mid] == target:
                return mid
            elif arr[mid] < target:
                left = mid + 1
            else:
                right = mid - 1
    
        return -1
    
    • 在使用二分查找时,需要先对数据进行排序,这样才能通过比较中间位置的值来确定目标值的位置。
    • 二分查找的基本思想是将待查找的区间不断缩小,直到找到目标值或者确定目标值不存在。
    • 每次将待查找区间的中间位置与目标值进行比较,如果中间位置的值等于目标值,则查找成功;如果中间位置的值小于目标值,则目标值一定在右侧区间;如果中间位置的值大于目标值,则目标值一定在左侧区间。重复以上过程,直到找到目标值或者确定目标值不存在。
    • 二分查找是一种高效的查找算法,它的时间复杂度为 O(log n)。
    • 二分查找适用于有序数组或者有序列表中查找目标值的场景,它的优势在于每次查找可以排除一半的数据,因此效率很高。
  • 哈希查找
    def hash_search(hash_table, value):
        # hash(key)是Python内置的哈希函数,它将给定的键key转换为一个哈希值
        # 将要查找的元素映射到哈希表中的某一个位置上。
        key = hash(value) % len(hash_table)
        # 如果该位置上已经有了其他元素,则通过线性探测的方式,依次往后查找,直到找到空闲位置或者找到了要查找的元素为止。
        while hash_table[key] != None:
            if hash_table[key] == value:
                return key
            key = (key + 1) % len(hash_table)
        return None
    
    • 哈希查找,也称为散列查找,是利用哈希表进行查找的一种方法,它的平均查找时间复杂度为O(1)。
    • 具体实现过程是将要查找的元素通过哈希函数映射到哈希表的某一个位置上,如果该位置上已经存在元素,则进行冲突处理,将该元素插入到其他位置上,直到找到空闲位置为止。
    • 哈希查找的优点是查询速度快,适用于大规模数据的查找。
    • 哈希查找的缺点也比较明显,即对于哈希函数的设计要求比较高,如果哈希函数设计不当,容易造成哈希冲突,影响查找效率。

3、字符串匹配算法

  • 暴力匹配
    def bruteforce_match(s, p):
        m, n = len(s), len(p)
        # i 指向主串当前比较的位置,j 指向模式串当前比较的位置
        i, j = 0, 0
        while i < m and j < n:
            if s[i] == p[j]:
                # 如果当前字符匹配,则 i 和 j 同时向后移动一位
                i += 1
                j += 1
            else:
                # 如果不匹配,则 i 回溯到之前的位置加一,j 回溯到模式串的开头
                i = i - j + 1
                j = 0
        if j == n:
            # 如果找到了一个匹配的子串,返回子串的起始位置
            return i - j
        else:
            # 否则返回 -1
            return -1
    
    • 暴力匹配算法也被称为朴素匹配算法,它是一种简单直接的字符串匹配算法。
    • 基本思想是从主串的第一个字符开始,依次和模式串的每个字符进行比较,如果不匹配,则主串的位置后移一位,继续从下一个位置开始匹配,直到找到一个匹配的子串,或者主串匹配完毕仍然没有找到。
    • 暴力匹配算法的时间复杂度为 O(mn),其中 m 和 n 分别为主串和模式串的长度。
    • 在最坏情况下,需要比较主串和模式串的所有可能的子串,因此算法的时间复杂度较高。
    • 在实际应用中,暴力匹配算法常用于短字符串匹配等规模较小的问题。
  • KMP算法
    def kmp(s: str, p: str) -> int:
        m, n = len(s), len(p)
        if n == 0:
            return 0
        # next数组用于记录模式串中前缀和后缀的最长公共部分,用于避免后续匹配过程中的不必要匹配操作
        next = [-1] * n
        # i指向模式串中已匹配的部分的末尾,j指向最长公共前后缀的末尾。
        i, j = 0, -1
        # 预处理
        while i < n - 1:
            if j == -1 or p[i] == p[j]:
                i, j = i + 1, j + 1
                next[i] = j
            else:
                j = next[j]
        # i指向文本串中已匹配的部分的末尾,j指向模式串中已匹配的部分的末尾
        i, j = 0, 0
        while i < m and j < n:
            if j == -1 or s[i] == p[j]:
                i, j = i + 1, j + 1
            else:
                j = next[j]
        if j == n:
            return i - j
        else:
            return -1
    
    • KMP算法是一种高效的字符串匹配算法,它的核心思想是利用已知信息尽可能减少无效的匹配操作。
    • 具体而言,它通过一个预处理的next数组来记录模式串中的前缀和后缀的最长公共部分,然后利用这个信息避免在匹配过程中对不可能匹配的位置进行重复的尝试,即利用next数组可以让模式串在匹配失败时跳过一定长度而不是直接回到开头重新匹配。
    • KMP算法的核心是next数组。
    • KMP算法的时间复杂度为O(n+m),其中n是文本串的长度,m是模式串的长度。

4、图算法

  • 最短路径算法
    # heapq是Python中的一个模块,提供了一些堆操作的函数,可以快速地实现堆数据结构
    import heapq
    def dijkstra(graph, start):
        # 初始化距离字典和优先队列
        distances = {node: float('inf') for node in graph}
        distances[start] = 0
        queue = [(0, start)]
        
        while queue:
            # 取出当前距离最短的节点
            current_distance, current_node = heapq.heappop(queue)
            # 如果当前节点的距离已经比原来的短了,就不用再更新了
            if current_distance > distances[current_node]:
                continue
            # 更新当前节点的邻居节点的距离
            for neighbor, weight in graph[current_node].items():
                distance = current_distance + weight
                if distance < distances[neighbor]:
                    distances[neighbor] = distance
                    heapq.heappush(queue, (distance, neighbor))
        return distances
    
    • 在图算法中,最短路径算法是用于寻找两个顶点之间的最短路径,即使得路径上边的权重和最小的路径。
    • 最常用的两种最短路径算法是Dijkstra算法和Bellman-Ford算法。
    • Dijkstra算法的时间复杂度为O(ElogV),其中E为边数,V为节点数。这是一种比较高效的最短路径算法,适用于大多数场景。
  • 最小生成树算法
    # heapq是Python中的一个模块,提供了一些堆操作的函数,可以快速地实现堆数据结构
    import heapq
    '''
    1. 该算法基于贪心思想,它会从一个点开始,每次选择一个最短的边,将其加入生成树中,直到生成树中包含了所有的节点。
    2. 该算法通过堆这个数据结构来快速找到最短的边,从而加速算法的执行。
    '''
    def prim(graph, start):
        # mst用于存储生成树
        mst = []
        # visited用于存储已访问的图节点,set为集合数据结构
        visited = set([start])
        # edges 表示图中的所有边,每个边是一个三元组 (cost, start, to),表示从节点 start 到节点 to 的一条边,边的权重为 cost。
        edges = [(cost, start, to) for to, cost in graph[start].items()]
        # heapq.heapify()函数的功能是将一个可迭代对象转换为堆
        # 即采用堆来存储边,以便按照边的权重来排序
        heapq.heapify(edges)
    
        while edges:
            # 选取最短边
            cost, frm, to = heapq.heappop(edges)
            # 如果to节点未访问过,则将其加入生成树中
            if to not in visited:
                mst.append((frm, to, cost))
                # 更新visited
                visited.add(to)
                # 更新edges
                for to_next, cost in graph[to].items():
                    if to_next not in visited:
                        heapq.heappush(edges, (cost, to, to_next))
    
        return mst
    
    • 最小生成树算法的目的是在一张图中找到一个生成树,使得这个生成树中所有边的权重之和最小。
    • 基本思想是从一个点开始,每次加入一个与已有部分最近的点和这个点之间的边。这样得到的就是一个最小生成树。

5、动态规划算法

动态规划的思想:从最小的子问题开始逐步推导到原问题,即将问题拆分成更小的子问题,通过保存子问题的最优解,再逐步合并子问题的解来得到原问题的最优解。
  • 背包问题
    • 在该问题中,我们有一个背包和一些物品,每个物品都有一个重量和一个价值。我们的目标是用有限的背包容量装下最有价值的物品。
    '''
        weights: 物品的重量
        values: 物品的价值
        capacity: 背包容量
    '''
    def knapsack(weights, values, capacity):
        n = len(weights)
        # dp用于记录背包容量为j时,前i个物品能够获得的最大价值
        dp = [[0 for j in range(capacity + 1)] for i in range(n + 1)]
        
        for i in range(1, n + 1):
            for j in range(1, capacity + 1):
                if weights[i-1] > j:
                    dp[i][j] = dp[i-1][j]
                else:
                    dp[i][j] = max(dp[i-1][j], dp[i-1][j-weights[i-1]] + values[i-1])
        
        return dp[n][capacity]
    
    • 具体的求解方法是,对于每个物品,我们可以选择将它放入背包中或不放入背包中。如果选择放入,则背包容量需要减去物品的重量,同时背包的总价值需要加上物品的价值。如果选择不放入,则背包的容量和价值都不变。
    • 动态规划算法中的背包问题是一个经典的问题,它可以用于解决许多实际问题,如货车装载问题、资源分配问题等。
  • 最长公共子序列问题
    • 目标是寻找两个字符串的最长公共子序列。
    def lcs(X, Y):
        m = len(X)
        n = len(Y)
        # L 数组来保存两个字符串的最长公共子序列的长度
        # 其中 L[i][j] 表示 X 的前 i 个字符和 Y 的前 j 个字符的最长公共子序列长度
        L = [[None] * (n + 1) for i in range(m + 1)]
    
        for i in range(m + 1):
            for j in range(n + 1):
                if i == 0 or j == 0:
                    L[i][j] = 0
                elif X[i-1] == Y[j-1]:
                    L[i][j] = L[i-1][j-1] + 1
                else:
                    L[i][j] = max(L[i-1][j], L[i][j-1])
    
        return L[m][n]
    
    • 该算法的时间复杂度为 O(mn),其中 m 和 n 分别为两个字符串的长度。
  • 最大子序列和问题
    • 目标是在一个长度为 n 的数组中找到一个连续的子数组,使得该子数组中的元素之和最大。
    def max_subarray_sum(arr):
        n = len(arr)
        # dp[i] 表示以arr[i] 为结尾的最大子序列和
        dp = [0] * n
        dp[0] = arr[0]
        for i in range(1, n):
            dp[i] = max(arr[i], dp[i-1]+arr[i])
        return max(dp)
    
    • 关键点是考虑以 arr[i] 结尾的最大子序列和应该如何计算。
    • 如果 arr[i] 单独成为一个子序列,那么最大子序列和就是它本身,即 dp[i] = arr[i];
    • 如果将arr[i]加入到以arr[i-1]结尾的最大子序列中,那么最大子序列和就是 dp[i-1]+arr[i]
    • 因此,以arr[i]结尾的最大子序列和应该是这两个数中的较大值,即 dp[i] = max(arr[i], dp[i-1]+arr[i])。
  • 2
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值