PTA L2-002链表去重

L2-002 链表去重

分数 25

全屏浏览

切换布局

作者 陈越

单位 浙江大学

给定一个带整数键值的链表 L,你需要把其中绝对值重复的键值结点删掉。即对每个键值 K,只有第一个绝对值等于 K 的结点被保留。同时,所有被删除的结点须被保存在另一个链表上。例如给定 L 为 21→-15→-15→-7→15,你需要输出去重后的链表 21→-15→-7,还有被删除的链表 -15→15。

输入格式:

输入在第一行给出 L 的第一个结点的地址和一个正整数 N(≤105,为结点总数)。一个结点的地址是非负的 5 位整数,空地址 NULL 用 −1 来表示。

随后 N 行,每行按以下格式描述一个结点:

地址 键值 下一个结点

其中地址是该结点的地址,键值是绝对值不超过104的整数,下一个结点是下个结点的地址。

输出格式:

首先输出去重后的链表,然后输出被删除的链表。每个结点占一行,按输入的格式输出。

输入样例:

00100 5
99999 -7 87654
23854 -15 00000
87654 15 -1
00000 -15 99999
00100 21 23854

输出样例:

00100 21 23854
23854 -15 99999
99999 -7 -1
00000 -15 87654
87654 15 -1

思路比较简单,用数组分别存地址,值,下一个符号地址即可,竞赛中这样相较于写一个正式的链表并操作会节省很多时间 

 上代码

#include<bits/stdc++.h>
using namespace std;
struct node
{
    int addr,data;
    int next;
};
const int N = 1e5+5;
int main()
{
    int start,n;cin>>start>>n;
    node nc[N];
    for(int i=0;i<n;i++)
    {
        int add;cin>>add;
        cin>>nc[add].data>>nc[add].next;
        nc[add].addr=add;
    }
    int cnt[N]={0};
    vector<node> ans1,ans2;
    for(int i=start;i!=-1;i=nc[i].next)
    {
        cnt[abs((nc[i].data))]++;
        if(cnt[abs((nc[i].data))]!=1)
        {
            ans2.push_back(nc[i]);
        }
        else 
        {
            ans1.push_back(nc[i]);
        }
    }
    int m1=ans1.size(),m2=ans2.size();
    for(int i=0;i<m1;i++)
    {
        printf("%05d %d ",ans1[i].addr,ans1[i].data);
        if(i==m1-1) 
        {
            cout<<"-1";
        }
        else 
        {
            printf("%05d",ans1[i+1].addr);
        }
        cout<<"\n";
    }
    for(int i=0;i<m2;i++)
    {
        printf("%05d %d ",ans2[i].addr,ans2[i].data);
        if(i==m2-1) 
        {
            cout<<"-1";
        }
        else 
        {
            printf("%05d",ans2[i+1].addr);
        }
         cout<<"\n";
    }
    return 0;
}
<think>嗯,用户想用Java实现链表,特别是提到了L2-002这个示例题目。首先,我需要回忆一下链表的常见方法。通常有两种方法:使用集合(比如HashSet)来记录已经出现的元素,或者排序后。但题目可能要求保留原有顺序,所以排序可能不合适。 根据用户的引用内容,引用[1]提到遍历链表两次来找到倒数第k个节点,这可能跟链表操作有关。引用[3]展示了用迭代法反转链表的Java代码,这涉及到链表的指针操作。这些可能对有帮助。 链表的基本思路是遍历每个节点,检查当前节点的值是否已经存在,如果存在则删除复节点。可以使用哈希集合来存储已经出现过的值。具体步骤应该是:初始化一个HashSet,遍历链表,如果当前节点的值在集合中,就跳过(删除),否则将值加入集合并继续。 但需要考虑链表的结构,比如单链表删除节点需要维护前驱节点。因此,在遍历时,需要保留前一个节点的引用,以便删除当前节点。或者,可以使用双指针法,一个指针检查当前节点,另一个指针跟踪前一个节点。 另外,题目L2-002可能是指PTA(程序设计类实验辅助教学平台)的题目,具体来说可能是要求删除链表中绝对值复的节点,只保留第一次出现的节点。这种情况下,可能需要使用哈希表来记录已经出现的绝对值,然后进行删除操作。 现在,用户需要Java代码示例。根据引用[3]中的Java代码结构,可以模仿使用虚拟头节点(dummy node)来简化操作。比如,创建dummy节点指向头节点,然后使用前驱指针和当前指针遍历链表,同时用HashSet记录已出现的值。 具体步骤: 1. 创建dummy节点,next指向头节点。 2. 初始化HashSet。 3. 使用前驱指针prev从dummy开始,当前指针curr从head开始。 4. 遍历链表,如果curr的值在集合中,prev.next跳过curr,即prev.next = curr.next;否则,将值加入集合,prev移动到curr。 5. curr继续移动到下一个节点。 需要注意处理头节点可能被删除的情况,使用dummy节点可以避免单独处理头节点。此外,要处理链表为null的情况。 然后,生成相关问题需要考虑链表操作的其他方法,比如如何反转链表、如何检测环、如何找到中间节点等,这些都是常见的链表相关问题,可以引导用户进一步学习。 最后,确保代码正确,语法无误,并且符合用户的要求。可能还需要测试一下代码的逻辑是否正确,比如复元素出现在不同位置时是否能正确删除。</think>### Java链表方法及代码示例 链表通常需要保留第一次出现的元素,后续复元素需删除。以下是两种常见实现方法: --- #### **方法1:哈希集合法(时间复杂度$O(n)$,空间复杂度$O(n)$)** **核心思路**:遍历链表时,用`HashSet`记录已出现的元素值,遇到复值则删除当前节点。 ```java public ListNode removeDuplicateNodes(ListNode head) { if (head == null) return null; Set<Integer> seen = new HashSet<>(); ListNode dummy = new ListNode(-1); dummy.next = head; ListNode prev = dummy; ListNode curr = head; while (curr != null) { if (seen.contains(curr.val)) { prev.next = curr.next; // 跳过复节点 } else { seen.add(curr.val); prev = curr; // 更新前驱节点 } curr = curr.next; } return dummy.next; } ``` --- #### **方法2:双指针暴力法(时间复杂度$O(n^2)$,空间复杂度$O(1)$)** **适用场景**:若要求不使用额外空间,可对每个节点遍历后续节点。 ```java public ListNode removeDuplicates(ListNode head) { ListNode current = head; while (current != null) { ListNode runner = current; while (runner.next != null) { if (runner.next.val == current.val) { runner.next = runner.next.next; // 删除后续复节点 } else { runner = runner.next; } } current = current.next; } return head; } ``` --- ### **关键点分析** 1. **哈希法高效但需额外空间**,适合数据规模较小的场景[^1]。 2. **双指针法节省空间但效率低**,适合对空间敏感的场景。 3. **虚拟头节点(dummy node)** 简化链表头部的删除操作[^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值