OJ 单链表

本文介绍了一种在PAT考试中解决链表问题的有效方法:通过使用顺序结构来模拟链表,并利用STL算法进行操作。文章详细解释了如何定义节点结构、如何遍历并排序链表,最终实现链表的翻转。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

PAT中有关链表的题一般都是用顺序结构来表示,即将链表的节点储存在一个很大的数组中,数组下标就是节点的地址,节点结构中有一个next成员,储存着逻辑上下个节点的下标,用-1来表示空值。

//基本的节点结构
struct Node
{
    //数据
    int data;
    //下个节点的坐标
    int next;
};

考察范围无非增查删改反转这些,有一个方法可以在不破坏原有的逻辑顺序的前提下使用stl算法

为节点结构添加 address order 成员

struct Node
{
    int data;
    int next;
    //添加成员
    int address;  //记录节点的地址
    int order; //记录节点在链表中的逻辑顺序
    Node():
    order(INT32_MAX) //order默认为最大值
    { }
};

用一个vector来保存节点
节点的地址范围为 00000-99999

vector<Node> linklist(100000);    //足够容纳所有的地址

先按照链表中节点的顺序遍历,为每个节点赋值其order

int order = 0;
for (auto it = head; it != -1; it = linklist[it].next)
{
    linklist[it].order = order++;
}

再根据order大小排序,将节点聚集
根据order排序后,节点聚集在一起,下标失去地址的意义,但节点的相对次序还是和链表中一样

//order小的排在前面,即链表中的逻辑顺序
sort(linklist.begin(), linklist.end(), [](const Node& lhs, const Node&rhs) {
        return lhs.order < rhs.order;
    });

此时可以直接用stl的方法插入、删除节点
一个翻转链表的例子

vector<Node> linklist(100000);
    int head;
    int N;

    cin >> head >> N;

    while (N--)
    {
        int address, data, next;
        cin >> address >> data >> next;
        linklist[address] = Node(address, data, next);
    }

    int order = 0;
    for (auto it = head; it != -1; it = linklist[it].next)
    {
        linklist[it].order = order++;
    }

    sort(linklist.begin(), linklist.end(), [](const Node& lhs, const Node&rhs) {
        return lhs.order < rhs.order;
    });
    //将后面多余的空节点删掉
    linklist.resize(order);
    //可以直接使用翻转函数
    reverse(linklist.begin(), linklist.end());

    for (auto it = linklist.begin(); it != linklist.end(); ++it)
    {
        if (it != linklist.end() - 1)
        {
        //注意要用(it + 1)->address输出下一个节点的地址
        //因为next没有在reverse后更新,也可以更新next后再将next输出
            printf("%05d %d %05d\n", it->address, it->data, (it + 1)->address);
        }
        else
        {
            printf("%05d %d -1\n", it->address, it->data);
        }
    }
### 如何在单链表中对信息进行分类 单链表作为一种常见的数据结构,其基本特性是非连续、非顺序的存储方式,其中每个节点由两部分组成:元素(即实际存储的数据)和指向下一个节点的指针[^1]。为了实现单链表中对信息进行分类的操作,可以采用以下几种常见的方式: #### 方法一:基于特定条件重新构建多个子链表 可以通过遍历整个单链表并根据某些预定义的条件将节点分配到不同的子链表中。例如,假设需要按照数值大小分为两类,则可以在一次遍历过程中创建两个新的链表分别保存满足条件和不满足条件的节点。 ```java public class Node { int data; Node next; public Node(int d) { this.data = d; } } // 假设按data值是否大于某个阈值threshold来进行分类 public void classifyByValue(Node head, int threshold) { Node lessHead = null, lessTail = null; // 小于等于threshold的部分 Node greaterHead = null, greaterTail = null; // 大于threshold的部分 while (head != null) { if (head.data <= threshold) { if (lessHead == null) { lessHead = lessTail = new Node(head.data); } else { lessTail.next = new Node(head.data); lessTail = lessTail.next; } } else { if (greaterHead == null) { greaterHead = greaterTail = new Node(head.data); } else { greaterTail.next = new Node(head.data); greaterTail = greaterTail.next; } } head = head.next; } // 可选操作:打印或进一步处理这些链表... } ``` 此方法的时间复杂度主要取决于原始列表长度 \(n\) 和具体实现细节,通常为 \(O(n)\)[^2]。 #### 方法二:原地修改法——调整现有链表内部链接关系完成分类 这种方法不需要额外空间去建立新链表而是直接改变当前链表里各节点之间的相对位置达到目的。比如对于整数类型的节点来说可能希望正负分开排列;或者字符串类型则依据字母顺序重组等等。 下面给出一个简单的例子展示如何利用双指针技术来交换相邻两项直到所有符合条件项都移到前面为止(类似于冒泡排序的思想): ```java public boolean shouldMoveToFront(Node node){ return true;// Define your own logic here. } public void moveElementsToBeginning(Node head){ if(null==head || null == head.next)return ; Node prev=null,current=head,nextNode=null; do{ current=current.next; if(current!=null &&shouldMoveToFront(current)){ if(prev!=null){prev.next=current;} nextNode=current.next; current.next=head; head=current; current=nextNode; }else{ prev=current; } }while(current!=null); System.out.println("After moving elements to beginning:"); printList(head); } private static void printList(Node n){ while(n!=null){ System.out.print(n.data+" "); n=n.next; } System.out.println(); } ``` 上述代码片段展示了如何通过不断向前移动满足 `shouldMoveToFront` 函数判定标准的节点至头部从而形成一种形式上的“分类”。当然实际情况下的判断准则会更加多样化也更复杂一些[^3]. #### 注意事项 当涉及到删除某类特殊项目时务必小心边界状况以及断开后的重连过程以免丢失重要数据或是造成内存泄漏等问题发生。另外,在执行任何大规模变动前最好先备份初始状态以便必要时候回滚恢复[^4]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值