惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

V
V2EX
C
Check Point Blog
博客园 - Franky
月光博客
月光博客
T
Tenable Blog
博客园 - 聂微东
Cyberwarzone
Cyberwarzone
The GitHub Blog
The GitHub Blog
C
CERT Recently Published Vulnerability Notes
Scott Helme
Scott Helme
IT之家
IT之家
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
C
CXSECURITY Database RSS Feed - CXSecurity.com
F
Full Disclosure
博客园 - 司徒正美
Project Zero
Project Zero
Y
Y Combinator Blog
A
Arctic Wolf
美团技术团队
博客园 - 叶小钗
S
Securelist
F
Fortinet All Blogs
T
Threatpost
T
Threat Research - Cisco Blogs
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
酷 壳 – CoolShell
酷 壳 – CoolShell
MyScale Blog
MyScale Blog
大猫的无限游戏
大猫的无限游戏
Recorded Future
Recorded Future
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
S
Schneier on Security
Apple Machine Learning Research
Apple Machine Learning Research
L
LangChain Blog
P
Privacy International News Feed
博客园_首页
S
SegmentFault 最新的问题
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
腾讯CDC
P
Proofpoint News Feed
Cisco Talos Blog
Cisco Talos Blog
AWS News Blog
AWS News Blog
阮一峰的网络日志
阮一峰的网络日志
G
Google Developers Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Simon Willison's Weblog
Simon Willison's Weblog
雷峰网
雷峰网
P
Proofpoint News Feed
Latest news
Latest news
G
GRAHAM CLULEY

Posts on Changkun's Blog

Why High-Output Systems Are Often the First to Stop Growing Dark Forest Theory: A Formal Derivation Agents (or Humans) in Goal-Directed and Goalless Environments: On Pipelines, Priors, and the Rhythm Between Exploration and Exploitation At the Boundary of Self-Reference: From Stable Structures in Artificial Intelligence to the Self as a Recursive Model in an Open Dissipative System 2023 Reading List 2022 Reading List 2021 Reading List Are PSS/USS and RSS Actually the Same Thing? Performance Differences from Page Faults vs. Prefetching 2020 Year-End Review Migration with Zero Downtime 2020 Reading List The All in Go Stack Pointers Might Not Be Ideal for Parameters Eliminating A Source of Measurement Errors in Benchmarks Setup Wordpress in 10 Minutes 我为什么不再写博客了? 2019 年终总结 2018-2019 读书清单 Ten years of blogging Rethinking the Reflections on Communications and Trusts 2018 年终总结 Go source code study is open source Go source study: unsafe Pattern Go source study: sync.Pool Go runtime programming A Million WebSocket and Go Designing Asynchronous RESTful APIs 分布式杂谈01:CAP 理论的误解 Issues of Human-Bot Interaction 压缩法与深度网络的泛化性 Go in 1 Hour UMSLT04: The Past and Present of SGD UMSLT03: A Gentle Start of Learning Theory UMSLT02: A Breif History of Neural Networks UMSLT01: A Breif History of Regularization 不笑不足以为道 论文笔记:Generalization in Deep Learning 2017 年终总结 2017 读书清单 深度学习的泛化理论简介 删除 GitHub 上已经提交的敏感信息 硕士生涯的第一年就这样告一段落了 人肉计算(10): 系统参与激励 人肉计算(9): 陷阱的解法 别聊,一聊你就暴露 人肉计算(8): 人肉计算与数据科学中的陷阱 人肉计算(7): 社会行为分析 Hexo + GitHub + Travis CI + VPS 自动部署 人肉计算(6): 预测市场 人肉计算(5): 信用风险评级模型 读书与回报 瞎扯: 对现代企业理论与当下IT企业的商业模式和信息产业链的规律性的思考 人肉计算(4): 输入数据聚合与PageRank 又一次打整了一下博客 人肉计算(3): 输入数据聚合与链路预测 人肉计算(2): 意图博弈 GWAPs 人肉计算(1): 众包与群众智慧 对后辈同学在计算机专业上的答疑与解惑 在德国的医疗及住院体验 这可能不是一个技术博客了 实验楼楼赛第3期-Python-题解 迅速更换了 DISQUS Electron 深度实践总结 良好的编码体验的三个方面 2016 年终总结 2016 读书清单 最近在着手写的文章 微信小程序文档极致总结 谈谈过去三个月在实验楼的实习经历 Built a Desktop Client for My Blog Guacamole 源码分析与 VNC 中 RFB 协议的坑 《高速上手 C++11/14》正式发布 Docker 极速入门教程02 - 镜像与容器管理 Docker 极速入门教程01 - 基本概念和操作 阶段性沉默 ELK+Redis 最佳实践 终于全面启用了 HTTPS 苹果开源了LZFSE无损压缩 Hash 碰撞的一种思路 记一次完整的 Kaldi-TIMIT 示例运行 Kaldi 上的 TIMIT 例子 Kaldi 安装与部署 从科研写作谈起 Swift API 设计指南 有趣的人类 所以其实论文并没有什么鬼用 Githug 通关记录及指南 小结一下这学期的收获 2015 读书清单 2015 年终总结 负能量爆表 转眼就快两个月了 博客迁移记录 大三总结 这个世界,终究不会是我们的。 Linux 内核分析 之六:Linux 内核创建进程的过程 小说「泽缘」 Linux 内核分析 之五:system_call中断处理过程的简要分析 大创项目的标题真是每年都在考验同学们的想象力啊
结构体排序引发的一连串问题
Changkun Ou · 2013-05-07 · via Posts on Changkun's Blog

结构体排序引发的一连串问题

Published at发布于:   |   PV/UV: /   |   Reading阅读: 4 min

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
题目:
给出n个仅由小写字幕构成的字符串,仅按其首字母排序。(若首字母相同,则直接维持原来的顺序。)

输入格式:
第一行为n。
接下来n行,每行一个串。

输出格式:
n行,按排好的顺序,每行一个串。

数据规定:
n <= 1000, 串长小于100000。

话说这题前前后后每周大概抽两个小时来搞这题,未怀疑过评测系统出问题,因为我看见老师过了。 前前后后拖了一个月的时间。

放上最终的代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include <iostream>
#include <fstream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <climits>
#include <string>
#include <algorithm>
#include <vector>
#include <map>
#include <time.h>

using namespace std;
struct Value
{
    char str[10000];
};

void structSort(Value *a, int n)
{
    int i = 0, j = n;
    char *temp[1000];
    char *help;

    //开辟临时存放字符串的空间
    char *p = (char *)malloc( sizeof(char)*10000*n );

    //保存地址
    for( i = 0; i < n; i++ )
        temp[i] = &amp;a[i].str[0];

    //冒泡排序
    for( j = n; j > 0; j-- )
        for( i = 0; i < j-1; i++ )
            if( *temp[i] > *temp[i+1] )   //  对地址进行排序
            {
                help = temp[i];
                temp[i] = temp[i+1];
                temp[i+1] = help; 
            }
    //按地址从结构体拷出
    for( i = 0; i < n; i++ )
        strcpy( p + i*10000 ,temp[i] );

    //拷贝回结构体
    for( i = 0; i < n; i++ )
        strcpy(a[i].str, p + i*10000 );

    free(p);
}

int n;
Value a[5000];

int main()
{
    scanf("%d", &amp;n);
    for (int i = 0; i<n; i++)
    {
        scanf("%s", a[i].str);
    }
    structSort(a, n);
    for (int i = 0; i<n; i++) 
    {
        printf("%s\n", a[i].str);
    }
    printf("\n\nTime used = %.0lf ms\n",(double)(1000*clock()/CLOCKS_PER_SEC));
    return 0;
}

下面详细说说这道题碰见的事情。

1、排序算法的选取

一开始并没有太在意这道题有什么特别之处,感觉特别平常的一道题,想想冒泡太慢,快排不熟悉,于是选择了相对简单的选择排序。

提交了好几次发现问题,选择排序并不能保证:“若首字母相同,则直接维持原来的顺序。” (排序的稳定性)

下面是反例: 数据为: wjbzbmr wa ac orz tle 如果是选择排序最后的结果会变成: ac orz tle wa wjbzbmr

于是果断滚回去用了冒泡,结果直接用冒泡会超时,这是字符串复制照成的(代码如下)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
void structSort(Value *a, int n)
{
    int i = 0, j = n;
    Value help;
    for( j = n; j > 0; j-- )
        for( i = 0; i < j-1; i++ )
            if( a[i].str[0] >a[i+1].str[0] )
            {
                help = a[i];
                a[i] = a[i+1];
                a[i+1] = help; 
            }
}

刚开始没有思考是什么原因造成的,马上开始着手使用快排。

猛然想到,快排和选择排序一样,会造成上面反例的结果。所以当时瞬间傻了一阵子。

后来想到,即使冒泡没有花多少时间,但是主要时间则花在了交换两个字符串上。

所以,开始采用地址的做法。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
void structSort(Value *a, int n)
{
    int i = 0, j = n;
    char *temp[1000];
    char *help;
    char temp_str[1000][100000];

    //保存地址
    for( i = 0; i < n; i++ )
        temp[i] = &amp;a[i].str[0];

    //冒泡排序
    for( j = n; j > 0; j-- )
        for( i = 0; i < j-1; i++ )
            if( *temp[i] > *temp[i+1] )   //  对地址进行排序
            {
                help = temp[i];
                temp[i] = temp[i+1];
                temp[i+1] = help; 
            }
    //按地址从结构体拷出
    for( i = 0; i < n; i++ )
        strcpy( temp_str[i], temp[i] );

    //拷贝回结构体
    for( i = 0; i < n; i++ )
        strcpy(a[i].str, temp_str[i] );

    free(p);
}

上面的做法直接在函数内申请了char temp_str[1000][100000];直接导致栈溢出。

所以后来改成了动态内存分配的方法。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
void structSort(Value *a, int n)
{
    int i = 0, j = n;
    char *temp[1000];
    char *help;

    //开辟临时存放字符串的空间
    char *p = (char *)malloc( sizeof(char)*10000*n );

    //保存地址
    for( i = 0; i < n; i++ )
        temp[i] = &amp;a[i].str[0];

    //冒泡排序
    for( j = n; j > 0; j-- )
        for( i = 0; i < j-1; i++ )
            if( *temp[i] > *temp[i+1] )   //  对地址进行排序
            {
                help = temp[i];
                temp[i] = temp[i+1];
                temp[i+1] = help; 
            }
    //按地址从结构体拷出
    for( i = 0; i < n; i++ )
        strcpy( p + i*10000 ,temp[i] );

    //拷贝回结构体
    for( i = 0; i < n; i++ )
        strcpy(a[i].str, p + i*10000 );

    free(p);
}

得到了最终的代码。

2、数据构造

但是这TNND死活就不过,十个测试数据,对两个,其他八个属于运行错误。

然后被这个错误折腾了半个月,今天终于知道了,原来我代码是正确的,错在评测系统。

构造了一个极限数据:1000*10000的数据包。

使用下面的程序来造数据:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <stdio.h>
#include <windows.h>
#include<stdlib.h>
#include<time.h>
#define R 1 //随机数启用1 

int main()
{
    FILE *fp;
    char desktop[100];
    char str1[20]={0};
    unsigned long size=20;
    GetUserName(str1,&amp;size);
    sprintf(desktop,"C:\\Users\\%s\\Desktop\\%s",str1,"1.in");
    freopen(desktop,"w",stdout);
    #if(R==1)

        int number;
        int N=123 ; //0到?内的随机数 
        srand(time(NULL)*100);
        number=rand()%N+1;
    #endif
    //开始输入到文件 
    printf("%d\n",1000);
    for(int k=1;k<=1000;k++)
    {
        for(int j=1;j<=10000;j++)
        {
            number=rand()%N;
            if(number>=97&amp;&amp;number<=122)
        printf("%c",number);
        }
        printf("\n");
    }
    return 0;
}

运用重定向来测试到底会运行多长时间:

用下面这条语句来测试运行时间:

1
printf("\n\nTime used = %.0lf ms\n",(double)(1000*clock()/CLOCKS_PER_SEC));

直接在冒泡内进行字符串的交换,所花时间为:1702ms

使用指针排序所花的时间为:163ms

评测系统允许的时间最大为1s,显然是对的。

然后我就不说什么了。以后还是要有自身判断正误的能力。