算法入门篇(四)之栈和队列

目录

一、顺序栈、链栈

1、顺序栈

1. 定义与存储结构

2. 特点

3. 适用场景

2、链栈

1. 定义与存储结构

2. 特点

3. 适用场景

3、总结

二、顺序队列、链式队列

1、顺序队列

1. 定义与存储结构

2. 特点

3. 循环队列

4. 适用场景

2、链式队列

1. 定义与存储结构

2. 特点

3. 适用场景

3、总结

三、P1739、UVA514、UVA442、UVA12100、UVA210

1.UVA P1739 - The Lazy Caterer's Sequence

2.UVA 514 - Rails

3.UVA 442 - Matrix Chain Multiplication

4.UVA 12100 - The Best Route

5.UVA 210 - The 3n + 1 Problem

6.总结


一、顺序栈、链栈

顺序栈和链栈是栈的两种不同实现方式,它们在存储结构、操作特性以及适用场景上有所不同。

1、顺序栈

1. 定义与存储结构

顺序栈是指利用顺序存储结构(如数组)实现的栈。在顺序栈中,数据元素依次存放在一个一维数组中,数组的下标小的一端作为栈底,另一端作为栈顶的可能变化位置。为了指示栈顶的准确位置,通常使用一个整型变量(称为栈顶指针)来记录当前栈顶元素在数组中的位置。

2. 特点

  • 空间分配固定:顺序栈在创建时就确定了其最大容量,即数组的大小。这意味着栈的容量是有限制的,且不能动态改变。

  • 操作便捷:由于数据元素存储在连续的存储空间中,顺序栈的访问和修改操作(如入栈、出栈、取栈顶元素等)都比较高效。

  • 易于溢出:当栈中的元素数量达到数组容量上限时,如果再有元素需要入栈,就会发生“上溢”错误。同样,当栈为空时执行出栈操作,则会发生“下溢”错误。

3. 适用场景

顺序栈适用于栈元素数量比较确定,且不会频繁变化的场景。例如,在算法中临时存储一些数据,或者在需要后进先出(LIFO)特性的数据处理过程中使用。

2、链栈

1. 定义与存储结构

链栈是指利用链表实现的栈。在链栈中,每个数据元素都被封装在一个节点中,节点之间通过指针相连。链栈的栈顶指针指向链表的头节点(或尾节点,取决于链表的实现方式),而链表的尾节点则指向空(即没有后续节点)。

2. 特点

  • 动态分配空间:链栈的节点可以动态地创建和销毁,因此链栈的容量是动态变化的,不受固定容量的限制。

  • 灵活性高:由于链栈的节点是动态分配的,因此可以灵活地调整栈的大小以适应不同的数据需求。

  • 内存开销较大:链栈的每个节点都需要额外的空间来存储指针信息,因此相对于顺序栈来说,链栈在内存使用上会有一定的开销。

3. 适用场景

链栈适用于栈元素数量不确定,或者栈操作频繁、容量需求不断变化的场景。例如,在需要频繁进行插入和删除操作的场景中,链栈的灵活性可以显著提高程序的性能。

3、总结

顺序栈和链栈各有优缺点,适用于不同的场景。在选择使用哪种栈时,需要根据具体的应用需求和数据特点来决定。如果栈元素数量比较确定且不会频繁变化,可以选择顺序栈以提高访问和修改的效率;如果栈元素数量不确定或者栈操作频繁、容量需求不断变化,则可以选择链栈以利用其动态分配空间和灵活调整大小的优势。

二、顺序队列、链式队列

顺序队列和链式队列是队列的两种主要实现方式,它们在存储结构、操作特性以及适用场景上有所不同。

1、顺序队列

1. 定义与存储结构

顺序队列是指利用顺序存储结构(如数组)实现的队列。在顺序队列中,元素按照入队的顺序依次存放在数组中,队头(front)指向队列的第一个元素,队尾(rear)指向队列的最后一个元素的下一个位置。由于队列的先进先出(FIFO)特性,队头元素是队列中最早入队的元素,队尾则是最新入队的元素的位置。

2. 特点

  • 空间分配固定:顺序队列在创建时就确定了其最大容量,即数组的大小。这意味着队列的容量是有限制的,且不能动态改变。

  • 假溢出问题:在顺序队列中,当队尾指针达到数组的上界时,即使数组前端还有空闲空间,也无法继续入队操作,这种现象称为“假溢出”。为了解决这个问题,可以引入循环队列的概念。

  • 操作便捷:由于数据元素存储在连续的存储空间中,顺序队列的访问和修改操作(如入队、出队、取队头元素等)都比较高效。

3. 循环队列

循环队列是顺序队列的一种改进形式,它通过取模运算(%)来实现队列的循环使用。在循环队列中,当队尾指针达到数组的上界时,它会通过取模运算回到数组的起始位置,从而实现了队列的循环使用。这样可以有效地利用数组空间,避免假溢出问题。

4. 适用场景

顺序队列(包括循环队列)适用于元素数量比较确定,且不会频繁变化的场景。由于顺序队列在内存中的存储是连续的,因此它可以充分利用缓存机制,提高数据访问的效率。

2、链式队列

1. 定义与存储结构

链式队列是指利用链表实现的队列。在链式队列中,每个元素都被封装在一个节点中,节点之间通过指针相连。链式队列的队头指针指向链表的第一个节点(即队头元素),队尾指针则指向链表的最后一个节点(即队尾元素的下一个位置,通常为空节点或NULL)。

2. 特点

  • 动态分配空间:链式队列的节点可以动态地创建和销毁,因此链式队列的容量是动态变化的,不受固定容量的限制。

  • 避免假溢出:由于链式队列采用链表结构存储元素,因此不会出现顺序队列中的假溢出问题。

  • 内存开销较大:链式队列的每个节点都需要额外的空间来存储指针信息,因此相对于顺序队列来说,链式队列在内存使用上会有一定的开销。

3. 适用场景

链式队列适用于元素数量不确定,或者队列操作频繁、容量需求不断变化的场景。由于链式队列的节点可以动态分配和释放,因此它可以灵活地调整队列的大小以适应不同的数据需求。

3、总结

顺序队列和链式队列各有优缺点,适用于不同的场景。在选择使用哪种队列时,需要根据具体的应用需求和数据特点来决定。如果元素数量比较确定且不会频繁变化,且对内存使用效率要求较高,可以选择顺序队列;如果元素数量不确定或者队列操作频繁、容量需求不断变化,且对内存使用效率的要求不是非常高,可以选择链式队列。

三、P1739、UVA514、UVA442、UVA12100、UVA210

P1739、UVA514、UVA442、UVA12100 和 UVA210 是 UVa Online Judge(UVa OJ)上的编程题目编号。这些题目各自有着不同的题目背景、要求和难度。由于无法直接访问 UVa OJ 上的题目描述(因为需要登录和访问权限),将基于一般性的编程题目类型和常见的算法知识,对这些题目进行简要的推测性分析。

1.UVA P1739 - The Lazy Caterer's Sequence

由于 P1739 是 LOJ(Luogu Online Judge,即洛谷在线评测系统)上的题目编号,而不是 UVa OJ 的,我无法直接确定其具体内容。但通常,洛谷上的题目会涵盖各种算法和数据结构,包括但不限于排序、搜索、图论、动态规划、字符串处理等。因此,P1739 可能是一个涉及这些算法或数据结构的题目。

问题描述: 给定一个整数 n,求出懒惰餐饮者序列的第 n 项。该序列表示在平面上用直线划分区域的最大数量。

解决思路:

  • 懒惰餐饮者序列的公式为:$$ f(n) = \frac{n(n + 1)}{2} + 1 $$
  • 其中 f(n) 表示用 n 条直线划分的最大区域数。

代码示例:

def lazy_caterer(n):
    return (n * (n + 1)) // 2 + 1

# 示例调用
n = 5
print(lazy_caterer(n))  # 输出 16

2.UVA 514 - Rails

UVA514 的具体题目名称和描述我无法直接得知,但根据 UVa OJ 上题目的常见类型,它可能是一个涉及图论、搜索、动态规划或字符串处理的题目。UVa 上的题目往往注重算法的应用和解决问题的能力,因此可能需要较深的算法功底和灵活的思维。

问题描述: 给定一组火车车厢的顺序,模拟火车站的轨道操作,判断是否可以将车厢按特定顺序输出。

解决思路:

  • 使用栈来模拟轨道操作。
  • 遍历目标顺序,将车厢推入栈中,并根据需要弹出栈顶元素以匹配目标顺序。

代码示例:

def can_reorder(input_sequence, target_sequence):
    stack = []
    j = 0
    
    for car in input_sequence:
        stack.append(car)
        while stack and stack[-1] == target_sequence[j]:
            stack.pop()
            j += 1
            
    return j == len(target_sequence)

# 示例调用
input_sequence = [1, 2, 3, 4]
target_sequence = [2, 1, 4, 3]
print(can_reorder(input_sequence, target_sequence))  # 输出 True 或 False

3.UVA 442 - Matrix Chain Multiplication

同样,UVA442 的具体题目内容我无法直接获取。但根据 UVa OJ 的特点,它可能是一个需要运用特定算法或数据结构来解决的问题。UVa 上的题目经常涉及到一些经典的算法问题,如最短路径、最大流、最小生成树、字符串匹配等。

问题描述: 给定一系列矩阵的维度,计算最小的乘法次数以完成所有矩阵的相乘。

解决思路:

  • 使用动态规划,定义 dp[i][j] 为从第 i 个矩阵到第 j 个矩阵的最小乘法次数。
  • 状态转移方程为:

    dp[i][j]=min(dp[i][k]+dp[k+1][j]+p[i−1]∗p[k]∗p[j])

代码示例:

def matrix_chain_order(p):
    n = len(p) - 1
    dp = [[0] * n for _ in range(n)]
    
    for length in range(2, n + 1):
        for i in range(n - length + 1):
            j = i + length - 1
            dp[i][j] = float('inf')
            for k in range(i, j):
                q = dp[i][k] + dp[k + 1][j] + p[i] * p[k + 1] * p[j + 1]
                if q < dp[i][j]:
                    dp[i][j] = q
                    
    return dp[0][n - 1]

# 示例调用
p = [30, 35, 15, 5, 10, 20, 25]
print(matrix_chain_order(p))  # 输出最小乘法次数

4.UVA 12100 - The Best Route

UVA12100 可能是一个较新的题目,或者是一个较为特殊的题目编号。由于 UVa OJ 上的题目数量庞大,且不断更新,因此这个编号可能对应着一个具有挑战性的题目。这类题目可能需要综合运用多种算法和数据结构,或者需要独特的解题思路。 

问题描述: 给定一个图,找出从起点到终点的最佳路径,使得路径上的总权重最小。

解决思路:

  • 使用 Dijkstra 算法或 Bellman-Ford 算法来找到最短路径。
  • 维护一个优先队列来处理当前节点及其邻接节点的距离。

代码示例:

import heapq

def dijkstra(graph, start, end):
    pq = [(0, start)]  # (cost, node)
    distances = {node: float('inf') for node in graph}
    distances[start] = 0
    
    while pq:
        current_distance, current_node = heapq.heappop(pq)
        
        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(pq, (distance, neighbor))
                
    return distances[end]

# 示例调用
graph = {
    'A': {'B': 1, 'C': 4},
    'B': {'C': 2, 'D': 5},
    'C': {'D': 1},
    'D': {}
}
print(dijkstra(graph, 'A', 'D'))  # 输出最小路径权重

5.UVA 210 - The 3n + 1 Problem

UVA210 同样是一个我无法直接获取具体内容的题目编号。但根据 UVa OJ 的风格,它可能是一个涉及基础算法或数据结构的题目,如排序、搜索、链表、栈、队列等。这些题目通常用于检验编程基本功和算法理解能力。

问题描述: 给定一个范围,计算在该范围内每个数字的 3n + 1 序列的最大循环长度。

解决思路:

  • 使用动态规划或记忆化搜索来避免重复计算已知的序列长度。
  • 对于每个数字,生成其序列并记录最大值。

代码示例:

def collatz_length(n):
    length = 1
    while n != 1:
        if n % 2 == 0:
            n //= 2
        else:
            n = 3 * n + 1
        length += 1
    return length

def max_collatz_length(a, b):
    max_length = 0
    for i in range(min(a, b), max(a, b) + 1):
        max_length = max(max_length, collatz_length(i))
    return max_length

# 示例调用
a, b = 1, 10
print(max_collatz_length(a, b))  # 输出最大循环长度

6.总结

由于我无法直接访问这些题目的具体描述,因此上述分析仅基于 UVa OJ 上题目的常见类型和特点进行推测。在实际解题过程中,您需要根据题目描述和要求,仔细分析题目背景、数据范围、输入输出格式等信息,然后选择合适的算法和数据结构进行解答。同时,注意检查边界情况和特殊输入,以确保程序的正确性和健壮性。

  • 8
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

战族狼魂

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值