队尾幸运编号

/*题目描述:
N个人排成一队, 从1到5轮流报数, 报5的人是幸运者, 出列。报到队尾后, 从队首接着报,依次循环
问: 排在队尾的人是第几名幸运者
注: N为小于100000的正整数
例:
1人排成一队, 他就是第一名幸运者。
3人排成一队, 他就是第2名幸运者。
5人排成一队, 他就是第1名幸运者。
8人排成一队, 他就是第3名幸运者。
即求:N人排成一队, 队尾是第多少名幸运者
*/ 
function LuckySeqNum(N=1) {
    var arr = new Array(N),tailLuckySeq = 0;
    var num = 0,flag = true;
    for(let i=0;i<N;i++) arr[i]=0 // 初始化数组
    var i = 0;
    while(flag) {
        if(i===arr.length) i=0;
        for(;i<arr.length;i++) {
            if(arr[i]===1) continue; // 关键: 若当前元素的值为1, 表示已经是幸运者, 不能再参与报数,应跳过
            num++;
            if( i<N-1 && num===5 && arr[N-1]===0) {
                arr[i]=1;
                tailLuckySeq++;
                num = 0;
            }
            if( i===N-1 && num===5 ) {
                arr[i] = 1;
                tailLuckySeq++;
                return tailLuckySeq
            }
        }
    }
}

console.log(LuckySeqNum(3))


 

<think> 我们正在讨论的是LinkedBlockingQueue,用户的问题是如何从LinkedBlockingQueue中获取队尾元素。 根据引用,我们知道LinkedBlockingQueue内部维护了一个尾节点(Tail),它指向队列中最后一个元素。但是,LinkedBlockingQueue的公开API中并没有直接提供获取队尾元素的方法。 然而,我们可以通过以下思路来获取队尾元素: 1. 遍历整个队列直到最后一个元素。但是这样效率很低,尤其是队列很长时。 2. 通过反射直接访问尾节点(tail)字段,但这破坏了封装,且不安全。 3. 使用其他数据结构(如双端队列)替代,因为LinkedBlockingQueue是单向队列。 实际上,LinkedBlockingQueue是一个单向链表实现的队列,它只提供了从头部取元素、从尾部添加元素的操作。因此,官方API没有提供获取队尾元素的方法。 但是,我们可以通过以下方法之一来获取队尾元素: 方法一(不推荐,效率低):遍历队列直到最后一个元素。 方法二(不推荐,反射):使用反射获取尾节点(tail)字段,然后读取其item值。 由于方法二存在安全风险且可能随着Java版本变化而失效,我们通常不推荐。因此,如果应用场景中需要频繁获取队尾元素,建议使用支持双端操作的队列,如LinkedBlockingDeque。 使用LinkedBlockingDeque替代LinkedBlockingQueue: LinkedBlockingDeque是一个双端阻塞队列,它提供了获取队尾元素的方法(如peekLast, getLast等)。 下面给出两种方法的示例代码: 方法一(遍历)的示例代码(效率低,仅用于理解): ```java import java.util.concurrent.LinkedBlockingQueue; public class GetTailElementExample { public static void main(String[] args) { LinkedBlockingQueue<String> queue = new LinkedBlockingQueue<>(); queue.add("A"); queue.add("B"); queue.add("C"); // 注意:遍历会消耗时间,且并发环境下可能不一致 String last = null; for (String element : queue) { last = element; } System.out.println("队尾元素: " + last); // 输出: C } } ``` 方法二(使用双端队列LinkedBlockingDeque)的示例代码(推荐): ```java import java.util.concurrent.LinkedBlockingDeque; public class DequeExample { public static void main(String[] args) { // 创建一个双端队列,容量为20(或者不指定,默认Integer.MAX_VALUE) LinkedBlockingDeque<String> deque = new LinkedBlockingDeque<>(20); deque.add("A"); deque.add("B"); deque.add("C"); // 获取队尾元素(不删除) String last = deque.peekLast(); System.out.println("队尾元素: " + last); // 输出: C } } ``` 原因分析: 1. 引用[1]明确指出,LinkedBlockingQueue的尾节点(Tail)是队列的最后一个元素,但队列的公开方法中并没有提供获取尾节点的API[^1]。 2. 引用[4]中展示了LinkedBlockingQueue的peek方法(获取队头)和contains方法(遍历),但没有提供队尾的获取方法[^4]。 3. 因此,如果确实需要获取队尾元素,应该使用双端队列(如LinkedBlockingDeque),它提供了peekLast()等方法。 结论: 在LinkedBlockingQueue中,没有直接获取队尾元素的方法。如果必须获取队尾元素,建议改用LinkedBlockingDeque。 ### §§相关问题§§ 1. LinkedBlockingQueue和LinkedBlockingDeque在并发性能上有何区别? 2. 如何安全地遍历LinkedBlockingQueue? 3. Java中还有哪些双端队列实现? </think> ### 如何从 LinkedBlockingQueue 中获取队尾元素? LinkedBlockingQueue **不直接提供获取队尾元素的 API**,但可通过以下三种方法实现: #### ✅ 最佳方案:使用双端队列替代(推荐) LinkedBlockingQueue 是单向链表实现的队列,其设计仅暴露队头操作API。若需频繁获取队尾元素,应切换为 **LinkedBlockingDeque**(双端阻塞队列),它提供专门的队尾操作方法: ```java import java.util.concurrent.LinkedBlockingDeque; public class DequeExample { public static void main(String[] args) { // 创建容量为20的双端队列 LinkedBlockingDeque<String> deque = new LinkedBlockingDeque<>(20); deque.add("A"); deque.add("B"); deque.add("C"); // 获取队尾元素(不删除) String tail = deque.peekLast(); System.out.println("队尾元素: " + tail); // 输出: C // 获取并删除队尾元素 String removed = deque.pollLast(); } } ``` **优点**: 1. 线程安全,原生支持队尾操作(`peekLast()`, `pollLast()`, `getLast()`)[^3] 2. 与 LinkedBlockingQueue 共享相似的并发特性和性能表现 --- #### ⚠️ 遍历方案(效率较低) 通过迭代器遍历链表直至最后一个节点: ```java import java.util.Iterator; import java.util.concurrent.LinkedBlockingQueue; public class QueueTraversal { public static void main(String[] args) { LinkedBlockingQueue<String> queue = new LinkedBlockingQueue<>(20); queue.add("A"); queue.add("B"); queue.add("C"); String tail = null; Iterator<String> it = queue.iterator(); while (it.hasNext()) { tail = it.next(); // 最后一次赋值即队尾元素 } System.out.println("队尾元素: " + tail); // 输出: C } } ``` **缺点**: 1. 时间复杂度为 $O(n)$,队列越长性能越差 2. 遍历过程中若队列被并发修改可能抛出 `ConcurrentModificationException`[^4] --- #### ❌ 反射方案(不推荐) 通过反射强行访问内部 `tail` 节点(破坏封装性): ```java import java.lang.reflect.Field; import java.util.concurrent.LinkedBlockingQueue; public class ReflectionExample { public static void main(String[] args) throws Exception { LinkedBlockingQueue<String> queue = new LinkedBlockingQueue<>(20); queue.add("A"); queue.add("B"); // 获取tail字段(非公开API) Field tailField = LinkedBlockingQueue.class.getDeclaredField("tail"); tailField.setAccessible(true); Object tailNode = tailField.get(queue); // 获取node.item字段 Field itemField = tailNode.getClass().getDeclaredField("item"); itemField.setAccessible(true); String tail = (String) itemField.get(tailNode); System.out.println("队尾元素: " + tail); // 输出: B } } ``` **强烈不推荐原因**: 1. 违反 Java 封装原则 2. 不同 JDK 版本可能导致兼容性问题 3. 线程安全问题(读取时可能被并发修改)[^5] --- ### 核心原理说明 根据引用[1][4],LinkedBlockingQueue 内部通过 `head` 和 `tail` 指针管理链表: - **head**:始终指向哑元节点(dummy node),`head.next` 是实际队首元素[^1] - **tail**:指向最后一个有效节点(队尾元素所在节点)[^1][^4] - 设计上刻意**隐藏队尾访问**,因为队列的典型使用场景只需操作队头[^3][^4] --- ### 最佳实践建议 | 场景 | 推荐方案 | |------|----------| | 需要频繁访问队尾 | 改用 `LinkedBlockingDeque` | | 仅需单次获取队尾 | 使用迭代器遍历 | | 高性能场景 | 重构设计避免获取队尾 | ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值