返回

带着算法,上LeetCode挑战109道题(2019年8月上)!

前端

自 2019-05-16 开始,jsliang 开始了 算法与数据结构 攻略(划水)之旅。为了帮助大家更高效地掌握算法与数据结构,并为您的编程面试做好准备,jsliang 每天会折腾一道及以上 LeetCode 题目,并将其解题思路记录成文章,发布到 GitHub 和 微信公众号 上。这里不一一累述,本次将8月上旬刷的109道题进行汇总,知识点为数组。

为了帮助您更好地理解和掌握这些知识点,我们按照以下顺序介绍:

  • 基础概念
  • 常见操作
  • 常见算法
  • 经典问题

基础概念

  • 数组 :数组是一种数据结构,它由一组相同类型的数据元素组成。数组中的每个元素都有一个唯一的索引,索引从0开始。
  • 一维数组 :一维数组是数组的一种特殊形式,它只有一行和多列。
  • 二维数组 :二维数组是数组的另一种特殊形式,它有多行和多列。
  • 三维数组 :三维数组是数组的第三种特殊形式,它有多行、多列和多层。

常见操作

  • 创建数组 :可以使用以下两种方式创建数组:

    • 使用数组字面量:
arr = [1, 2, 3, 4, 5]
* 使用数组构造函数:
arr = array.array('i', [1, 2, 3, 4, 5])
  • 访问数组元素 :可以使用以下两种方式访问数组元素:

    • 使用索引:
arr[0]  # 输出 1
* 使用切片:
arr[1:3]  # 输出 [2, 3]
  • 修改数组元素 :可以使用以下方式修改数组元素:
arr[0] = 10
  • 添加数组元素 :可以使用以下方式添加数组元素:
arr.append(6)
  • 删除数组元素 :可以使用以下方式删除数组元素:
arr.pop()

常见算法

  • 遍历数组 :遍历数组的常见算法有:

    • 顺序遍历 :顺序遍历数组是最简单的一种遍历算法。它从数组的第一个元素开始,依次访问数组的每个元素,直到最后一个元素。
for i in range(len(arr)):
    print(arr[i])
* **逆序遍历** :逆序遍历数组与顺序遍历数组相反,它是从数组的最后一个元素开始,依次访问数组的每个元素,直到第一个元素。
for i in range(len(arr) - 1, -1, -1):
    print(arr[i])
* **二分查找** :二分查找是一种高效的查找算法,它适用于有序数组。二分查找算法首先将数组分成两半,然后比较目标值与数组中间元素的值。如果目标值小于中间元素的值,则继续在数组的前一半进行二分查找;如果目标值大于中间元素的值,则继续在数组的后一半进行二分查找。如此反复,直到找到目标值或确定目标值不存在于数组中。
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
  • 排序数组 :排序数组的常见算法有:

    • 冒泡排序 :冒泡排序是一种简单的排序算法,它通过不断地比较相邻元素,并将较大的元素向后移动,直到数组中的所有元素都按升序排列。
def bubble_sort(arr):
    for i in range(len(arr) - 1):
        for j in range(len(arr) - i - 1):
            if arr[j] > arr[j + 1]:
                arr[j], arr[j + 1] = arr[j + 1], arr[j]

    return arr
* **选择排序** :选择排序也是一种简单的排序算法,它通过不断地找到数组中最小(或最大)的元素,并将其与数组的第一个元素交换,直到数组中的所有元素都按升序(或降序)排列。
def selection_sort(arr):
    for i in range(len(arr) - 1):
        min_index = i

        for j in range(i + 1, len(arr)):
            if arr[j] < arr[min_index]:
                min_index = j

        arr[i], arr[min_index] = arr[min_index], arr[i]

    return arr
* **插入排序** :插入排序是一种简单的排序算法,它通过不断地将一个元素插入到数组中适当的位置,直到数组中的所有元素都按升序排列。
def insertion_sort(arr):
    for i in range(1, len(arr)):
        key = arr[i]
        j = i - 1

        while j >= 0 and key < arr[j]:
            arr[j + 1] = arr[j]
            j -= 1

        arr[j + 1] = key

    return arr
* **归并排序** :归并排序是一种高效的排序算法,它通过不断地将数组分成两半,然后对每一半进行排序,最后合并两个有序的子数组,直到数组中的所有元素都按升序排列。
def merge_sort(arr):
    if len(arr) <= 1:
        return arr

    mid = len(arr) // 2
    left_half = merge_sort(arr[:mid])
    right_half = merge_sort(arr[mid:])

    return merge(left_half, right_half)

def merge(left_half, right_half):
    i = 0
    j = 0
    merged = []

    while i < len(left_half) and j < len(right_half):
        if left_half[i] < right_half[j]:
            merged.append(left_half[i])
            i += 1
        else:
            merged.append(right_half[j])
            j += 1

    while i < len(left_half):
        merged.append(left_half[i])
        i += 1

    while j < len(right_half):
        merged.append(right_half[j])
        j += 1

    return merged
* **快速排序** :快速排序是一种高效的排序算法,它通过不断地选择一个枢轴元素,然后将数组分成两部分,一部分包含所有小于枢轴元素的元素,另一部分包含所有大于枢轴元素的元素,最后对每一部分进行排序,直到数组中的所有元素都按升序排列。
def quick_sort(arr):
    if len(arr) <= 1:
        return arr

    pivot = arr[len(arr) // 2]
    left = []
    right = []

    for i in range(len(arr)):
        if arr[i] < pivot:
            left.append(arr[i])
        elif arr[i] > pivot:
            right.append(arr[i])

    return quick_sort(left) + [pivot] + quick_sort(right)

经典问题

  • 寻找最大子数组 :寻找最大子数组的问题是,给定一个数组,找到数组中连续的子数组,使得子数组的和最大。
def max_subarray(arr):
    max_so_far = float('-inf')
    max_ending_here = 0

    for i in range(len(arr)):
        max_ending_here = max_ending_here + arr[i]
        if max_so_far < max_ending_here:
            max_so_far = max_ending_here

        if max