1097 Deduplication on a Linked List (25分)[静态链表][hash散列]

By Jalan

知识工具需求

数学

数据结构和算法

  • 静态链表
  • hash散列

语言

  • 一般产生段错误的原因就是数组下标访问越界了,这种越界特别可能出现在
  1. 单独访问数组内特定的某个位置(需要验证位置是否存在)
  2. 需要对数组的开头,结尾做特殊处理(需要验证是否有开头结尾,部分情况下还要验证他们是否一致等)
  3. size()的返回值是unsigned unsigned到int是降级,应该极力避免.

bug

这里注意两个写法

//1
for (int i = 0; i < removedList.size() - 1; i++)
    {
        printf("%05d %d %05d\n", removedList[i].address, removedList[i].key, removedList[i + 1].address);
    }
if (removedList.size()>=1)
    {
        printf("%05d %d -1\n", removedList[removedList.size() - 1].address, removedList[removedList.size() - 1].key);
    }
//2
if (removedList.size()>=1)
    {
        for (int i = 0; i < removedList.size() - 1; i++)
        {
            printf("%05d %d %05d\n", removedList[i].address, removedList[i].key, removedList[i + 1].address);
        }
        printf("%05d %d -1\n", removedList[removedList.size() - 1].address, removedList[removedList.size() - 1].key);
    }

写法1会出现段错误.在removedList.size()==0的时候,表面上这两个写法是等价的.但是实际上.size()的返回值是unsigned 类型,removedList.size() - 1的结果会是非常大的一个数.因此会进循环.所以要想还是写成1这种可以写成:for (int i = 0; i < (int)removedList.size() - 1; i++)
但是还是要注意,因为这里unsigned到int是缩小了,所以这个unsigned不能超int正范围(因为数值在int正的范围内和unsigned补码行为一样).超了会发生溢出,补码一致可以推得.(比如unsigned 4294967295(补码全1)强制转换成int 就是-1(补码全1))

题干

给一个单链表L,key是整数,对于每个key只能保存第一次出现这个值或者它绝对值的节点.但是所有被移除的节点也要保存在一个list里.比如,给L:21→-15→-15→-7→15打印:21→-15→-7和移出列表:-15→15

输入条件

第一行包含首节点地址(5位非负)和节点数N<=10^5NULL是-1;
下面是N行node,格式是:
Address Key Next
key[0,10^4]

例1

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

输出条件

先打印结果L,再打印引出表

例1

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

题解

第一次

思路

  1. 这PTA对所有链表的题基本都会给几个不在链表里的节点,所以首先还是要用一个大数组nodeList保存所有的节点.
  2. 根据nodeList遍历这个链表,生成一个散列表hashValue和一个结果数组resultList和移除数组removedList,绝对值在hashList里值出现过第一次时加进resultList,多次是加进removedList
  3. 分别输出resultList和removedList,注意控制输出,每个表每节点末尾是下一个节点的地址,最后一个节点单独处理

预期时间复杂度

nlogn

编写用时

代码

CPP
#include <stdio.h>
#include <vector>
using namespace std;
typedef struct node
{
    int address;
    int key;
    int next;
} node;

int main(int argc, char const *argv[])
{
    //input
    int firstId, N;
    scanf("%d %d", &firstId, &N);
    vector<node> nodeList(100001);
    for (int i = 0, ad, key, next; i < N; i++)
    {
        scanf("%d%d%d", &ad, &key, &next);
        nodeList[ad] = {ad, key, next};
    }
    //process
    if (firstId == -1)
    {
        return 0;
    }
    vector<int> hashValue(10001);
    int nowId = firstId;
    vector<node> resultList;
    vector<node> removedList;
    int absValue = 0;
    while (nowId != -1)
    {
        if (nodeList[nowId].key > 0)
        {
            absValue = nodeList[nowId].key;
        }
        else
        {
            absValue = -nodeList[nowId].key;
        }
        hashValue[absValue]++;
        if (hashValue[absValue] == 1)
        {
            resultList.push_back(nodeList[nowId]);
        }
        else
        {
            removedList.push_back(nodeList[nowId]);
        }
        nowId = nodeList[nowId].next;
    }

    //output
    if (resultList.size()>=1)//注意做这种独立角标索引的时候一定要注意会不会越界.这里假如是空,单独下面会越界的.
    {
        for (int i = 0; i < resultList.size() - 1; i++)
        {
            printf("%05d %d %05d\n", resultList[i].address, resultList[i].key, resultList[i + 1].address);
        }
        printf("%05d %d -1\n", resultList[resultList.size() - 1].address, resultList[resultList.size() - 1].key);
    }
    if (removedList.size()>=1)
    {
        for (int i = 0; i < (removedList.size() - 1); i++)
        {
            printf("%05d %d %05d\n", removedList[i].address, removedList[i].key, removedList[i + 1].address);
        }
        printf("%05d %d -1\n", removedList[removedList.size() - 1].address, removedList[removedList.size() - 1].key);
    }
    return 0;
}

运行用时

在这里插入图片描述

结尾

看在我写了这么多注释的份上可以给我点个赞嘛,求求惹=]砰砰砰,给我加点写下去的油呀@.@
也欢迎关注我的CSDN账号呀,接下来两个月我应该会按这个格式更新所有的PTA甲级题目

                                        **开心code每一天**
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值