第二章链表问题算法(一)

1,打印两个有序链表的公共部分
题目:给定两个有序链表的头指针head1和head2,打印两个链表的公共部分。
解答思路:
1,如果head1的值小于head2,则head1往下移动
2,如果head2的值小于head1,则head2往下移动
3,如果head1的值与head2的值相等,则打印这个值,然后head1与head2都往下移
4,head1或head2有任何一个移动到null,整个过程停止
代码如下:

public static void printCommonPart(Node head1, Node head2) {
        System.out.print("Common Part: ");
        while (head1 != null && head2 != null) {
            if (head1.value < head2.value) {
                head1 = head1.next;
            } else if (head1.value > head2.value) {
                head2 = head2.next;
            } else {
                System.out.print(head1.value + " ");
                head1 = head1.next;
                head2 = head2.next;
            }
        }
        System.out.println();
}

2,在单链表和双链表中删除倒数第K个数
题目:分别实现两个函数,一个可以删除单链表倒数第K个数,另一个可以删除双链表中倒数第K个节点。
要求:如果链表长度为N,要求时间复杂度达到O(N),额外空间复杂度O(1)。
先看单链表的代码:

public static class Node {
        public int value;
        public Node next;

        public Node(int data) {
            this.value = data;
        }
    }

    public static Node removeLastKthNode(Node head, int lastKth) {
        if (head == null || lastKth < 1) {
            return head;
        }
        Node cur = head;
        while (cur != null) {
            lastKth--;
            cur = cur.next;
        }
        if (lastKth == 0) {
            head = head.next;
        }
        if (lastKth < 0) {
            cur = head;
            while (++lastKth != 0) {
                cur = cur.next;
            }
            cur.next = cur.next.next;
        }
        return head;
}

双链表调整:

public static class DoubleNode {
        public int value;
        public DoubleNode last;
        public DoubleNode next;

        public DoubleNode(int data) {
            this.value = data;
        }
    }

    public static DoubleNode removeLastKthNode(DoubleNode head, int lastKth) {
        if (head == null || lastKth < 1) {
            return head;
        }
        DoubleNode cur = head;
        while (cur != null) {
            lastKth--;
            cur = cur.next;
        }
        if (lastKth == 0) {
            head = head.next;
            head.last = null;
        }
        if (lastKth < 0) {
            cur = head;
            while (++lastKth != 0) {
                cur = cur.next;
            }
            DoubleNode newNext = cur.next.next;
            cur.next = newNext;
            if (newNext != null) {
                newNext.last = cur;
            }
        }
        return head;
}

3,删除链表的中间节点和a/b处的节点
题目:给定链表头节点head,实现删除链表的中间节点的函数。
例如:
1–>2:删除1节点;
1–>2–>3:删除2节点;
1–>2–>3–>4:删除2节点;
1–>2–>3–>4–>5:删除3节点;
进阶:给定链表头节点head、整数a和b,实现删除位于a/b处节点的函数。
例如:
链表:1–>2–>3–>4–>5,假设a/b的值为r。
如果r等于0,不删除任何节点;
如果r在区间(0,1/5]上,删除节点1;
如果r在区间(1/5,2/5]上,删除节点2;
如果r在区间(2/5,3/5]上,删除节点3;
如果r在区间(3/5,4/5]上,删除节点4;
如果r在区间(4/5,1/5]上,删除节点5;
删除中间节点的代码:

public static Node removeMidNode(Node head) {
        if (head == null || head.next == null) {
            return head;
        }
        if (head.next.next == null) {
            return head.next;
        }
        Node pre = head;
        Node cur = head.next.next;
        while (cur.next != null && cur.next.next != null) {
            pre = pre.next;
            cur = cur.next.next;
        }
        pre.next = pre.next.next;
        return head;
    }

进阶问题,删除a/b处的节点

public static Node removeByRatio(Node head, int a, int b) {
        if (a < 1 || a > b) {
            return head;
        }
        int n = 0;
        Node cur = head;
        while (cur != null) {
            n++;
            cur = cur.next;
        }
        n = (int) Math.ceil(((double) (a * n)) / (double) b);
        if (n == 1) {
            head = head.next;
        }
        if (n > 1) {
            cur = head;
            while (--n != 1) {
                cur = cur.next;
            }
            cur.next = cur.next.next;
        }
        return head;
    }

4,反转单向链表和双向链表
题目:分别实现反转单向链表和双向链表的函数
要求:长度为n,时间复杂度为O(n),额外空间复杂度要求为O(1)。
反转单链表代码如下:

public static Node reverseList(Node head) {
        Node pre = null;
        Node next = null;
        while (head != null) {
            next = head.next;
            head.next = pre;
            pre = head;
            head = next;
        }
        return pre;
}

反转双向链表代码:

public static DoubleNode reverseList(DoubleNode head) {
        DoubleNode pre = null;
        DoubleNode next = null;
        while (head != null) {
            next = head.next;
            head.next = pre;
            head.last = next;
            pre = head;
            head = next;
        }
        return pre;
}

5,反转部分单向链表
题目:给定一个单向链表的头节点,以及两个整数from和to,在单向链表上把第from个节点到第to个节点这一部分进行反转。
例如:1–>2–>3–>4–>5–>null,from=2,to=4.
调整结果为:1–>4–>3–>2–>5–>null
再如:1–>2–>3–>null,from=1,to=3
调整结果为:3–>2–>1–>null
要求:
1,链表长度为n,时间复杂度为O(n),额外空间复杂度要求为O(1)。
2,如果不满足1<=frim<=to<==n,则不调整。

public static Node reversePart(Node head, int from, int to) {
        int len = 0;
        Node node1 = head;
        Node fPre = null;
        Node tPos = null;
        while (node1 != null) {
            len++;
            fPre = len == from - 1 ? node1 : fPre;
            tPos = len == to + 1 ? node1 : tPos;
            node1 = node1.next;
        }
        if (from > to || from < 1 || to > len) {
            return head;
        }
        node1 = fPre == null ? head : fPre.next;
        Node node2 = node1.next;
        node1.next = tPos;
        Node next = null;
        while (node2 != tPos) {
            next = node2.next;
            node2.next = node1;
            node1 = node2;
            node2 = next;
        }
        if (fPre != null) {
            fPre.next = node1;
            return head;
        }
        return node1;
}

6,环形单链表的约瑟夫问题
题目:39个犹太人与Josephus以及他的朋友躲到一个洞中,39个犹太人宁愿死也不愿意被敌人抓到,于是决定了一个自杀方式,41个人排成一个圈,由第一个人开始报数,报道3的人开始自杀,然后下一个人继续重新报1,报到3的人再自杀,这样再一次下去,直到剩下最后一个人时,那个人可以自由选择自己的命运。这就是著名的约瑟夫问题,现在请用单向环形链表描述该结构呈现整个自杀过程。
思路:
代码:

public static Node josephusKill1(Node head, int m) {
        if (head == null || head.next == head || m < 1) {
            return head;
        }
        Node last = head;
        while (last.next != head) {
            last = last.next;
        }
        int count = 0;
        while (head != last) {
            if (++count == m) {
                last.next = head.next;
                count = 0;
            } else {
                last = last.next;
            }
            head = last.next;
        }
        return head;
}

以上代码的时间复杂度是O(n*m),显然不符合进阶的要求。
进阶的代码:
7,判断链表是否为回文结构
题目:判断是否是回文结构,例如:1–>2–>1,返回true;1–>2–>3,返回false。
思路:利用栈即可,遍历栈,遍历过程中把节点压入栈,因为栈是先进后出的,所以在遍历后,从栈顶到栈底的节点值出现顺序会与原链表从左到右的值顺序反过来,那么,如果一个链表是回稳结构,如果出现的值还是一样的,那就是回文结构,否则不是。
代码如下:

// need n extra space。空间复杂度为O(n/2)。利用栈
    public static boolean isPalindrome1(Node head) {
        Stack<Node> stack = new Stack<Node>();
        Node cur = head;
        while (cur != null) {
            stack.push(cur);
            cur = cur.next;
        }
        while (head != null) {
            if (head.value != stack.pop().value) {
                return false;
            }
            head = head.next;
        }
        return true;
}

需要n/2的额外空间复杂度的代码:

// need n/2 extra space,额外空间复杂度为O(n/2)。这个方法是对第一种方法进行了改进,每次压右半部分的节点,这样从栈顶到栈底的顺序就相当于反过来了,然后与前半部分一一比较。
    public static boolean isPalindrome2(Node head) {
        if (head == null || head.next == null) {
            return true;
        }
        Node right = head.next;
        Node cur = head;
        while (cur.next != null && cur.next.next != null) {
            right = right.next;
            cur = cur.next.next;
        }
        Stack<Node> stack = new Stack<Node>();
        while (right != null) {
            stack.push(right);
            right = right.next;
        }
        while (!stack.isEmpty()) {
            if (head.value != stack.pop().value) {
                return false;
            }
            head = head.next;
        }
        return true;
}

空间复杂度为仅仅为O(1):

// need O(1) extra space
    方法三:不需要栈和其他数据结构,只用有限几个变量,其额外空间复杂度为O(1),可以在O(n)内完成所有的过程,也就是满足进阶的要求。具体过程如下:
    1,首先改变链表右半部分的结构,使整个右半区反转,最后指向中间节点。
    例如:1-->2-->3-->2-->1,通过这一步将其调整之后结构如下:1-->2-->3<--2<--1,链表1-->2-->3-->3-->2-->1,调整后为:1-->2-->3<--3<--2<--1。
    我们把左半边区域第一个节点记为leftStart,把反转后的右半部分的最右边的节点记为rightStart。
    2,leftStart和rightStart同时向中间点移动,移动每一步都比较leftStart和rightStart节点的值,看是否一样,一样的话,说明链表为为回文结构,否则不是回文结构。
    3,不管最后返回时false还是true,在返回前都应该把链表恢复成原来的状况。
    4,链表恢复成原来的样子,返回检查结果。以下是代码:
    public static boolean isPalindrome3(Node head) {
        if (head == null || head.next == null) {
            return true;
        }
        Node n1 = head;
        Node n2 = head;
        while (n2.next != null && n2.next.next != null) { // find mid node,找到中间节点,和方法二有一点不一样,方法二的中间是从head.next开始,方法三是从head开始,这有什么区别呢?在节点为偶数的时候,方法二得到的中间是中间两个节点的后一个节点,而方法三得到的中间节点得到的是中间两个节点的前一个节点,为什么?因为方法二的情况下要压入节点进栈就是从后半部分开始,这样就可以理解了,实际上理解为后半部分更恰当。
            n1 = n1.next; // n1 -> mid
            n2 = n2.next.next; // n2 -> end
        }
        n2 = n1.next; // n2 -> right part first node
        n1.next = null; // mid.next -> null
        Node n3 = null;
        while (n2 != null) { // right part convert
            n3 = n2.next; // n3 -> save next node
            n2.next = n1; // next of right node convert
            n1 = n2; // n1 move
            n2 = n3; // n2 move
        }
        n3 = n1; // n3 -> save last node
        n2 = head;// n2 -> left first node
        boolean res = true;
        while (n1 != null && n2 != null) { // check palindrome
            if (n1.value != n2.value) {
                res = false;
                break;
            }
            n1 = n1.next; // left to mid
            n2 = n2.next; // right to mid
        }
        n1 = n3.next;
        n3.next = null;
        while (n1 != null) { // recover list
            n2 = n1.next;
            n1.next = n3;
            n3 = n1;
            n1 = n2;
        }
        return res;
}
### RT-DETRv3 网络结构分析 RT-DETRv3 是一种基于 Transformer 的实时端到端目标检测算法,其核心在于通过引入分层密集正监督方法以及一系列创新性的训练策略,解决了传统 DETR 模型收敛慢和解码器训练不足的问题。以下是 RT-DETRv3 的主要网络结构特点: #### 1. **基于 CNN 的辅助分支** 为了增强编码器的特征表示能力,RT-DETRv3 引入了一个基于卷积神经网络 (CNN) 的辅助分支[^3]。这一分支提供了密集的监督信号,能够与原始解码器协同工作,从而提升整体性能。 ```python class AuxiliaryBranch(nn.Module): def __init__(self, in_channels, out_channels): super(AuxiliaryBranch, self).__init__() self.conv = nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1) self.bn = nn.BatchNorm2d(out_channels) def forward(self, x): return F.relu(self.bn(self.conv(x))) ``` 此部分的设计灵感来源于传统的 CNN 架构,例如 YOLO 系列中的 CSPNet 和 PAN 结构[^2],这些技术被用来优化特征提取效率并减少计算开销。 --- #### 2. **自注意力扰动学习策略** 为解决解码器训练不足的问题,RT-DETRv3 提出了一种名为 *self-att 扰动* 的新学习策略。这种策略通过对多个查询组中阳性样本的标签分配进行多样化处理,有效增加了阳例的数量,进而提高了模型的学习能力和泛化性能。 具体实现方式是在训练过程中动态调整注意力权重分布,确保更多的高质量查询可以与真实标注 (Ground Truth) 进行匹配。 --- #### 3. **共享权重解编码器分支** 除了上述改进外,RT-DETRv3 还引入了一个共享权重的解编码器分支,专门用于提供密集的正向监督信号。这一设计不仅简化了模型架构,还显著降低了参数量和推理时间,使其更适合实时应用需求。 ```python class SharedDecoderEncoder(nn.Module): def __init__(self, d_model, nhead, num_layers): super(SharedDecoderEncoder, self).__init__() decoder_layer = nn.TransformerDecoderLayer(d_model=d_model, nhead=nhead) self.decoder = nn.TransformerDecoder(decoder_layer, num_layers=num_layers) def forward(self, tgt, memory): return self.decoder(tgt=tgt, memory=memory) ``` 通过这种方式,RT-DETRv3 实现了高效的目标检测流程,在保持高精度的同时大幅缩短了推理延迟。 --- #### 4. **与其他模型的关系** 值得一提的是,RT-DETRv3 并未完全抛弃经典的 CNN 技术,而是将其与 Transformer 结合起来形成混合架构[^4]。例如,它采用了 YOLO 系列中的 RepNCSP 模块替代冗余的多尺度自注意力层,从而减少了不必要的计算负担。 此外,RT-DETRv3 还借鉴了 DETR 的一对一匹配策略,并在此基础上进行了优化,进一步提升了小目标检测的能力。 --- ### 总结 综上所述,RT-DETRv3 的网络结构主要包括以下几个关键组件:基于 CNN 的辅助分支、自注意力扰动学习策略、共享权重解编码器分支以及混合编码器设计。这些技术创新共同推动了实时目标检测领域的发展,使其在复杂场景下的表现更加出色。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值