螺旋矩阵 II:从理论到实践的五种算法解析

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: 螺旋矩阵 II:从理论到实践的五种算法解析

作者介绍:10年大厂数据\经营分析经验,现任大厂数据部门负责人。

会一些的技术:数据分析、算法、SQL、大数据相关、python

欢迎加入社区:码上找工作

作者专栏每日更新:

LeetCode解锁1000题: 打怪升级之旅

python数据分析可视化:企业实战案例

备注说明:方便大家阅读,统一使用python,带必要注释,公众号 数据分析螺丝钉 一起打怪升级

题目描述

给定一个链表,旋转链表,将链表每个节点向右移动 k 个位置,其中 k 是非负数。

输入格式
  • head:链表的头节点。
  • k:一个整数,表示旋转的位置数。
输出格式
  • 返回旋转后的链表的头节点。

示例

示例 1
输入: 1->2->3->4->5->NULL, k = 2
输出: 4->5->1->2->3->NULL
解释:
向右旋转 1 步: 5->1->2->3->4->NULL
向右旋转 2 步: 4->5->1->2->3->NULL
示例 2
输入: 0->1->2->NULL, k = 4
输出: 2->0->1->NULL
解释:
向右旋转 1 步: 2->0->1->NULL
向右旋转 2 步: 1->2->0->NULL
向右旋转 3 步: 0->1->2->NULL
向右旋转 4 步: 2->0->1->NULL

方法一:闭合为环

解题步骤
  1. 链表长度:首先遍历整个链表得到链表长度 n 和最后一个节点。
  2. 闭合成环:将链表的尾节点连接到头节点,形成环。
  3. 计算新头位置:根据 k % n 计算新的头节点的位置。
  4. 重置头节点:找到新的头节点和尾节点,断开环。
完整的规范代码
class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next
def rotateRight(head, k):
    """
    使用闭合环的方法进行链表旋转
    :param head: ListNode, 链表的头节点
    :param k: int, 旋转的位置数
    :return: ListNode, 旋转后的链表头节点
    """
    if not head or not head.next or k == 0:
        return head
    
    # 计算链表长度并形成环
    lastElement = head
    length = 1
    while lastElement.next:
        lastElement = lastElement.next
        length += 1
    lastElement.next = head
    
    # 找到新的尾部:(长度 - k % 长度 - 1)的位置
    k = k % length
    steps_to_new_tail = length - k
    new_tail = head
    for i in range(steps_to_new_tail - 1):
        new_tail = new_tail.next
    
    new_head = new_tail.next
    
    # 断开环
    new_tail.next = None
    
    return new_head
# 示例代码用于创建链表和打印链表
def create_list(lst):
    head = current = ListNode(lst[0])
    for number in lst[1:]:
        current.next = ListNode(number)
        current = current.next
    return head
def print_list(node):
    while node:
        print(node.val, end=" -> ")
        node = node.next
    print("NULL")
# 示例调用
lst = create_list([1, 2, 3, 4, 5])
rotated_lst = rotateRight(lst, 2)
print_list(rotated_lst)  # 输出: 4 -> 5 -> 1 -> 2 -> 3 -> NULL
算法分析
  • 时间复杂度:(O(n)),主要时间开销来自于链表遍历和旋转计算。
  • 空间复杂度:(O(1)),除了给定的链表结构,只使用常数额外空间。

方法二:数组模拟

解题步骤
  1. 转换为数组:遍历链表,存储每个节点的值到数组中。
  2. 数组旋转:对数组进行旋转操作。
  3. 重建链表:根据旋转后的数组重建链表。
完整的规范代码
def rotateRight(head, k):
    """
    使用数组模拟链表旋转
    :param head: ListNode, 链表的头节点
    :param k: int, 旋转的位置数
    :return: ListNode, 旋转后的链表头节点
    """
    if not head or not head.next or k == 0:
        return head
    
    # 转换链表到数组
    nodes = []
    current = head
    while current:
        nodes.append(current.val)
        current = current.next
    
    # 数组旋转
    k = k % len(nodes)
    nodes = nodes[-k:] + nodes[:-k]
    
    # 重建链表
    new_head = current = ListNode(nodes[0])
    for val in nodes[1:]:
        current.next = ListNode(val)
        current = current.next
    
    return new_head
# 示例调用
lst = create_list([1, 2, 3, 4, 5])
rotated_lst = rotateRight(lst, 2)
print_list(rotated_lst)  # 输出: 4 -> 5 -> 1 -> 2 -> 3 -> NULL
算法分析
  • 时间复杂度:(O(n)),包括链表到数组的转换和数组的旋转。
  • 空间复杂度:(O(n)),需要额外空间来存储链表元素的数组。

方法三:直接旋转

解题步骤
  1. 链表遍历:计算链表长度并连接成环。
  2. 计算新尾部:根据旋转次数确定新的尾部位置。
  3. 重建链表:在新的尾部断开环,形成新的链表。
完整的规范代码
def rotateRight(head, k):
    """
    直接通过链表旋转实现
    :param head: ListNode, 链表的头节点
    :param k: int, 旋转的位置数
    :return: ListNode, 旋转后的链表头节点
    """
    if not head or not head.next or k == 0:
        return head
    
    # 计算长度并形成环
    old_tail = head
    length = 1
    while old_tail.next:
        old_tail = old_tail.next
        length += 1
    old_tail.next = head
    
    # 计算新尾部位置
    k = k % length
    new_tail = head
    for i in range(length - k - 1):
        new_tail = new_tail.next
    
    new_head = new_tail.next
    new_tail.next = None
    
    return new_head
# 示例调用
lst = create_list([1, 2, 3, 4, 5])
rotated_lst = rotateRight(lst, 2)
print_list(rotated_lst)  # 输出: 4 -> 5 -> 1 -> 2 -> 3 -> NULL
算法分析
  • 时间复杂度:(O(n)),需要遍历链表两次。
  • 空间复杂度:(O(1)),仅使用常数额外空间。

方法四:改进的数组模拟

解题步骤
  1. 转换为数组:将链表节点转换为数组,方便随机访问。
  2. 数组旋转:对数组进行旋转操作。
  3. 重建链表:根据旋转后的数组重建链表。
完整的规范代码
def rotateRight(head, k):
    """
    使用改进的数组模拟方法旋转链表
    :param head: ListNode, 链表的头节点
    :param k: int, 旋转的位置数
    :return: ListNode, 旋转后的链表头节点
    """
    if not head:
        return None
    
    # 转换链表到数组
    nodes = []
    while head:
        nodes.append(head)
        head = head.next
    
    k = k % len(nodes)
    nodes = nodes[-k:] + nodes[:-k]
    
    # 重建链表
    for i in range(len(nodes) - 1):
        nodes[i].next = nodes[i + 1]
    nodes[-1].next = None
    
    return nodes[0]
# 示例调用
lst = create_list([1, 2, 3, 4, 5])
rotated_lst = rotateRight(lst, 2)
print_list(rotated_lst)  # 输出: 4 -> 5 -> 1 -> 2 -> 3 -> NULL
算法分析
  • 时间复杂度:(O(n)),链表到数组的转换和数组旋转各需要 (O(n))。
  • 空间复杂度:(O(n)),需要额外空间来存储节点指针的数组。

方法五:反向索引旋转

解题步骤
  1. 链表遍历:一次遍历计算链表长度并形成尾部连接。
  2. 反向计算头部:使用长度减去 (k % \text{长度}) 计算新头部和尾部位置。
  3. 断开连接:在新尾部断开链表,形成新的链表结构。
完整的规范代码
def rotateRight(head, k):
    """
    使用反向索引旋转链表
    :param head: ListNode, 链表的头节点
    :param k: int, 旋转的位置数
    :return: ListNode, 旋转后的链表头节点
    """
    if not head or not head.next or k == 0:
        return head
    
    # 完成一次完整遍历以确定长度并形成环
    last = head
    length = 1
    while last.next:
        last = last.next
        length += 1
    last.next = head
    
    # 确定新的头部和尾部
    k = k % length
    if k:
        for _ in range(length - k):
            last = last.next
    head = last.next
    last.next = None
    
    return head
# 示例调用
lst = create_list([1, 2, 3, 4, 5])
rotated_lst = rotateRight(lst, 2)
print_list(rotated_lst)  # 输出: 4 -> 5 -> 1 -> 2 -> 3 -> NULL
算法分析
  • 时间复杂度:(O(n)),可能需要遍历链表两次(计算长度和调整位置)。
  • 空间复杂度:(O(1)),不需要额外的存储空间,除了输入的链表。

不同算法的优劣势对比

特征 方法一: 闭环 方法二: 数组模拟 方法三: 直接旋转 方法四: 改进数组模拟 方法五: 反向索引旋转
时间复杂度 (O(n)) (O(n)) (O(n)) (O(n)) (O(n))
空间复杂度 (O(1)) (O(n)) (O(1)) (O(n)) (O(1))
优势 直观简单,实现快速 方便理解和实现,便于调试 减少了中间转换步骤,提高效率 易于理解和操作,灵活处理 计算头尾连接,避免额外空间,快速
劣势 需要特别处理新的头尾 需要额外空间存储数组,增加空间复杂度 需要准确计算长度和剩余部分 需要额外空间且稍复杂 需要两次遍历,稍增时间开销

应用示例

游戏开发中的角色队列管理

在多人在线游戏中,经常需要管理玩家的队列,例如在战场游戏中旋转玩家的出场顺序。使用旋转链表的算法可以有效地管理这种动态变化的玩家队列,确保每个玩家都能公平地参与游戏,同时也支持快速的动态调整。

欢迎关注微信公众号 数据分析螺丝钉

相关文章
|
26天前
|
存储 缓存 安全
Java内存模型深度解析:从理论到实践####
【10月更文挑战第21天】 本文深入探讨了Java内存模型(JMM)的核心概念与底层机制,通过剖析其设计原理、内存可见性问题及其解决方案,结合具体代码示例,帮助读者构建对JMM的全面理解。不同于传统的摘要概述,我们将直接以故事化手法引入,让读者在轻松的情境中领略JMM的精髓。 ####
33 6
|
23天前
|
运维 持续交付 云计算
深入解析云计算中的微服务架构:原理、优势与实践
深入解析云计算中的微服务架构:原理、优势与实践
56 1
|
1月前
|
消息中间件 存储 缓存
十万订单每秒热点数据架构优化实践深度解析
【11月更文挑战第20天】随着互联网技术的飞速发展,电子商务平台在高峰时段需要处理海量订单,这对系统的性能、稳定性和扩展性提出了极高的要求。尤其是在“双十一”、“618”等大型促销活动中,每秒需要处理数万甚至数十万笔订单,这对系统的热点数据处理能力构成了严峻挑战。本文将深入探讨如何优化架构以应对每秒十万订单级别的热点数据处理,从历史背景、功能点、业务场景、底层原理以及使用Java模拟示例等多个维度进行剖析。
54 8
|
16天前
|
机器学习/深度学习 人工智能 算法
深入解析图神经网络:Graph Transformer的算法基础与工程实践
Graph Transformer是一种结合了Transformer自注意力机制与图神经网络(GNNs)特点的神经网络模型,专为处理图结构数据而设计。它通过改进的数据表示方法、自注意力机制、拉普拉斯位置编码、消息传递与聚合机制等核心技术,实现了对图中节点间关系信息的高效处理及长程依赖关系的捕捉,显著提升了图相关任务的性能。本文详细解析了Graph Transformer的技术原理、实现细节及应用场景,并通过图书推荐系统的实例,展示了其在实际问题解决中的强大能力。
106 30
|
16天前
|
存储 网络协议 编译器
【C语言】深入解析C语言结构体:定义、声明与高级应用实践
通过根据需求合理选择结构体定义和声明的放置位置,并灵活结合动态内存分配、内存优化和数据结构设计,可以显著提高代码的可维护性和运行效率。在实际开发中,建议遵循以下原则: - **模块化设计**:尽可能封装实现细节,减少模块间的耦合。 - **内存管理**:明确动态分配与释放的责任,防止资源泄漏。 - **优化顺序**:合理排列结构体成员以减少内存占用。
86 14
|
20天前
|
存储 算法
深入解析PID控制算法:从理论到实践的完整指南
前言 大家好,今天我们介绍一下经典控制理论中的PID控制算法,并着重讲解该算法的编码实现,为实现后续的倒立摆样例内容做准备。 众所周知,掌握了 PID ,就相当于进入了控制工程的大门,也能为更高阶的控制理论学习打下基础。 在很多的自动化控制领域。都会遇到PID控制算法,这种算法具有很好的控制模式,可以让系统具有很好的鲁棒性。 基本介绍 PID 深入理解 (1)闭环控制系统:讲解 PID 之前,我们先解释什么是闭环控制系统。简单说就是一个有输入有输出的系统,输入能影响输出。一般情况下,人们也称输出为反馈,因此也叫闭环反馈控制系统。比如恒温水池,输入就是加热功率,输出就是水温度;比如冷库,
143 15
|
22天前
|
弹性计算 持续交付 API
构建高效后端服务:微服务架构的深度解析与实践
在当今快速发展的软件行业中,构建高效、可扩展且易于维护的后端服务是每个技术团队的追求。本文将深入探讨微服务架构的核心概念、设计原则及其在实际项目中的应用,通过具体案例分析,展示如何利用微服务架构解决传统单体应用面临的挑战,提升系统的灵活性和响应速度。我们将从微服务的拆分策略、通信机制、服务发现、配置管理、以及持续集成/持续部署(CI/CD)等方面进行全面剖析,旨在为读者提供一套实用的微服务实施指南。
|
16天前
|
存储 缓存 Python
Python中的装饰器深度解析与实践
在Python的世界里,装饰器如同一位神秘的魔法师,它拥有改变函数行为的能力。本文将揭开装饰器的神秘面纱,通过直观的代码示例,引导你理解其工作原理,并掌握如何在实际项目中灵活运用这一强大的工具。从基础到进阶,我们将一起探索装饰器的魅力所在。
|
17天前
|
机器学习/深度学习 搜索推荐 API
淘宝/天猫按图搜索(拍立淘)API的深度解析与应用实践
在数字化时代,电商行业迅速发展,个性化、便捷性和高效性成为消费者新需求。淘宝/天猫推出的拍立淘API,利用图像识别技术,提供精准的购物搜索体验。本文深入探讨其原理、优势、应用场景及实现方法,助力电商技术和用户体验提升。
|
24天前
|
安全 持续交付 Docker
深入理解并实践容器化技术——Docker 深度解析
深入理解并实践容器化技术——Docker 深度解析
43 2

推荐镜像

更多
下一篇
DataWorks