当前位置: 首页 > news >正文

Python常见面试题的详解25

1. 什么是 MD5 加密,有什么特点

  • 要点

  1. 定义:MD5 是一种广泛应用的哈希函数,它能够把任意长度的输入数据经过特定算法处理,转化为长度固定为 128 位的哈希值,通常以 32 位十六进制字符串的形式呈现,主要用于验证数据在传输或存储过程中是否保持完整,未被篡改。

  2. 特点

    • 固定输出:无论输入的数据量大小如何,MD5 都会生成长度统一的 128 位哈希值,方便数据的存储和比较。

    • 高效计算:MD5 的计算过程相对简单,执行速度快,能在短时间内对大量数据完成哈希计算。

    • 不可逆性:从生成的哈希值无法反向推导出原始的输入数据,保证了数据的安全性。

    • 雪崩效应:输入数据哪怕只有极其微小的改变,都会使输出的哈希值发生巨大的变化,这一特性增强了哈希值的唯一性和辨识度。

    • 安全缺陷:由于存在碰撞攻击的风险,即可以找到两个不同的输入产生相同的哈希值,MD5 已不适合用于对安全性要求较高的场景,如密码存储。

python

import hashlib

def md5_encryption(data):
    """
    该函数用于对输入的数据进行 MD5 加密
    :param data: 待加密的字符串
    :return: 加密后的十六进制哈希值
    """
    # 创建 MD5 哈希对象
    md5_hash = hashlib.md5()
    # 将输入数据编码为 UTF - 8 格式,并更新哈希对象的内容
    md5_hash.update(data.encode('utf-8'))
    # 获取十六进制表示的哈希值
    return md5_hash.hexdigest()

# 定义要加密的字符串
original_data = "Hello, World!"
# 调用函数进行加密
encrypted_data = md5_encryption(original_data)

print(f"原始数据: {original_data}")
print(f"MD5 加密后的哈希值: {encrypted_data}")

  • 补充知识点

        1. 安全替代方案:鉴于 MD5 的安全漏洞,在实际应用中,可使用更安全的哈希算法,如 SHA - 256。以下是使用 Python 实现 SHA - 256 加密的示例代码:

python

import hashlib

def sha256_encryption(data):
    """
    该函数用于对输入的数据进行 SHA - 256 加密
    :param data: 待加密的字符串
    :return: 加密后的十六进制哈希值
    """
    # 创建 SHA - 256 哈希对象
    sha256_hash = hashlib.sha256()
    # 将输入数据编码为 UTF - 8 格式,并更新哈希对象的内容
    sha256_hash.update(data.encode('utf-8'))
    # 获取十六进制表示的哈希值
    return sha256_hash.hexdigest()

original_data = "Hello, World!"
encrypted_data = sha256_encryption(original_data)

print(f"原始数据: {original_data}")
print(f"SHA - 256 加密后的哈希值: {encrypted_data}")

        2. 批量数据验证:在处理大量数据时,可对每个数据块进行 MD5 哈希计算,并将哈希值存储下来。在后续验证数据完整性时,重新计算哈希值并与之前存储的进行比对,以此判断数据是否被篡改。

2. 什么是对称加密和非对称加密

  • 要点

       1. 对称加密

  • 原理:使用相同的密钥进行数据的加密和解密操作。在通信过程中,发送方和接收方需要预先共享这个密钥。
  • 常见算法:包括 DES(Data Encryption Standard)、3DES(Triple DES)、AES(Advanced Encryption Standard)等。
  • 优缺点:优点是加密和解密速度快,处理效率高,适合对大量数据进行加密;缺点是密钥管理困难,因为密钥需要在通信双方之间安全传输,一旦密钥泄露,数据就会面临安全风险。

       2. 非对称加密

  • 原理:使用一对密钥,即公钥和私钥。公钥可以公开分发,任何人都可以使用公钥对数据进行加密;而私钥只有拥有者知道,用于解密用公钥加密的数据。

  • 常见算法:如 RSA、ECC(Elliptic Curve Cryptography)等。

  • 优缺点:优点是密钥管理相对简单,公钥可以公开,无需像对称加密那样进行安全传输。同时,它还可用于数字签名,确保数据的真实性和不可否认性;缺点是加密和解密速度相对较慢,不适合对大量数据进行加密。

对称加密(AES)

python

from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
import os

def aes_encrypt(data, key):
    """
    该函数使用 AES 算法对数据进行加密
    :param data: 待加密的字节数据
    :param key: 加密密钥(字节类型)
    :return: 加密后的字节数据
    """
    # 创建 AES 加密器,使用 ECB 模式
    cipher = AES.new(key, AES.MODE_ECB)
    # 填充数据以满足 AES 块大小要求
    padded_data = pad(data, AES.block_size)
    # 加密数据
    return cipher.encrypt(padded_data)

def aes_decrypt(encrypted_data, key):
    """
    该函数使用 AES 算法对加密数据进行解密
    :param encrypted_data: 加密后的字节数据
    :param key: 解密密钥(字节类型)
    :return: 解密后的原始数据
    """
    # 创建 AES 解密器,使用 ECB 模式
    decipher = AES.new(key, AES.MODE_ECB)
    # 解密数据
    decrypted_data = decipher.decrypt(encrypted_data)
    # 去除填充
    return unpad(decrypted_data, AES.block_size)

# 生成 16 字节的密钥
key = os.urandom(16)
# 要加密的数据
original_data = b"Hello, AES!"
# 调用加密函数
encrypted_data = aes_encrypt(original_data, key)
# 调用解密函数
decrypted_data = aes_decrypt(encrypted_data, key)

print(f"原始数据: {original_data}")
print(f"加密后的数据: {encrypted_data}")
print(f"解密后的数据: {decrypted_data}")

非对称加密(RSA)

python

from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP

def rsa_encrypt(data, public_key):
    """
    该函数使用 RSA 公钥对数据进行加密
    :param data: 待加密的字节数据
    :param public_key: RSA 公钥对象
    :return: 加密后的字节数据
    """
    # 创建 RSA 加密器
    cipher_rsa = PKCS1_OAEP.new(public_key)
    # 加密数据
    return cipher_rsa.encrypt(data)

def rsa_decrypt(encrypted_data, private_key):
    """
    该函数使用 RSA 私钥对加密数据进行解密
    :param encrypted_data: 加密后的字节数据
    :param private_key: RSA 私钥对象
    :return: 解密后的原始数据
    """
    # 创建 RSA 解密器
    cipher_rsa = PKCS1_OAEP.new(private_key)
    # 解密数据
    return cipher_rsa.decrypt(encrypted_data)

# 生成 RSA 密钥对
key = RSA.generate(2048)
private_key = key.export_key()
public_key = key.publickey().export_key()

# 加载公钥
recipient_key = RSA.import_key(public_key)
# 要加密的数据
original_data = b"Hello, RSA!"
# 调用加密函数
encrypted_data = rsa_encrypt(original_data, recipient_key)

# 加载私钥
private_key = RSA.import_key(private_key)
# 调用解密函数
decrypted_data = rsa_decrypt(encrypted_data, private_key)

print(f"原始数据: {original_data}")
print(f"加密后的数据: {encrypted_data}")
print(f"解密后的数据: {decrypted_data}")

  • 补充知识点

  1. 混合加密方案:结合对称加密和非对称加密的优势,先使用非对称加密算法安全地传输对称加密所需的密钥,然后使用对称加密算法对大量数据进行高效加密。这样既能保证密钥传输的安全性,又能提高数据加密的效率。

  2. 数字签名应用拓展:在实际应用中,数字签名不仅可用于验证数据的真实性和不可否认性,还可用于软件分发、电子合同签署等场景,确保数据在传输和存储过程中的完整性和合法性。

3. 什么是冒泡排序

  • 要点

冒泡排序是一种简单直观的排序算法,其核心思想是通过多次比较相邻元素的大小,若顺序错误则交换它们的位置,使得较大的元素逐步 “冒泡” 到数组的末尾。具体步骤为:从数组的第一个元素开始,依次比较相邻的两个元素,若前一个元素大于后一个元素,则交换它们的位置;重复这个过程,直到整个数组有序。

python

def bubble_sort(arr):
    """
    该函数实现冒泡排序算法
    :param arr: 待排序的列表
    :return: 排序后的列表
    """
    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]
    return arr

# 测试示例
original_array = [64, 34, 25, 12, 22, 11, 90]
sorted_array = bubble_sort(original_array)
print(f"原始数组: {original_array}")
print(f"排序后的数组: {sorted_array}")

  • 补充知识点

        1. 优化冒泡排序算法:在冒泡排序的基础上,添加一个标志位来记录每一轮是否发生了元素交换。如果某一轮没有发生交换,说明数组已经有序,可以提前结束排序过程,从而减少不必要的比较次数,提高算法效率。以下是优化后的代码:

python

def optimized_bubble_sort(arr):
    """
    该函数实现优化后的冒泡排序算法
    :param arr: 待排序的列表
    :return: 排序后的列表
    """
    n = len(arr)
    for i in range(n):
        swapped = False
        for j in range(0, n - i - 1):
            if arr[j] > arr[j + 1]:
                arr[j], arr[j + 1] = arr[j + 1], arr[j]
                swapped = True
        if not swapped:
            break
    return arr

original_array = [64, 34, 25, 12, 22, 11, 90]
sorted_array = optimized_bubble_sort(original_array)
print(f"原始数组: {original_array}")
print(f"排序后的数组: {sorted_array}")

        2. 双向冒泡排序(鸡尾酒排序):在传统冒泡排序的基础上进行改进,不仅从前往后比较和交换元素,还从后往前进行相同的操作,这样可以更快地将较大和较小的元素移到合适的位置,进一步提高排序效率。

4. 什么是快速排序

  • 要点

快速排序是一种高效的分治排序算法,其基本思想是通过选择一个基准元素,将数组分为两部分,使得左边部分的所有元素都小于等于基准元素,右边部分的所有元素都大于等于基准元素,然后分别对左右两部分递归地进行排序,最终得到一个有序的数组。具体步骤包括:选择基准元素、进行分区操作将数组划分为两部分、对左右子数组递归排序。

python

def quick_sort(arr):
    """
    该函数实现快速排序算法
    :param arr: 待排序的列表
    :return: 排序后的列表
    """
    if len(arr) <= 1:
        return arr
    else:
        # 选择第一个元素作为基准
        pivot = arr[0]
        # 小于等于基准的元素组成的列表
        left = [x for x in arr[1:] if x <= pivot]
        # 大于基准的元素组成的列表
        right = [x for x in arr[1:] if x > pivot]
        # 递归排序左右子数组,并合并结果
        return quick_sort(left) + [pivot] + quick_sort(right)

# 测试示例
original_array = [64, 34, 25, 12, 22, 11, 90]
sorted_array = quick_sort(original_array)
print(f"原始数组: {original_array}")
print(f"排序后的数组: {sorted_array}")

  • 补充知识点

        1. 随机选择基准元素:为了避免在某些特殊情况下(如数组已经有序)快速排序的性能下降,可采用随机选择基准元素的方法。这样可以使算法在不同输入数据下的性能更加稳定。以下是改进后的代码:

python

import random

def quick_sort_random_pivot(arr):
    """
    该函数实现随机选择基准元素的快速排序算法
    :param arr: 待排序的列表
    :return: 排序后的列表
    """
    if len(arr) <= 1:
        return arr
    else:
        # 随机选择一个基准元素的索引
        pivot_index = random.randint(0, len(arr) - 1)
        # 获取基准元素
        pivot = arr[pivot_index]
        # 将基准元素移到数组开头
        arr[0], arr[pivot_index] = arr[pivot_index], arr[0]
        # 小于等于基准的元素组成的列表
        left = [x for x in arr[1:] if x <= pivot]
        # 大于基准的元素组成的列表
        right = [x for x in arr[1:] if x > pivot]
        # 递归排序左右子数组,并合并结果
        return quick_sort_random_pivot(left) + [pivot] + quick_sort_random_pivot(right)

original_array = [64, 34, 25, 12, 22, 11, 90]
sorted_array = quick_sort_random_pivot(original_array)
print(f"原始数组: {original_array}")
print(f"排序后的数组: {sorted_array}")

        2. 三数取中法选择基准:另一种选择基准元素的方法是三数取中法,即从数组的开头、中间和结尾选取三个元素,然后选择这三个元素的中位数作为基准元素,这样可以进一步提高算法的性能。

5. 如何判断单向链表中是否有环

  • 要点

可以使用快慢指针的方法来判断单向链表中是否存在环。具体思路是:初始化两个指针,一个慢指针和一个快指针,都指向链表的头节点。慢指针每次移动一步,快指针每次移动两步。如果链表中存在环,那么快指针最终会追上慢指针,即两个指针会相遇;如果链表中不存在环,那么快指针会先到达链表的末尾(即快指针为 None 或快指针的下一个节点为 None)。

python

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

def hasCycle(head):
    """
    该函数用于判断单向链表中是否有环
    :param head: 链表的头节点
    :return: 如果有环返回 True,否则返回 False
    """
    slow = head
    fast = head
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next
        if slow == fast:
            return True
    return False

# 创建有环链表
node1 = ListNode(1)
node2 = ListNode(2)
node3 = ListNode(3)
node4 = ListNode(4)
node1.next = node2
node2.next = node3
node3.next = node4
node4.next = node2  # 创建环

print(f"链表是否有环: {hasCycle(node1)}")

  • 补充知识点

找到环的入口节点:当快慢指针相遇后,将其中一个指针重新指向链表头,然后两个指针都以每次一步的速度移动,再次相遇的节点即为环的入口节点。

python

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

def detectCycle(head):
    """
    该函数用于判断单向链表中是否有环,并找到环的入口节点
    :param head: 链表的头节点
    :return: 如果有环返回环的入口节点,否则返回 None
    """
    slow = head
    fast = head
    has_cycle = False
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next
        if slow == fast:
            has_cycle = True
            break
    if has_cycle:
        slow

6. 常用排序算法有哪些

  • 要点

排序算法是将一组数据按照特定顺序(通常是升序或降序)重新排列的方法。常见的排序算法可分为比较排序和非比较排序两大类。

  1. 比较排序:通过比较元素之间的大小关系来确定它们的顺序,包括冒泡排序、选择排序、插入排序、希尔排序、归并排序、快速排序和堆排序等。

  2. 非比较排序:不依赖元素之间的比较来确定顺序,而是利用数据的其他特性进行排序,如计数排序、桶排序和基数排序。


选择排序

python

def selection_sort(arr):
    """
    选择排序算法实现
    :param arr: 待排序的列表
    :return: 排序后的列表
    """
    n = len(arr)
    for i in range(n):
        min_index = i
        for j in range(i + 1, n):
            if arr[j] < arr[min_index]:
                min_index = j
        arr[i], arr[min_index] = arr[min_index], arr[i]
    return arr

arr = [64, 25, 12, 22, 11]
sorted_arr = selection_sort(arr)
print(f"原始数组: {arr}")
print(f"选择排序后的数组: {sorted_arr}")

插入排序

python

def insertion_sort(arr):
    """
    插入排序算法实现
    :param arr: 待排序的列表
    :return: 排序后的列表
    """
    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

arr = [64, 25, 12, 22, 11]
sorted_arr = insertion_sort(arr)
print(f"原始数组: {arr}")
print(f"插入排序后的数组: {sorted_arr}")

归并排序

python

def merge_sort(arr):
    """
    归并排序算法实现
    :param arr: 待排序的列表
    :return: 排序后的列表
    """
    if len(arr) <= 1:
        return arr
    mid = len(arr) // 2
    left = merge_sort(arr[:mid])
    right = merge_sort(arr[mid:])
    return merge(left, right)

def merge(left, right):
    """
    合并两个已排序的列表
    :param left: 左子列表
    :param right: 右子列表
    :return: 合并后的有序列表
    """
    result = []
    i = j = 0
    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.extend(left[i:])
    result.extend(right[j:])
    return result

arr = [64, 25, 12, 22, 11]
sorted_arr = merge_sort(arr)
print(f"原始数组: {arr}")
print(f"归并排序后的数组: {sorted_arr}")

堆排序

python

import heapq

def heap_sort(arr):
    """
    堆排序算法实现
    :param arr: 待排序的列表
    :return: 排序后的列表
    """
    heapq.heapify(arr)
    return [heapq.heappop(arr) for _ in range(len(arr))]

arr = [64, 25, 12, 22, 11]
sorted_arr = heap_sort(arr)
print(f"原始数组: {arr}")
print(f"堆排序后的数组: {sorted_arr}")

计数排序

python

def counting_sort(arr):
    """
    计数排序算法实现
    :param arr: 待排序的列表(元素应为非负整数)
    :return: 排序后的列表
    """
    if not arr:
        return arr
    max_val = max(arr)
    count = [0] * (max_val + 1)
    for num in arr:
        count[num] += 1
    result = []
    for i in range(len(count)):
        result.extend([i] * count[i])
    return result

arr = [4, 2, 2, 8, 3, 3, 1]
sorted_arr = counting_sort(arr)
print(f"原始数组: {arr}")
print(f"计数排序后的数组: {sorted_arr}")

  • 补充知识点

  1. 排序算法复杂度分析:不同的排序算法在时间复杂度和空间复杂度上有不同的表现。冒泡排序、选择排序和插入排序适合小规模数据的排序;而归并排序、快速排序和堆排序更适合大规模数据的排序。在实际应用中,需要根据数据规模、数据特点和性能要求选择合适的排序算法。

  2. 排序算法稳定性:排序算法的稳定性是指相等元素在排序前后的相对顺序是否保持不变。稳定的排序算法包括冒泡排序、插入排序和归并排序等;不稳定的排序算法包括选择排序、快速排序和堆排序等。在某些应用场景中,如对学生成绩进行排序时,需要保持相同成绩学生的原始顺序,此时就需要使用稳定的排序算法。

  3. 并行排序算法:对于大规模数据的排序,可以采用并行排序算法来提高排序效率。例如,并行归并排序可以利用多核处理器的优势,将数据分成多个子任务并行处理,从而显著减少排序时间。

7. 如何翻转一个单链表

  • 要点

翻转单链表可以使用迭代和递归两种方法。

  1. 迭代方法:通过遍历链表,依次改变节点的指针方向,将当前节点的 next 指针指向前一个节点,从而实现链表的翻转。需要使用三个指针:prev 用于记录前一个节点,curr 用于遍历当前节点,next_node 用于保存当前节点的下一个节点。

  2. 递归方法:先递归地翻转当前节点之后的链表,然后将当前节点的 next 指针指向前一个节点,最后返回新的头节点。递归方法的关键在于理解递归的终止条件和递归调用的过程。

迭代方法

python

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

def reverseList(head):
    """
    迭代方法翻转单链表
    :param head: 链表的头节点
    :return: 翻转后链表的头节点
    """
    prev = None
    curr = head
    while curr:
        next_node = curr.next
        curr.next = prev
        prev = curr
        curr = next_node
    return prev

# 创建链表
node1 = ListNode(1)
node2 = ListNode(2)
node3 = ListNode(3)
node1.next = node2
node2.next = node3

reversed_head = reverseList(node1)
current = reversed_head
result = []
while current:
    result.append(current.val)
    current = current.next
print(f"翻转后的链表值: {result}")

递归方法

python

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

def reverseList(head):
    """
    递归方法翻转单链表
    :param head: 链表的头节点
    :return: 翻转后链表的头节点
    """
    if not head or not head.next:
        return head
    new_head = reverseList(head.next)
    head.next.next = head
    head.next = None
    return new_head

# 创建链表
node1 = ListNode(1)
node2 = ListNode(2)
node3 = ListNode(3)
node1.next = node2
node2.next = node3

reversed_head = reverseList(node1)
current = reversed_head
result = []
while current:
    result.append(current.val)
    current = current.next
print(f"翻转后的链表值: {result}")

  • 补充知识点

        1. 部分翻转链表:实现只翻转链表中指定区间的节点。可以先找到需要翻转的区间的起始节点和结束节点,然后使用迭代或递归方法翻转该区间的节点,最后将翻转后的区间与原链表的其他部分连接起来。

python

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

def reverseBetween(head, left, right):
    if not head or left == right:
        return head
    dummy = ListNode(0)
    dummy.next = head
    pre = dummy
    for _ in range(left - 1):
        pre = pre.next
    cur = pre.next
    for _ in range(right - left):
        next_node = cur.next
        cur.next = next_node.next
        next_node.next = pre.next
        pre.next = next_node
    return dummy.next

# 创建链表
node1 = ListNode(1)
node2 = ListNode(2)
node3 = ListNode(3)
node4 = ListNode(4)
node5 = ListNode(5)
node1.next = node2
node2.next = node3
node3.next = node4
node4.next = node5

reversed_head = reverseBetween(node1, 2, 4)
current = reversed_head
result = []
while current:
    result.append(current.val)
    current = current.next
print(f"部分翻转后的链表值: {result}")

        2. 翻转双向链表:对于双向链表,除了要改变节点的 next 指针,还需要改变 prev 指针。可以使用类似迭代的方法,依次交换每个节点的 nextprev 指针。

8. 青蛙跳台阶问题,一只青蛙要跳上 n 层高的台阶,一次能跳一级,也可以跳两级,请问这只青蛙有多少种跳上这个 n 层台阶的方法

  • 要点

这是一个典型的动态规划问题,本质上是斐波那契数列问题。设 f(n) 表示跳上 n 层台阶的方法数。

  1. n = 1 时,只有一种跳法,即一次跳一级,所以 f(1) = 1

  2. n = 2 时,有两种跳法:一次跳两级或分两次每次跳一级,所以 f(2) = 2

  3. n > 2 时,青蛙最后一步要么是从 n - 1 级台阶跳一级上来,要么是从 n - 2 级台阶跳两级上来。因此,f(n) = f(n - 1) + f(n - 2)

python

def climbStairs(n):
    """
    计算青蛙跳上 n 层台阶的方法数
    :param n: 台阶的层数
    :return: 跳上 n 层台阶的方法数
    """
    if n == 1:
        return 1
    if n == 2:
        return 2
    a, b = 1, 2
    for i in range(3, n + 1):
        a, b = b, a + b
    return b

n = 5
print(f"青蛙跳上 {n} 层台阶的方法数: {climbStairs(n)}")

  • 补充知识点

        1. 青蛙一次可跳多级台阶:若青蛙一次可以跳 1 级、2 级、...、m 级台阶,可通过动态规划的思想推导出更通用的递推公式。设 f(n) 表示跳上 n 层台阶的方法数,则有:
 

python

def climbStairsMultiSteps(n, m):
    dp = [0] * (n + 1)
    dp[0] = 1
    for i in range(1, n + 1):
        for j in range(1, min(i, m) + 1):
            dp[i] += dp[i - j]
    return dp[n]

n = 5
m = 3
print(f"青蛙一次可跳 1 到 {m} 级台阶,跳上 {n} 层台阶的方法数: {climbStairsMultiSteps(n, m)}")

        2. 带有障碍物的台阶问题:在台阶上设置障碍物,青蛙不能跳到有障碍物的台阶上,需要对算法进行相应调整。可以使用一个数组来标记每个台阶是否有障碍物,然后在动态规划的过程中,跳过有障碍物的台阶。

python

def climbStairsWithObstacles(obstacles):
    n = len(obstacles)
    dp = [0] * n
    if obstacles[0] == 0:
        dp[0] = 1
    for i in range(1, n):
        if obstacles[i] == 0:
            if i == 1:
                dp[i] = dp[i - 1]
            else:
                dp[i] = dp[i - 1] + dp[i - 2]
    return dp[n - 1]

obstacles = [0, 0, 1, 0, 0]
print(f"带有障碍物的台阶问题,跳上最后一级台阶的方法数: {climbStairsWithObstacles(obstacles)}")

9. 如何求两数之和

  • 要点

给定一个整数数组 nums 和一个目标值 target,要求找出数组中两个数的和等于目标值的两个数的索引。可以使用哈希表来解决这个问题,具体思路如下:

  1. 遍历数组,对于每个元素 num,计算 target - num

  2. 检查 target - num 是否在哈希表中。如果存在,则返回这两个数的索引。

  3. 如果 target - num 不在哈希表中,将当前元素 num 及其索引存入哈希表。

python

def twoSum(nums, target):
    """
    找出数组中两数之和等于目标值的两个数的索引
    :param nums: 整数数组
    :param target: 目标值
    :return: 两个数的索引列表
    """
    num_dict = {}
    for i, num in enumerate(nums):
        complement = target - num
        if complement in num_dict:
            return [num_dict[complement], i]
        num_dict[num] = i
    return []

nums = [2, 7, 11, 15]
target = 9
result = twoSum(nums, target)
print(f"数组 {nums} 中两数之和等于 {target} 的两个数的索引: {result}")

  • 补充知识点

        1. 三数之和问题:找出数组中三个数的和等于目标值的所有不重复组合。可以先对数组进行排序,然后固定一个数,再使用双指针法在剩余的数组中寻找另外两个数,使得它们的和等于目标值减去固定的数。

python

def threeSum(nums):
    nums.sort()
    result = []
    n = len(nums)
    for i in range(n - 2):
        if i > 0 and nums[i] == nums[i - 1]:
            continue
        left, right = i + 1, n - 1
        while left < right:
            total = nums[i] + nums[left] + nums[right]
            if total < 0:
                left += 1
            elif total > 0:
                right -= 1
            else:
                result.append([nums[i], nums[left], nums[right]])
                while left < right and nums[left] == nums[left + 1]:
                    left += 1
                while left < right and nums[right] == nums[right - 1]:
                    right -= 1
                left += 1
                right -= 1
    return result

nums = [-1, 0, 1, 2, -1, -4]
print(f"数组 {nums} 中三数之和等于 0 的所有不重复组合: {threeSum(nums)}")

        2. 两数之和的变体:如找出两数之和最接近目标值的组合。可以先对数组进行排序,然后使用双指针法遍历数组,记录两数之和与目标值的最小差值以及对应的组合。

10. 如何搜索旋转排序数组

  • 要点

  1. 问题背景:原本升序排列的数组在某个未知点进行了旋转,例如 [0, 1, 2, 4, 5, 6, 7] 可能变为 [4, 5, 6, 7, 0, 1, 2]。要在这样的旋转排序数组中查找目标值 target,若存在则返回其索引,不存在则返回 -1。

  2. 算法核心:采用二分查找算法。由于数组旋转后部分有序,需要根据中间元素与左右边界元素的大小关系判断哪部分是有序的,再结合目标值是否在有序部分内来缩小搜索范围。

python

def search(nums, target):
    """
    在旋转排序数组中搜索目标值
    :param nums: 旋转排序数组
    :param target: 目标值
    :return: 目标值的索引,若不存在则返回 -1
    """
    left, right = 0, len(nums) - 1
    while left <= right:
        mid = (left + right) // 2
        if nums[mid] == target:
            return mid
        # 判断左半部分是否有序
        if nums[left] <= nums[mid]:
            # 目标值在左半部分有序区间内
            if nums[left] <= target < nums[mid]:
                right = mid - 1
            else:
                left = mid + 1
        # 左半部分无序,则右半部分有序
        else:
            # 目标值在右半部分有序区间内
            if nums[mid] < target <= nums[right]:
                left = mid + 1
            else:
                right = mid - 1
    return -1


nums = [4, 5, 6, 7, 0, 1, 2]
target = 0
print(f"数组 {nums} 中目标值 {target} 的索引为: {search(nums, target)}")

  • 补充知识点

        1. 处理重复元素:当数组中存在重复元素时,原有的二分查找逻辑可能会失效。例如 [1, 3, 1, 1, 1] 这样的数组,在判断哪部分有序时会出现歧义。可以在每次比较时,当遇到边界元素和中间元素相等的情况,将边界指针向内移动一位,继续进行二分查找。

python

def search_with_duplicates(nums, target):
    left, right = 0, len(nums) - 1
    while left <= right:
        mid = (left + right) // 2
        if nums[mid] == target:
            return mid
        # 处理重复元素,当左边界和中间元素相等时,左指针右移
        if nums[left] == nums[mid]:
            left += 1
        # 判断左半部分是否有序
        elif nums[left] < nums[mid]:
            if nums[left] <= target < nums[mid]:
                right = mid - 1
            else:
                left = mid + 1
        # 左半部分无序,则右半部分有序
        else:
            if nums[mid] < target <= nums[right]:
                left = mid + 1
            else:
                right = mid - 1
    return -1


nums = [1, 3, 1, 1, 1]
target = 3
print(f"含重复元素的数组 {nums} 中目标值 {target} 的索引为: {search_with_duplicates(nums, target)}")

       2. 查找旋转点:除了查找目标值,还可以进一步找出数组的旋转点,即数组中最小元素的索引。可以使用二分查找,当中间元素大于右边界元素时,说明旋转点在右半部分;当中间元素小于左边界元素时,说明旋转点在左半部分;当中间元素既不大于右边界元素也不小于左边界元素时,说明数组是有序的,旋转点就是左边界元素。

python

def find_rotation_point(nums):
    left, right = 0, len(nums) - 1
    while left < right:
        mid = (left + right) // 2
        if nums[mid] > nums[right]:
            left = mid + 1
        else:
            right = mid
    return left


nums = [4, 5, 6, 7, 0, 1, 2]
print(f"数组 {nums} 的旋转点索引为: {find_rotation_point(nums)}")

 

友情提示:本文已经整理成文档,可以到如下链接免积分下载阅读

https://download.csdn.net/download/ylfhpy/90436291

相关文章:

  • ISIS(中间系统到中间系统)——基础
  • header在spring boot中解析
  • 什么是元数据管理?为什么数据治理的第一步是整理元数据?
  • docker container 修改 Mounts
  • Visual Studio 使用 GitHub Copilot 与 IntelliCode 辅助编码 【AI辅助开发系列】
  • SpringBoot AOP 源码解析
  • Selenium 不同语言绑定版本的官方操作文档获取途径(科学上网)
  • WPF12-MVVM
  • 禹神:一小时快速上手Electron,前端Electron开发教程,笔记。一篇文章入门Electron
  • 牛客刷题自留-深度学习
  • 【cv】vs2022配置opencv
  • flutter 局部刷新控件Selector源码实现原理
  • Spring之Bean的生命周期过程中调用的方法
  • MySQL -操作
  • 12个大语言模型平台对比测试-搜索视角
  • 网络安全(黑客技术)一2025年自学入门手册_合天网安-零基础系统学习网络安全教程下载
  • 使用MATLAB结合EasySpin进行ESR模拟的详细步骤及示例代码
  • 达梦数据库授权给某个用户查询其他指定用户下所有表的权限
  • OSPF BIT 类型说明
  • labview中VISA串口出现异常的解决方案
  • 催眠美女做仆人网站/seo索引擎优化
  • 哈尔滨网站建设兼职/soso搜索引擎
  • 济南网站建设外包公司/百度指数大数据
  • 科技类网站设计特点/网站设计与建设
  • 上海定制网站建设公司/龙岗网络公司
  • 湛江模板建站多少钱/株洲seo优化报价