100分 求最优算法根据地址反查行政区代码 (面试题之一.)

aspvfp 2012-04-02 05:57:18
110000 北京市
110100 北京市市辖区
110101 北京市东城区
110102 北京市西城区
110103 北京市崇文区
110104 北京市宣武区
110105 北京市朝阳区
110106 北京市丰台区
110107 北京市石景山区
110108 北京市海淀区
110109 北京市门头沟区
110111 北京市房山区
110112 北京市通州区
....略
120000 天津市
120100 天津市市辖区
120101 天津市和平区
120221 天津市宁河县
120223 天津市静海县
120225 天津市蓟县
130000 河北省
130100 河北省石家庄市
130101 河北省石家庄市市辖区
....
130107 河北省石家庄市井陉矿区
130108 河北省石家庄市裕华区
130121 河北省井陉县
.....
==========================
大概4000多行
以上是地址行政区代码文件

要求用 C# Framework 2.0
根据客户提供的地址 找到行政区编码

例 地址:北京市朝阳区南湖南路15号院甲1号 应返回 110105


要求效率最高的一种方法

行政区文件 可以导入数据库 也可以导成 XML 或者就普通文本

请大家支个招...












...全文
577 27 打赏 收藏 转发到动态 举报
写回复
用AI写文章
27 条回复
切换为时间正序
请发表友善的回复…
发表回复
缪军 2012-04-03
  • 打赏
  • 举报
回复
楼主,把这些放在数据库中处理吧,别说4000条数据,
我做了个测试,七百万条数据(大小400兆),不需要任何索引,1秒钟之内搞定:
(这个sql算法如果再稍加优化,还能应付上亿级的数据)
--比如说:Create table t2(Code1 CHAR(6),Code2 NVARCHAR(50);
DECLARE @se NVARCHAR(100);
SET @se=N'北京市朝阳区南湖南路15号院甲1号';
SELECT * FROM(
SELECT * FROM t2
WHERE left(Code2,3)= LEFT(@se,3)) t
WHERE Code2=left(@se,len(Code2));


  • 打赏
  • 举报
回复
5。

如果面试时,能够写出5楼的代码,就应该可以录取了(在其linq代码上稍微增加一句 ret.OrderBy(...).FirsrDefault()语句)。

我们在面试时有过类似的上机编程题目,预留的时间是3个小时,可惜没有一人做出来这个题目。其实用五分钟时间写成5楼这样的2、3句话完成编程,也就算是做出来这道题了。
缪军 2012-04-03
  • 打赏
  • 举报
回复
现在我再次测试4000行/200k的数据,耗时在0-16ms之间
测试平均值如下:
从客户端发送的字节数	751	
从服务器接收的字节数 140.8
时间统计信息
客户端处理时间 2.9ms
总执行时间 8.0ms
服务器应答等待时间 5.1ms

showjim 2012-04-03
  • 打赏
  • 举报
回复
[Quote=引用 20 楼 的回复:]

谢谢楼上各位大侠的提醒 醍醐灌顶.

昨天特定去查了 字典树相关. 查找速度是上去了 可是构造树的时候占用了大量的内存空间
[/Quote]
4000数据能占几个内存?
即使使用分词的模糊查询,这种查询应该是几百ns级别的。
showjim 2012-04-03
  • 打赏
  • 举报
回复
[Quote=引用 19 楼 的回复:]
你说的这种交互方式,我们十几年前就采用了,其实逐字检索更加容易实现,
出现位置不同,对于sql也就是用charindex或者padindex或者其他不同的字符串函数的区别

另外,我已经说了,我的测试样本是700万行/400M的数据,比楼主的数据复杂得多,
没用索引时,就算是用"="检索,返回0行,在我的机器上也需要1秒钟,
我不知道你说的毫秒以内如何实现,
又或者你是以服务器配置测试的,那我想,我的代码也会同样是毫秒级完成
[/Quote]
如果要求的是效率,这事就不是数据库能做的
showjim 2012-04-03
  • 打赏
  • 举报
回复
[Quote=引用 20 楼 的回复:]

谢谢楼上各位大侠的提醒 醍醐灌顶.

昨天特定去查了 字典树相关. 查找速度是上去了 可是构造树的时候占用了大量的内存空间
[/Quote]
4000数据能占你几个内存?trie图是关键,trie树(字典树)也可以,即使模糊查询用到分词,单次查询应该是?ns级别的。
aspvfp 2012-04-03
  • 打赏
  • 举报
回复
谢谢楼上各位大侠的提醒 醍醐灌顶.

昨天特定去查了 字典树相关. 查找速度是上去了 可是构造树的时候占用了大量的内存空间
缪军 2012-04-03
  • 打赏
  • 举报
回复
[Quote=引用 26 楼 的回复:]
[/Quote]
注意力放在性能评估上了,没注意算法有bug,改了一下,
(其实,这个写法简单而已,不一定就好,只是我觉得根据楼主提供的线索,应该没什么性能问题,
最终的算法还是要根据实际数据来优化,已测试结果为依据)

SELECT TOP 1 Code1,Code2 FROM(
SELECT Code1,Code2,Width=LEN(Code2) FROM t2
WHERE left(Code2,3)= LEFT(@se,3)) t
WHERE Code2=left(@se,Width))
ORDER BY Width DESC;

aspvfp 2012-04-03
  • 打赏
  • 举报
回复
[Quote=引用 16 楼 的回复:]
楼主,把这些放在数据库中处理吧,别说4000条数据,
我做了个测试,七百万条数据(大小400兆),不需要任何索引,1秒钟之内搞定:
(这个sql算法如果再稍加优化,还能应付上亿级的数据)

SQL code
--比如说:Create table t2(Code1 CHAR(6),Code2 NVARCHAR(50);
DECLARE @se NVARCHAR(100);
SET @s……
[/Quote]


这样好像有点小bug 会查出2-3条数据
只能用 个top 1 + desc


110000 北京市
110105 北京市朝阳区
=========河北省石家庄市桥东区某某某地址===============
会取出这样三条数据
130000 河北省
130100 河北省石家庄市
130103 河北省石家庄市桥东区



缪军 2012-04-03
  • 打赏
  • 举报
回复
[Quote=引用 18 楼 的回复:]
[/Quote]
你说的这种交互方式,我们十几年前就采用了,其实逐字检索更加容易实现,
出现位置不同,对于sql也就是用charindex或者padindex或者其他不同的字符串函数的区别

另外,我已经说了,我的测试样本是700万行/400M的数据,比楼主的数据复杂得多,
没用索引时,就算是用"="检索,返回0行,在我的机器上也需要1秒钟,
我不知道你说的毫秒以内如何实现,
又或者你是以服务器配置测试的,那我想,我的代码也会同样是毫秒级完成
  • 打赏
  • 举报
回复
业务是从第33个字符才开始出 --> 也许是从第33个字符才开始出


假设需要返回最后一次出现的行政区,但是必须是最长的(“北京市”就比“北京市朝阳区”短),而当用户按任意键包括backspave键的时候你又要每一次都重新查找,并且考虑到网络传输的速度对用户体验的影响,这个就要求使用比较有效率的算法来实现,使得每一次查询都在设计毫秒以内。
  • 打赏
  • 举报
回复
不一定行政区是在给定的第一个字符,业务是从第33个字符才开始出现“北京市朝阳区”这些字的。

另外,“1秒钟”太长了。比这个复杂好基本的(但是算法基本上一模一样)的需求,网页通过在用户打入每一个字时(类似于autocomplete)随时通过互联网访问到服务器,然后服务器找到复杂的规则中的地址信息,再返回到客户端,这整个程序也就是在0.5秒以内。实际上服务器端(普通pc机)的查找运算速度应该在15毫秒以下,并且计算要求比这个“行政区”查询要复杂多了。
  • 打赏
  • 举报
回复
[Quote=引用 12 楼 的回复:]

在数据库中 select top 1 code
from mytable
where '北京市朝阳区南湖南路15号院甲1号'
like name +"%"
order by name desc
[/Quote]

这种搜索用不到半点索引(无法用到对地址信息的索引),跟#5楼是一类。还需要创建数据库,写入数据,跑到数据库系统里查询(而不是直接在应用程序内存里查询),比#5楼的要麻烦多了。

实际上如果遇到面试时“要求用 C# Framework 2.0 ”这种要求,我们不妨稍稍“鄙视”一下下。这都什么年代了,不用新的开发工具也就算了,连7年前的.net类库中的错误我们都不想订正了吗?要避免类库中的错误,就应该版本升级。
opopl 2012-04-02
  • 打赏
  • 举报
回复
在数据库中 select top 1 code
from mytable
where '北京市朝阳区南湖南路15号院甲1号'
like name +"%"
order by name desc
  • 打赏
  • 举报
回复
另外,其实#1楼是关键。#2楼说到用字典会“最快”。但是从最重要的启发式思路上看,则立的关键是首先要知道按照每一个字拆分而创建一个树,这样的数据结构是关键的。至于树的每一个节点的子树使用一个
List<Tree> (需要遍历子树的一个属性来知道每一个儿子代表的char,然后找到要深度查找的子树)
还是
Dictionary<char, Tree> (直接根据key找到要深度查找的子树)
其实不是最重要的。


所以#1的启发比#2楼的重要。
  • 打赏
  • 举报
回复
[Quote=引用 6 楼 的回复:]
2楼说的有点道理
要从右向左试探
北京市朝阳区南湖南路15号院甲1
北京市朝阳区南湖南路15号院甲
北京市朝阳区南湖南路15号院
北京市朝阳区南湖南路15号
北京市朝阳区南湖南路15
北京市朝阳区南湖南路1
北京市朝阳区南湖南路
北京市朝阳区南湖南
北京市朝阳区南湖
北京市朝阳区南
北京市朝阳区 找到 返回
[/Quote]

“2楼说的”跟你理解的正好相反。

比如说先去字典树上去找到“北”这个字,那么这个字下面就只有儿子节点“京”这个字,然后下面有一个儿子节点“市”这个字,再下面则有13个儿子节点(null、“市”、“东”、“西”、“崇”、“宣”、“朝”、“丰”、“石”、“海”、“门”、“房”、“通”),然后在“朝”下面有一个儿子节点“阳”.....

然后用你的文字作为输入,去从“北”这个字开始去字典树上深度搜索,最深地址能达到“北京市朝阳区”的“区”字的节点,于是就直接返回这个最深的结果了。而这个“区”的节点的值就是“北京市朝阳区”这几个字。

这样,假设你的行政地址不是4000条,而是400万条,那么从一个100个字的地址中找到第一次出现的行政区编号,也是毫秒级的一个方法而已。而#5楼的那个解法所耗的时间就很“恐怖”了。


不过话要说回来,不知道现在的学校教育是怎么回事,学了3年软件,甚至再工作2年的程序员,能够像#5楼那样给出答案的也只是少数。大多数人似乎只会套用模板,抄袭csdn上的答案,背诵概念。我真心的希望大多数程序员只至少都会学习#5楼那样学会迅速拿出一个解决方案的能力。要想有这样的能力,就应该学习linq等高级的、直接了当反应逻辑解题思路的好编程方法,解决新问题,而不要只是去一遍遍重复使用低级的工具做着千篇一律的“开发”工作。
threenewbee 2012-04-02
  • 打赏
  • 举报
回复
没有限制条件谈不上怎么解决。

必须给出测试用例。

输入数据可能是非标准化的,要不要考虑怎么处理。
输入数据是否有序。

我们要求一过性的速度还是反复查找的速度

等等。
WAN 2012-04-02
  • 打赏
  • 举报
回复
树 + 哈希
  • 打赏
  • 举报
回复
假设是查行政区代码的话,那么就从左向右查是最快的。比如只要是有“北”开头的行政区,那么就不可能再去考虑从“京”这个字以后的任何字符开始查找了。而从“北”开头的行政区中有几个与这个字符串相匹配,实际上很容易找到,你保留字符最多的一个行政区作为结果就行了。

这有一个数据结构的问题,就是假设先把4000行数据创建为单字的“倒排序”全文检索索引树,那么就可以非常快地搜索到。
aspvfp 2012-04-02
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 的回复:]

Dictionary<string, string> dict = new Dictionary<string, string>();

var ret = from kp in dict where "北京市朝阳区南湖南路15号院甲1号".IndexOf(kp.Key)> 0 select kp;

效率不是最高 但代码最少的方法
[/Quote]

这是LINQ吧 Framework 2.0 还不支持吧.
另外这样不不准确 可能会查到 北京市 这一级

2楼说的有点道理
要从右向左试探
北京市朝阳区南湖南路15号院甲1
北京市朝阳区南湖南路15号院甲
北京市朝阳区南湖南路15号院
北京市朝阳区南湖南路15号
北京市朝阳区南湖南路15
北京市朝阳区南湖南路1
北京市朝阳区南湖南路
北京市朝阳区南湖南
北京市朝阳区南湖
北京市朝阳区南
北京市朝阳区 找到 返回




加载更多回复(5)

111,126

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Creator Browser
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧