1133. Splitting A Linked List (25)

网上看到一道不错的题目,做了一下感觉对链表的理解有很大的帮助。题目的意思是给定一个正数K和一个链表 ,把链表的节点分成3类输出,先输出负数,再输出小于K的非负数,最后输出大于K的数,每一类输出保持原来的相对顺序不变。

比如给定链表18→7→-4→0→5→-6→10→11→-2K=10,输出-4→-6→-2→7→0→5→10→18→11。正式输入时第一行为首节点地址,节点数和K值,后面每行分别输输入节点的首地址,节点数值,下一节点的地址,输入的时候节点顺序是打乱了的。输出的时候按排好的顺序输出,并且本节点输出的下一节点地址与下一节点输出的地址是相同的,地址按5位数输出,不足5位的前面补0NULL-1表示,格式如下:

Sample Input:

00100 9 10

23333 10 27777

00000 0 99999

00100 18 12309

68237 -6 23333

33218 -4 00000

48652 -2 -1

99999 5 68237

27777 11 48652

12309 7 33218

Sample Output:

33218 -4 68237

68237 -6 48652

48652 -2 12309

12309 7 00000

00000 0 99999

99999 5 23333

23333 10 00100

00100 18 27777

27777 11 -1

      一般的做法都是定义一个很大的数组list[100000],用数组的下标来定位下一个节点,在输入节点数量很多的时候,这种做法可以有效提高排序的速度,又不会浪费太多内存。当输入节点比较少的时候,就会有大量内存浪费,本文主要针对这种情况,在节点输入比较少的情况下,直接对链表排序,牺牲一点cpu效率来节省内存空间。具体的选择还要根据实际情况做出折中。

      首先输入的链表是无序的,但是可以根据节点的前后地址进行2层轮询最后得到链表的正确顺序,这里比较费时间,时间复杂度O(n^2),得到正确顺序的链表后就可以直接输出了,分3次遍历链表,将已经输出的节点删除可以减少下一次遍历的时间。

#include <stdio.h>
#include <assert.h>

struct NODE {
    int addr;
    int data;
    int next_addr;
    struct NODE *next_node;
};
typedef struct NODE NODE;


void output_print(NODE* link_node,NODE* pre_node)
{
    static int flag=0;
    if(!flag)
    {
        printf("%05d %d ", link_node->addr,link_node->data);
        flag=1;
    }
    else
    {
        printf("%05d\n%05d %d ", link_node->addr,link_node->addr,link_node->data);
    }
    pre_node->next_node = link_node->next_node;//删除输出的节点
}

void Splitting_A_Linked_List(void)
{
    int nodes_num,first_addr,k;
    int first_node_index=0,end_flag=0;

    NODE *link_node,*pre_node;
    scanf("%d %d %d",&first_addr,&nodes_num,&k);
    assert((nodes_num>0) && (nodes_num<=100000));
    assert((k>0) && (nodes_num<=1000));
    NODE input_node[nodes_num];//变长数组,gcc支持


    for(int i=0;i<nodes_num;i++)//输入所有节点
    {
        scanf("%d %d %d",&input_node[i].addr,&input_node[i].data,&input_node[i].next_addr);
        input_node[i].next_node = NULL;
        if(input_node[i].addr == first_addr)
        {
            first_node_index = i;//定位头节点
        }
        end_flag=0;
        for(int j=0;j<i;j++)
        {
            if(input_node[i].next_addr == input_node[j].addr)//存在之前输入节点的地址等于当前下一节点地址
            {
                end_flag++;
                input_node[i].next_node = &input_node[j] ;//将该节点插入当前节点之后
                if(end_flag == 2)  break;
            }
            if(input_node[i].addr == input_node[j].next_addr)//存在之前输入节点的下一节点地址与当前节点地址相同
            {
                end_flag++;
                input_node[j].next_node = &input_node[i] ;//将当前节点插入该节点之后
                if(end_flag == 2)  break;          //前后地址的节点都找到后就可以跳出循环了
            }
        }
    }

    for(int i=0;i<3;i++)//分3次轮询链表
    {
        link_node = &input_node[first_node_index];
        pre_node = link_node;
        while(link_node!= NULL)
        {
            switch (i)
            {
                case 0:
                    if(link_node->data<0)
                    {
                        output_print(link_node,pre_node);
                    }
                    break;
                case 1:
                    if(link_node->data>=0 && link_node->data <= k )
                    {
                        output_print(link_node,pre_node);
                    }
                    break;
                case 2:
                    if(link_node->data > k )
                    {
                        output_print(link_node,pre_node);
                    }
                    break;
                default:
                    break;
            }
            pre_node=link_node; //保存前一个节点,当删除节点时会用到
            link_node=link_node->next_node;//遍历下一个节点
        }
    }
    printf("-1\n");
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值