顺序存储双有序列的二分折叠查找算法

源于Google的一道笔试题:给出两个升序排列的n维整数数组,求这2n个数中的第n大数。(算法导论:Exercise 9.3-8)

笔试的时候有一个想法,但是写的很草率,回来仔细想了下,把它写出来。我想这个应该是别人已经做过的东西,但我也不知道怎么查,第一次自己写查找算法,在我这就把它叫做“顺序存储双有序列的二分折叠查找算法”吧。其实这个算法好象没什么实际作用,有序列的查找嘛,计算量都不大。

算法描述太累赘了,就写个基本思想吧:把每个数组尾部的指针向前移动n/2个数,然后比较这两个数的大小,如果a中的大,就把这个数与b从尾部开始比较直到某个数小于它,这样,第n个数肯定不在两个指针后面,把两个指针后面的数字个数加起来,作为count。n-count就是执行下次类似查找的n。主要工作量节省在每次跨n/2上。至于最后如何选择第n个数,需要分析一下具体情况。

int search4n(int *a, int *b, int n)
{
int *p,*q,*p1,*q1,range;
p = a+n;
q = b+n;
if( a[0]>b[n-1] ) return a[0];
if( b[0]>a[n-1] ) return b[0];
while(n>1)
{
range = ( n+1 ) / 2;
p1 = p-range;
q1 = q-range;
if( *p1>*q1 ){
n -= range;
p = p1;
q--;
while( *q>*p1 ){
q--;
n--;
}
}
else{
n -= range;
q = q1;
p--;
while(*p>*q1){
p--;
n--;
}
}
}
if( n==0 ) return *p > *q ? *p : *q;
else if( n==1 ) return *(--p) > *(--q) ? *p : *q;
else return 0;
}

其实这个算法可以推而广之到任意两个有序列的第n大数查找,见下面。但是为了控制指针越界需要浪费一些判断语句,其实有很多判断语句只有在足够接近边界时候才起作用。这算法也可以推广到链表,但似乎效率要低些。

int search4n(int *a, int *b, int an, int bn, int n)
{
int *p, *q, *p1, *q1, range;
p = a+an;
q = b+bn;
if( a[0]>b[bn-1] ) return n <= an ? a[an-n] : b[an+bn-n];
if( b[0]>a[an-1] ) return n <= bn ? b[bn-n] : a[an+bn-n];

while( n>1 ){
range = ( n+1 ) / 2;
p1 = p-range>a ? ( q==b ? p-n : p-range) : a;
if(q1==b && *q1>*(p-1)) return *(p-n);
q1 = q-range>b ? ( p==a ? q-n : q-range) : b;
if(p1==a && *p1>*(q-1)) return *(q-n);
if( *p1>*q1 ){
n -= (p-p1);
p = p1;
q--;
while( *q>*p && q>b ){
n--;
q--;
}
*q>*p ? q : q++;
}
else{
n -= (q-q1);
q = q1;
p--;
while( *p>*q && p>a ){
n--;
p--;
}
*p>*q ? p : p++;
}
}
if( n==0 ) return *p<*q ? *p : *q;
else if( n==1 ) return (p1==a || q1==b) ? ( p1==a ? *(--q) : *(--p) ) : ( *(--p)>*(--q) ? *p : *q );
else return 0;
}

算法复杂度:

时间复杂度,显然最优状况下,比较只需要常数次(如果比较次数不包括各种边界条件的比较);最坏状况下,比较主要在中间那个while双循环,假设n对应的比较次数为f(n),则f(n)=1+k+f(n-[(n+1)/2]-k),其中1=<k<[(n+1)/2];f(1)=1;f(2)=2。则f(n)的最大值就是最差情况下的比较次数,我估计不出来,所以无法说它的平均算法复杂度了,但由f(n)的下降速度可以看出比较次数是介于log(n)和n之间的,究竟是多少数量级不太清楚,回头再想想怎么估计平均复杂度,应该是需要考虑k的分布。

空间复杂度,需要4个指针空间和1个整型变量空间,常数级。

我估计这个算法应该不是最优的,尤其对题目来说这种算法肯定不是最优的,因为求中位数的话可以更简单,但它可以推广到任意两有序列的查找,也算是一种途径。不过我觉得这个算法里可能还有漏洞,只是我没检查出来,也该有优化的可能,以后再慢慢想吧。
Copyright © 2005-2006 Solrex Yang. All rights reserved.

奇怪的Ajax请求浏览器显示情况

接着前上面的“静态主页使用Ajax”,发现同样的网页Ajax请求返回内容,在Opera中就能显示,而在IE、Firefox、Netscape里只有本机建的服务器上可以显示,上传到Google Page就无法显示,但是XML文件确实是已经下载下来了,为什么?

示例网页:http://solrex.googlepages.com/index.htm
Copyright © 2005-2006 Solrex Yang. All rights reserved.

使用SQL删除冗余或重复记录

数学建模三天,比二年级那次轻松多了,还打了场球,每天睡了五六个小时,不过还是很累,早上把论文交上去以后,倒头睡到下午6点。

还是蛮有收获的,下面是一些经验:

1.使用SQL删除冗余或者重复记录

由于A题给的是5个两万八千多条记录的Excel表,为了处理方便,我们都导入到Access数据库里,使用SQL查询语言处理数据。里面有一些重复的记录,但不是全部重复,大概在十几个字段里有几个不会重复,而且不能全部删除,必须得保留一条。 由于牵涉的是数据的纵向比较,以前没有做过,不知道怎么写,从网上查查,也没有查到合适的方法,后来自己琢磨出来一种方法可以删除冗余数据:

先从表里查出来哪些是冗余记录,查出重复字段,并且取重复记录主键字段的最小值,加到重复字段里存到临时表temp1中:

SELECT min(ID) AS tid,问卷编号 AS t1,Q2e AS t2,Q2g AS t3,Q2h1 AS t4,Q2j AS t5 INTO temp1 FROM 2001 WHERE Q2d=1 GROUP BY 问卷编号,Q2e,Q2g,Q2h1,Q2j HAVING count(ID)>1;

再从表里把所有冗余记录选出来,存到临时表temp2中:

SELECT * INTO temp2 FROM 2001,temp1 WHERE 问卷编号=t1 AND Q2e=t2 AND Q2g=t3 AND Q2h1=t4 AND Q2j=t5;

把主键字段在临时表temp2中(表明是重复记录),且不在temp1中(表明是冗余记录)的记录删除。如果只是删除重复数据的话,就把后面一个判断去掉就可以了。

DELETE * FROM 2001 WHERE ID IN (SELECT ID FROM temp2) AND ID NOT IN (SELECT tid FROM temp1);

其实不同的数据库软件有不同的方法,因为各个数据库有自己的SQL语言支持,有的数据库定义了自己的SQL语句或者特征字段,可以非常容易的删除重复字段。由于ACCESS对SQL的支持比较弱,既然上面的方法对ACCESS都可以适用,应该能应用于很多数据库软件。

2.Java连接ACCESS的方法

因为数据的处理语句太多,而且近似,用手工的方法太笨也太慢,我就写了一个JAVA程序来自动进行查询。还是应了那句话,磨刀不误砍柴工。

下面是JAVA连接ACCESS的设置:

try{
  Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
  String DBUrl="jdbc:odbc:driver={Microsoft Access Driver (*.mdb)};DBQ=dataBaseAddress";
  Connection con=DriverManager.getConnection(DBUrl,"","") ;
  Statement stmt=con.createStatement();
} catch (Exception e) {
  e.printStackTrace();
}

接着就可以像别的数据库一样执行查询了,但是最奇异的是我发现ACCESS不接受JAVA程序里带转义双引号的查询语句,查询语句里只能使用单引号。

静态页面使用Ajax

其实Ajax技术并不是只有那些动态页面可以使用,在静态页面里也可以有很大用处,当然,这里面只牵涉到了静态xml文件,而不可能是动态生成的了。

在我的页面里,由于主要是放些文章,是采用的这样的方法,xml文件的结构是:<solrex_shuffling>
<article>
<title><![CDATA[]]></title>
<info><![CDATA[]]></info>
<content><![CDATA[]]></content>
</article>
</solrex_shuffling>

把所有的文章都放到一个xml文档里,用CSS控制显示格式,用Ajax技术控制显示内容。本来是想一下子读取出来所有的节点内容,放到变量里,这样使用起来就不用再去访问服务器了。对于小型的应用,这样做是可以的,但是对于像文章这种长字符串存储到变量里是不可行的。这样就只好使用一个笨方法,每次去request这个xml文件,使用全局变量来控制取出的内容。

IE和Firefox在这方面又表现出了不同,当把xml文件取回来的以后,IE就不会去再访问服务器了,只使用缓存里的文件,速度比较快。但是Firefox是会再去访问,取回最新的文件。其实可以对文件的访问进行控制,让浏览器只访问更新的文件,但是这又得是服务器端和网页程序配合下才可以,对于静态页面来说,就不用考虑那么多了,如果访问量在一定范围内的话,其实速度还是可以的。而且Firefox可以在本地执行request,不需要访问服务器,而IE就不行。

IE和Firefox在处理xml内容的时候也会有不同,例如取出底层节点内部文本的时候,IE是用node.text,而Firefox是用node.textContent。我不知道有没有一个统一的接口,我是在调试JS代码的过程中发现的这两个不同,就用我自己的方法实现了,很简单,先判断节点具有不具有node.textContent属性,具有就采用Firefox处理方式,不具有就用IE的处理方式。

还有值得注意的是,Firefox会把xml中两个标签之间的空格和换行符当作是一个节点,而IE不会,所以为了通用性考虑,xml中的标签之间尽量不留空,不要像HTML编程一样为了看清楚格式放很多空白字符。

在节点内部使用<![CDATA[]]>是为了能不让解析器解析里面的内容,比如HTML格式的文本。

在编码过程中我还发现,用Object.innerHTML写入的内容仍然可以被getElementById()方法访问,这样就说明是可以循环嵌套写入代码了。

Sam Stephenson的Prototype JavaScript framework对JS和Ajax开发都是很有帮助的,利用它可以节约很多劳动。

做完以后发现,只用一个htm文件和一些xml文件就可以显示非常丰富的内容,用起来非常舒服。但是发现在我电脑上的本地服务器运行得好好的,上传到googlepage却没办法运行,猜想可能是google禁用了一些javascript脚本。反正至少在google提供的在线HTML编辑的话,一些JS代码是不能用的。
Copyright © 2005-2006 Solrex Yang. All rights reserved.

JavaScript Cookies

说明:

cookie是保存在用户计算机上的变量。每次相同的计算机请求该页时,它(计算机)就会发送cookie。通过JS可以建立和检索cookie的值。
格式类似于:User=solrex; expires=Thu,01-Jan-70 00:00:01 GMT; domain=www.google.com; path=/; secure;

cookie使用:

基本的cookie在用户关闭浏览器时会被自动删除,因为通常的域只允许在用户的机器上保留20个cookie.如果希望将cookie保存在用户的机器上就需要设置一个cookie失效的时间,它的格式是一种叫做GMT的特殊格式.
例如:Mon, 27-Jul-2006 00:00:00 GMT

要正确设置GMT不是件容易的事,需要计算好某个日期是星期几.Javascript有一个日期的方法叫做toGMTString可以解决这个问题.下面是设定某个时间的一个例子:var the_date = new Date("December 31, 2008");
var the_cookie_date =the_date.toGMTString();

一旦设置了cookie的失效期,必须在cookie设置里加入这条信息. cookie_name=cookie_value;expires=date

我在使用中发现,FireFox对cookie格式的限制比IE要宽松很多,用IE时,失效日期必须严格的按照GMT格式来写,否则设定日期不起作用或者浏览器根本不接受这个cookie,而FireFox可以接受一些日期格式不是很规范的cookie。

缺省情况下cookie只能被在同一服务器上的同一路径下设置了该cookie的网页读取.例如,如果在"http://localhost/vstock/index.jsp"询问了用户的姓名,可能需要另一个网页中访问它.所以就必须设定该cookie的路径.路径"path"用于设置可以读取一个cookie的最顶层的目录.将cookie的路径设置为最顶层目录可让该目录下的所有网页都能访问该cookie.

方法:在你的cookie中加入path=/; 如果只想让"vstock" 目录中的网页可以使用该cookie,则加入path=/vstock;.假如网站有许多小的域名,例如:"a.localhost","b.localhost" 和"c.localhost",缺省情况下只有"localhost" 域下的网页可以读取该cookie.如果想让"localhost"下的所有虚拟主机都可以读取该cookie,必须在cookie中加入 "domain=localhost" .

我使用的cookie操作函数,有些cookie类使用起来其实并不方便,还有一些缺陷,不如直接用函数:function setCookie(c_name,c_value,c_expiredays,c_path,c_domain,c_secure)
{
var c_expires = new Date();
c_expires.setTime(c_expires.getTime() + c_expiredays*86400000);
document.cookie=c_name+ "=" +escape(c_value)
+((c_expiredays==null) ? ("") : (";expires=" +c_expires.toGMTString()))
+((c_path==null) ? ("") : (";path="+c_path))
+((c_domain==null) ? ("") : (";domain=" +c_domain))
+((c_secure==null) ? ("") : (";secure"));
}

function getCookie(c_name)
{
if (document.cookie.length>0)
{
c_start=document.cookie.indexOf(c_name + "=");
if (c_start!=-1)
{
c_start=c_start + c_name.length+1;
c_end=document.cookie.indexOf(";",c_start);
if (c_end==-1) c_end=document.cookie.length;
return unescape(document.cookie.substring(c_start,c_end));
}
}
return null;
}

function delCookie(c_name)
{
var c_value=getCookie(c_name);
if(c_value!=null)
{
var c_expires = new Date();
c_expires.setTime(c_expires.getTime() - 1);
document.cookie= c_name + "="+""+";expires="+c_expires.toGMTString();
}
}
Copyright © 2005-2006 Solrex Yang. All rights reserved.

一些小技巧

在JAVA中使用正则表达式

其定义在包java.util.regex里,使用举例: String accountRegx = "^[A-Za-z0-9][\w\.]{1,18}[A-Za-z0-9]$";
Pattern accountPattern = Pattern.compile(accountRegx);
Matcher accountMatcher = accountPattern.matcher(account);
boolean accountMatches = accountMatcher.matches();这样语句比较麻烦,但是编译好后比临时再构造要快。在JAVA中使用正则表达式时,注意的转义,需写为\。

无href属性的A对象的CSS属性问题

在CSS1中,对于无href属性(特性)的a对象,伪类:link,:hover,:active,:visited均不发生作用,而且在CSS1中这几个伪类只能用于a对象。在CSS2中,这几个伪类可以用于其它对象。

在使用中发现,Firefox1.5以上是支持CSS2的,而IE6.0支持不完全。

例如在使用无href属性的a对象时候,鼠标移动到链接上时,在IE里什么都不发生,在Firefox里,鼠标变为选择光标,:hover属性会起作用,即可以变色。

针对这样的问题,可以采用定义a的cursor方法解决,即在样式表中添加上这样一条:
a {
cursor:pointer;
}

此处的cursor不适合用hand,因为浏览器支持不一样,但是用pointer的话,当鼠标移动到链接上时候均能变成手型。
Copyright © 2005-2006 Solrex Yang. All rights reserved.

JSP在Tomcat下与MySQL连接

被这个问题困扰三天了,查了不少资料,写了不少测试,真是痛苦啊。不过总算基本解决,赶紧把方法写下来,留待以后参考。

使用的软件版本:
Tomcat:Apache Tomcat 5.5.12
MySQL:MySQL Server 5.0.16
JDK:J2SE Development Kit 5.0
JRE:J2SE Runtime Environment 5.0
JDBC Driver:mysql-connector-java-3.1.13-bin.jar

别名:
$CATALINA_HOME: Tomcat的安装目录,例如D:serveTomcat 5.5
$APP_HOME:JSP网站工程所在的根目录,例如D:vstock

总问题:

如何在Tomcat环境下,使JSP和后台Java class与MySQL数据库建立连接并进行查询?

问题一:JDBC驱动到底应该放在哪里?

这个问题网上有很多回答,但是根据我的测试,根本不需要把JDBC驱动放到$CATALINA_HOMEcommonlib目录下,这个目录里面是进行Tomcat的全局配置的。为了程序的可移植和适应性考虑,只需要把JDBC的驱动mysql-connector-java-3.1.x-bin.jar放到$APP_HOMEWEB-INFlib下就可以实现在网站工程的JSP文件和$APP_HOMEWEB-INFclasses下的java bean的class里使用JDBC驱动。

问题二:应该使用哪个驱动?

有很多人不知道,jar文件其实是一种把class文件压缩到一起的一种压缩包。mysql-connector-java-3.1.x-bin.jar里一般包含两个包,一个是com.mysql.jdbc,另一个是org.gjt.mm.mysql。用WinRAR解压后可以发现,前者下面有很多class,而后者下面只有一个Driver.class,怀疑后者应该是为了兼容而设计的,而具体实现仍是在前者的包里实现,故虽然说两者使用结果上无甚区别,但最好还是用com.mysql.jdbc.Driver。

问题三:如何进行MySQL数据库的连接?

下面给出两个例子,分别是只在JSP文件里使用数据库连接和在class里使用连接。先是测试数据库的建立,后是两个MySQL数据库连接测试源文件。运行以后均输出一句话:Evil:Japanese。

1.测试数据库的建立(大小写无所谓):

进入mysql,执行以下语句:

drop database IF EXISTS datasource;
CREATE DATABASE datasource;
use datasource;
CREATE TABLE user(username varchar(50) NOT NULL,password varchar(50),PRIMARY KEY (username));
INSERT into user(username,password) values("Japanese","evil");

2.JSP文件里连接数据库,测试页面testDB.jsp,位置$APP_HOME下

testDB.jsp内容:

<%@ page contentType="text/html;charset=GB2312" %>
<%@ page import="java.sql.*" %>
<%@ page import="javax.sql.*" %>
<%@ page import="javax.naming.*" %>
<%@ page session="false" %>
<html>
<head>
<title>test</title>
<meta http-equiv="Content-Type" content="text/html;charset=gb2312">
<link href="vstock_style.css" rel="stylesheet" type="text/css">
</head>
<%
String driverName = "com.mysql.jdbc.Driver";
String userName = "MySQLuser";//你的MySQL用户名
String userPassword = "Password";//MySQL密码
String dbName = "datasource";
String tableName = "user";
String url = "jdbc:mysql://localhost/"+dbName+"?user="+userName+"&password="+userPassword;
try{
Class.forName("com.mysql.jdbc.Driver").newInstance();
Connection conn = DriverManager.getConnection(url);
Statement state = conn.createStatement();
String strSql="select * from user";
ResultSet rs=state.executeQuery(strSql);
while(rs.next()){
out.print("Evil:"+rs.getString(1));
}
out.println("True");
}catch(Exception e)
{
out.println("Error");
}
%>
</head>
<body>
</body>
</html>

3.class里连接数据库,测试文件testDB.java,位于$APP_HOMEWEB-INFclassesJavaBean目录下,测试文件test.jsp,位于$APP_HOME下。

testDB.java内容:

package JavaBean;
import java.sql.*;
public class testDB{
public testDB(){}
String driverName = "com.mysql.jdbc.Driver";
String userName = "MySQLuser";
String userPassword = "Password";
String dbName = "datasource";
String tableName = "user";
String url = "jdbc:mysql://localhost/"+dbName+"?user="+userName+"&password="+userPassword;
public ResultSet getConn()
{
ResultSet rs = null;
try
{
Class.forName("com.mysql.jdbc.Driver").newInstance();
Connection conn = DriverManager.getConnection(url);
Statement state = conn.createStatement();
String strSql="select * from user";
rs = state.executeQuery(strSql);
}
catch(Exception e)
{
System.out.println("Error");
}
return rs;
}
}

test.jsp内容:

<%@ page contentType="text/html;charset=GB2312" %>
<jsp:useBean id="test" scope="page" class="JavaBean.testDB"/>
<%@ page import="java.sql.*" %>
<html>
<head>
<title>test</title>
<meta http-equiv="Content-Type" content="text/html;charset=gb2312">
<link href="vstock_style.css" rel="stylesheet" type="text/css">
</head>
<%
ResultSet rs = test.getConn();
while(rs.next())
{
out.print("Evil:"+rs.getString(1));
}
%>
<body />
</html>

问题四:可能的错误类型

一定要检查变量名、变量类型、函数的拼写,大小写,函数的括号,包的包含关系,数据库的路径。不要从网页上直接拷贝,最好手动输入,要检查全角的空格。唉,教训惨重啊!

结论:用以上的方法,可以把JDBC驱动只放到用户的网站工程里,移植时只需要在Tomcat里建立一个虚拟网站目录和把MySQL数据库拷贝到服务器的数据库目录下即可,不需要再有其他配置。

Copyright © 2005-2006 Solrex Yang. All rights reserved.

JavaScript学习笔记(1)

Amazing,在Ajax技术被炒作地神乎其神的今天,似乎又看到了更加流行的趋势,JavaScript,一种基于对象的客户端脚本语言,能用非常简单的方式实现很多网页特效。

1.在HTML中添加JavaScript代码
head或者body标签里,大部分代码一般在head,函数调用在body里。
<SCRIPT language="JavaScript[1.2|1.3]">
JavaScript code here.
</SCRIPT>

2.调用外部脚本文件
<SCRIPT language="JavaScript[version]" src="yourfile.js"></SCRIPT>

3.用脚本输出网页
document.write("Whatever in HTML, i.e. <br>,<b>,</b>"+variable);

4.注释
单行://comment here
多行:/*comment here*/

5.变量定义和赋值
var var_name=var_value;
没有数型之分,可修改,字符串用"string"或者’string’,正则表达式用/string/。
转义符:,退格:,换页:f,换行:
,回车:
,制表: ,bool:true|false,空:null。

6.函数构造和使用
构造:function func_name(para1,para2,...)
{func_body;
return ret_value;}
调用:var var3=function func_name(var1,var2);
JavaScript严格区分大小写,变量有作用域,函数体内重新声明可避免修改全局变量。

7.运算符和程序控制语句
和C几乎完全一样。

8.事件句柄
JavaScript预定义关键字,用来处理网页上事件的激发,即是对某个动作的反应,如:鼠标点击,滑过,获得焦点等。
使用举例:<FORM><INPUT type="button" onClick="window.alert(’Hi!’);">Click Here.</FORM>
点击按钮可弹出一个窗口,内容为:Hi!。
常用句柄:
onClick,onMouseOver,onMouseOut,onLoad,onUnload,onFocus,onBlur,onChange,onSubmit,onAbort,onError,onDragDrop,onKeyDown,onKeyPress,onKeyup,onMouseDown,onMouseUp,onMouseMove,onReset,onResize,onSelect.

9.对象知识
建立对象(用构造函数):
function class_name(para1,para2,...)
{properties go here
this.property1=para1;
this.property2=para2;
methods go here
this.method1=normal_func_in_thispage_without_bracket;}
对象实例化:var instance_of_class=new class_name(var1,var2);
建立对象(对象初始化):
instance_of_class={property1:var1,property2:var2,...} //比较适用于建立数量较少实例的对象。

10.预定义的JavaScript对象
Navigator对象:访问客户端浏览器属性,使用方法:
属性:navigator.property,property主要有:
appCodeName,appName,appVersion,language,mimeTypes,platform,plugins,userAgent。
方法:navigator.method(),method()主要有:
javaEnabled(),plugins.refresh(),preference(),savePreferences()。

Copyright © 2005-2006 Solrex Yang. All rights reserved.

JavaScript学习笔记(2)

JavaScript有很多非常有用的预定义对象。

11.Document对象
属性:
alinkColor,anchors,applets,bgColor,cookie,domain,embeds,fgColor,formName,forms,images,lastModified,layers,all,linkColor,links,plugins,referrer,title,URL,vlinkColor。
方法:
open(),close(),write(),writeln()。

12.Window对象
属性:
closed,defaultStatus,frames,length,location,name,opener,parent,self,status,top。
方法:
Alert(),confirm(),find(),print(),prompt(),open(),close(),blur(),focus(),moveBy(),moveTo(),resizeBy(),resizeTo(),scrollBy(),scrollTo(),setInterval(),cleanInterval(),setTimeout(),cleanTimeout()。
其实里面有好多方法是不适合使用的,因为涉及到对用户窗口的操作,现在的倾向是尽量少控制用户窗口。

13.数组的定义和操作
JavaScript里的数组类似于对象,有属性和方法。
定义:var array_name=new Array(elem1,elem2,...);
访问:array_name[index]
属性:constructor,index,input,length,prototype。
方法:concat(),join(),pop(),push(),reverse(),shift(),unshift(),slice(),splice(),sort()。

14.关联数组
可以将两个数组关联起来,用一个的元素来代替另一个的索引号。
var array_name=new Array();
array_name["Japanese"]="Evil";
array_name["American"]="Idiot";

15.数学和日期对象
Math属性:
E,LN10,LN2,LOG10E,LOG2E,PI,SQRT2,SQRT1_2。
Math方法:
abs(),acos(),asin(),atan(),ceil(),cos(),exp(),floor(),log(),max(),min(),pow(),random(),round(),sin(),sqrt(),tan()。
Date对象,必须先创建一个实例:var instance_name=new Date();
属性:
constructor,prototype。
方法:
getDate(),getDay(),getHours(),getMinutes(),getMonth(),getSeconds(),getTime(),getTimezoneOffset(),getYear(),getFullYear(),parse(),setDate(),setHours(),setMinutes,setMonth(),setSeconds(),setTime(),setYear(),setFullYear(),toGMTString(),toLocalString()。

16.字符串对象
实例化:var instance_name=new String("string");
属性:
constructor,length,prototype。
方法:
anchor(),big(),blink(),bold(),charAt(),charCodeAt(),concat(),fixed(),fontcolor(),fontsize(),fromCharCode(),indexOf(),italics(),lastIndexOf(),link(),match(),replace(),search(),slice(),small(),split(),strike(),sub(),substr(),substring(),toString(),toLowerCase(),toUpperCase()。

17.表单操作和框架操作
JavaScript可以使用document的属性进行表单和框架的操作,其中表单和框架也各自有自己的属性和方法。

18.Cookies的使用
建立:document.cookie="your_cookies";
其中your_cookies格式为:name:value&name:value,其中分隔符可用任何间隔符,但不能使用空格、逗号和分号。如果要使用它们,需要使用escape(var_string)将字符串转为CGI能接受的字符代码。
添加失效日期:在cookie里添加一个name/value对,expires:GMTdate。
读cookie:可直接读,也可用unescape()转换,然后用split()将其分割成需要的变量。

19.图像对象
实例化:var pic_name=new Image(width,length);
属性:
name,src,width,height,border,hspace,vspace,lowsrc,complete。

20.eval()函数
eval()可以将作为参数发给它的表达式按字符串形式求值。
eval("1+1")返回2;
var todo="alert";
eval("window."+todo+"(’Hi!’);");会执行window.alert(’Hi!’);
这样非常有利于函数的动态调用。

Copyright © 2005-2006 Solrex Yang. All rights reserved.

JavaScript学习笔记(3)

正则表达式
正则表达式在验证输入错误的地方有非常好和方便的用处

定义正则表达式:
var var_name=/your_pattern/flags;
也可以用构造函数来创建实例:var pattern = new RegExp("your_pattern");

用正则表达式测试字串,var_name.test(string)返回true|false。

标志(flags):
i:使匹配忽略大小写
g:进行全局匹配测试
ig:在全局进行匹配测试

正则表达式对象的属性

C++中计算顺序问题(补充)

接着上一篇的讨论,上次给吴老师发过去,老师回答的时候顺便提了一下,在VC6.0中,如果Optimization Switches选择maximum speed或者minimum size的方式来编译的话,计算结果b就是9。我试了一下,不管是用release或者debug mode,如果选择default或者Disables optimizations,结果就是7,如果选择maximum speed或者minimum size,结果就是9。

本来想反编译一下看看原理的,可当有优化时,从代码实在看不出来,debug调了半天也没有看到哪句是关键语句,感觉全是系统调用,只好作罢。

查了下MSDN,说是如果选择优化的话会有Local and global common subexpression elimination和Automatic register allocation。自动分配寄存器的解释是:This optimization allows the compiler to store frequently used variables and subexpressions in registers; the register keyword is ignored.鬼知道它怎么用的寄存器,难不成它在make的时候已经把结果给算出来了?

用VC写的程序可真大呀,拿这个为例,default是168k(172,083B),maximum speed和minimum size是104k(106,547B)。调程序的时候看到好多系统信息字符串,像service pack 2之类的,还有许多错误提醒的程序分支,它好多的工夫放在了对系统的识别和错误的处理上了。有感于此就顺便看了一下TC的表现:

用TC3.0编译上个程序,按C++方式,大小:5.96k(6,108B),汇编核心代码:
:0001.0298 33F6 xor si, si
:0001.029A C746FE0000 mov word ptr [bp-02], 0000
:0001.029F 46 inc si
:0001.02A0 46 inc si
:0001.02A1 46 inc si
:0001.02A2 8BC6 mov ax, si
:0001.02A4 03C6 add ax, si
:0001.02A6 03C6 add ax, si
:0001.02A8 8946FE mov [bp-02], ax
前面0001是反编译程序后来加上的,本来应该只有后面的偏移量的。TC3.0还是用的16位寄存器,而且只在存储空间里保存了b的值,a的值就只用寄存器si了,而且结果明显也是9。

用TC3.0编译,不选择优化,大小:5.99k(6,135B),核心代码:
:0001.0297 C746FE0000 mov word ptr [bp-02], 0000
:0001.029C C746FC0000 mov word ptr [bp-04], 0000
:0001.02A1 FF46FE inc word ptr [bp-02]
:0001.02A4 FF46FE inc word ptr [bp-02]
:0001.02A7 FF46FE inc word ptr [bp-02]
:0001.02AA 8B46FE mov ax, [bp-02]
:0001.02AD 0346FE add ax, [bp-02]
:0001.02B0 0346FE add ax, [bp-02]
:0001.02B3 8946FC mov [bp-04], ax
如果不选择优化,则可以看到它给了两个变量内存地址,但是运算结果还是没变,仍然是9。

用TC2.01编译,按C语言方式,大小:4.22k(4,325B),核心代码:
:0001.01FF 33F6 xor si, si
:0001.0201 33FF xor di, di
:0001.0203 46 inc si
:0001.0204 46 inc si
:0001.0205 46 inc si
:0001.0206 8BFE mov di, si
:0001.0208 03FE add di, si
:0001.020A 03FE add di, si
TC2.0就直接用两个寄存器si,di,根本不用存储空间,倒真是干净。结果显然也是9。

这样觉得其实它们做的都对。因为如果按照C++的标准语法,+是Left to right,而Pre-increment“++"是Right to left。按照优先级,()最高,先计算,Left to right,三个括号中计算完了,再计算+,Left to right,然后才是赋值=,Right to left,把值给b。这样说的话,结果等于7的时候倒是有些违反C++语法了。
就算是把()去掉,因为规定,++与()优先级一样,也是应该先算三个++,结果还是9。

结论:

看来,这个争论似乎不是编译器的问题,而是通常理解的语法与程序规范语法的抵触。计算机不可能像人一样具有抽象的推理能力,只能给它先固定好语法的模式,它只按照规范来执行,而不管语义的多样性。

但是有一个问题是,VC的不优化处理难道不根据C++语法来吗?是不是为了某些需要,每几个运算符的结合来做的?还是还有其它的一些原因。估计这个问题可以请教一下学“编译原理”的。

Copyright © 2005-2006 Solrex Yang. All rights reserved.

C++中计算次序问题

今天计算技术基础课上有同学提出一个问题,C++里,a=0,(++a)+(++a)+(++a)为什么会等于7?

当时想可能就是自己想的,但不确定,回来后把程序反汇编一下,发现和自己想的是一样的。对此问题的解释如下:

C++程序为:

int main()
{
int a = 0;
int b = 0;
b = (++a) + (++a) + (++a);
return 0;
}

反汇编以后的主要语句代码如下:

[01]:00401028 C745FC00000000 mov [ebp-04], 00000000
[02]:0040102F C745F800000000 mov [ebp-08], 00000000
[03]:00401036 8B45FC mov eax, dword ptr [ebp-04]
[04]:00401039 83C001 add eax, 00000001
[05]:0040103C 8945FC mov dword ptr [ebp-04], eax
[06]:0040103F 8B4DFC mov ecx, dword ptr [ebp-04]
[07]:00401042 83C101 add ecx, 00000001
[08]:00401045 894DFC mov dword ptr [ebp-04], ecx
[09]:00401048 8B55FC mov edx, dword ptr [ebp-04]
[10]:0040104B 0355FC add edx, dword ptr [ebp-04]
[11]:0040104E 8B45FC mov eax, dword ptr [ebp-04]
[12]:00401051 83C001 add eax, 00000001
[13]:00401054 8945FC mov dword ptr [ebp-04], eax
[14]:00401057 0355FC add edx, dword ptr [ebp-04]
[15]:0040105A 8955F8 mov dword ptr [ebp-08], edx

下面是对每句指令的分析(注:+是右运算符,是从左向右计算):

[01]:将0赋给a,a所在的地址为[ebp-04],int型变量在内存中占四个字节
[02]:将0赋给b,b的地址为[ebp-08]
[03]:把a放到寄存器eax中,eax中值为0,dword ptr代表以双字(4字节)放入
[04]:寄存器eax中数值+1,计算结果是:++0=1,应该是计算第一个括号里的++a
[05]:把寄存器计算结果放入内存,a所在的地址。——此时a即地址[ebp-04]中的值为1
[06]:把a放到寄存器ecx中,ecx中值为1
[07]:寄存器ecx中数值+1,计算结果是:++1=2,应该是计算中间括号里的++a
[08]:把寄存器计算结果放入内存,a所在的地址。——此时a即地址[ebp-04]中的值为2
[09]:把a放到寄存器edx中,edx中值为2
[10]:把edx寄存器中的值加上a的值,实际是为了计算(++a)+(++a),计算结果是:edx:2+2=4
[11]:把a放到寄存器eax中,eax中值为2
[12]:寄存器eax中数值+1,计算结果是:++2=3,应该是计算最后一个括号里的++a
[13]:把寄存器计算结果放入内存,a所在的地址。——此时a即地址[ebp-04]中的值为3
[14]:把a的值加到edx上,计算第一个括号外面的加号,得:edx:3+4=7
[15]:把edx中的计算结果放入b所在的地址,[ebp-08]

结论:

由此可见,之所以会产生a=0,(++a)+(++a)+(+ +a)=7的情况,是由语句的执行顺序决定的,语句执行时候先计算“+”号两边的值,然后再把它们的计算结果相加。当两边都是++a时,两次++先执行,再执行+,这样就造成了“+”号两边的值是一样的,2+2=4,其他的依次类推。
Copyright © 2005-2006 Solrex Yang. All rights reserved.

Some Tips On Using MFC and STL and Containers

做计算技术基础课吴老师的作业时候发现的一些问题和解决办法,留做备份。

作业如下:

1.两个输入文件:grade.txt及grade2.txt. grade.txt每行如:"0211111001 张三丰 99"
grade2.txt也有多行,每行形如:"0201121002 周伯通 老玩童 100"
2.用已提供的MyStl.h文件。
3.定义一个输出文件,名为“你的学号+result.txt“。
4.创立一个名为Homework.cpp的文件。
5.在Homework.cpp中用Grade 的CArray, CList, CMap, vector, list, queue, map分别做:
<A>读入grade.txt, grade2.txt于容器(即上述CArray, Clist 等)。
<B>将内容输出至输出文件(见4)。
<C>如果容器可以排序,将内容排序,再将结果输出至输出文件。
<D>删除所有grade==100的记录,再将结果输出至输出文件。

问题一:MFC库的使用

由于平常只是建立空文档,很少使用到MFC库。但当程序中使用到MFC的容器类之后,如果没有选择使用MFC库,会在link时候报出"error LNK2001"错误。
解决办法是在建立Win32 Console Application的时候在向导的第二个对话框里选择"Using MFC as shared dll"复选框。已经建立好工程的,在"工程"->"设置"->"常规"选项卡里找到"Microsoft Fundation Class"下拉框,选择"using MFC as shared dll"即可。

问题二:头文件的包含关系

由于作业涉及到MFC和STL,很多东西老的库和新的都会有冲突,还有命名空间的问题,最好对每个类别建立一个头文件和源程序文件,能尽量的避免出错。
我的方法是,用MFC来处理数据的程序为一个源文件,并在头文件里只包含和MFC有关的东西,其中要包含的系统头文件有:
#include <afx.h> //包含许多基类的声明,例如:CObject,CString
#include <afxtempl.h> //包含模板类的定义
#include <iostream.h> //标准输入输出流
#include <string.h> //包含字符串处理函数
#include <fstream.h> //文件输入输出流
用STL来处理数据的源程序的头文件包括:
#include <fstream> //文件输入输出流,标准C++
#include <iostream> //标准输入输出流,标准C++
#include <string> //字符串,串处理函数,串模板,STL
#include <algorithm> //算法,STL
#include <vector> //向量模板,STL
#include <list> //列表模板,STL
#include <queue> //队列模板,STL
#include <map> //散列表(准确的来说散列表是hash_map,但这里暂且这么称呼)模板,STL
using namespace std; //使用标准名字空间
#pragma warning(disable:4786) //不提示4786号警告,因为VC6.0与标准C++的不全相容性,对长名有警告

问题三:类的定义

由于显然性的两个文件的关系,定义类的时候可以采用继承的方法,注意变量尽量声明成protected类型,这样在派生类里可以直接访问父类的变量。其实在这里并不是完全有必要把数据封装起来,但封装会有很大的好处。比如说,通过重载函数来增加程序的一致性和可读性,重载运算符对算法的支持。下面是在MFC环境下的类定义,相应在标准C++环境下不同的地方主要是CString变为string,BOOL变为:bool,char*也可变为:string。
class cSwordsman
{
public:
cSwordsman(char* Sworder_ID = "",char* Sworder_NAME = "",int Sworder_SCORE = 0);
~cSwordsman();
CString GetId();
CString GetName();
int GetScore();
void SetId(char* Sworder_ID);
void SetName(char* Sworder_NAME);
void SetScore(int Sworder_SCORE);
BOOL operator<(cSwordsman& y);
BOOL operator>(cSwordsman& y);
BOOL operator==(cSwordsman& y);
void FileOut(ofstream& fout);
void ScreenOut();
protected:
CString id;
CString name;
int score;
};
class cLongSwordsman:public cSwordsman
{
public:
cLongSwordsman(char* Sworder_ID = "",char* Sworder_NAME = "",char* Sworder_MONIKER = "",int Sworder_SCORE = 0);
~cLongSwordsman();
CString GetMoniker();
void SetMoniker(char* Sworder_MONIKER);
void FileOut(ofstream& fout);//对父类成员函数的重载
void ScreenOut();
protected:
CString moniker;
};

问题四:文件输入和输出流的参数不同

文件输入输出流其实是一个类,平常所用的:ifstream fin("grade.txt",ios::in|ios::nocreate);其实是对象的初始化。所以在上面可以在FileOut函数的参数里设为:ofstream& fout。这样在函数里就可以直接用fout输出了,通过用ofstream对象的引用做函数参数,避免了频繁的打开文件。
在<fstream.h>和<fstream>中对文件流的定义是相似但不同的,最主要的一个方面表现在打开参数上,<fstream.h>中定义的打开模式是ios::in(输入)形式的参数,而<fstream>中是以ios_base::app(添加)形式的参数。
而且<fstream.h>和<fstream>的成员方法有些使用上差别很大,比如getline。

问题五:函数重载的使用减少工作量

比如由上面两个类的定义,可以使处理相应的类型数据的函数使用相同的名字,而在参数中使用两个不同的类引用作为参数,这样就能通过参数的不同调用不同的函数,而函数名是相同的,减少记忆和出错的可能。

问题六:运算符重载以使用已有算法

在STL的<algorithm>中定义了许多现成算法,比如:sort,就是根据类中"<"的重载来排序的。

问题七:文本文件的读取

文本文件可以用标准输入输出流来读,假如已经定义了ifstream fin("grade.txt",ios::in|ios::nocreate);,就可以像cin一样读取数据,但有时候可能需要更完善的方法,还可以用fin.getline来读入一行,然后再用字符串处理函数对这和串进行处理。

问题八:STL中iterator的使用

iterator实际是一种类型的变量,定义的时候根据模板的不同而不同。在vector,list和queue中,可以把iterator当作指向成员类的指针,用iterator->PublicVar,来使用公有成员变量,用(*iterator).MemFunctions,来使用成员函数。但在map中要注意iterator指向的是一个pair,pair是map模板中定义的一个类,有两个公有成员变量,first和second。其中iterator->first,代表使用的key,iterator->second才是模板成员类的对象。用iterator->second.MemFunctions,来调用成员函数。

Copyright © 2005-2006 Solrex Yang. All rights reserved.

Solrex的新年礼物

我的Space也开半年了,这半年从羞于谈起到和朋友们很自然的交流,从当初的周访问量不超过十到现在的日均访问量到了十以上(呵呵,不好意思也不多,但是有人看已经很满足了)。朋友们的关心这里很感激啦,所以这不该过年了,送大家一个小礼物吧!

相信大家都有Chinaren的校友录吧。

If    你是管理员

恭喜你,中大奖了

Goto Part1;

Elseif    你是普通班级成员

恭喜你,也有奖

Goto Part2;

Elseif    你没有Chinaren帐户

RP也太低了吧!算了,我也帮不了你。

Return;

End

Part1:管理员篇(让你的祝福图片/Flash盖住SOHU的广告,或者跳出窗口祝福。)

嘿嘿,如果你想公开在全班面前向MM表白,我也拦不住哈。图片/Flash内容你自己确定,我这里可只是说给大家送祝福。

1.  1.进入自己的班级后, 点击"班级管理"按钮

2.   2.在“班级宣言”部分, 点击“文本编辑方式”

3.   3.在编辑框输入附件部分的代码

4.   4.回到班级首页

[效果]

我的班级

[附件]
附件代码很多可以自由设定显示位置, 若你想覆盖更多, 请自行增加或调整长度/宽度/左/顶部定位等等.这里的位置是绝对位置,就是你打开网页的页面的位置,所以你的窗口大小会影响这个图片的位置.不过可以自己调整,不知道百分比可以用吗?没有尝试.

A. 头部播放Flash代码

<EMBED style="LEFT:120px; position:absolute; TOP:56px;" src=”你要播放的Flash文件,swf格式” width=760 height=105 type=application/x-shockwave-flash;quality="high">
要点: 定位 左:120像素 顶: 56像素 宽: 760像素 高:105 像素

B. 头部显示自己喜欢的图片代码

<div style="LEFT:120; position:absolute; TOP:56px;"><img src=”图片地址” width=760px height=105px alt="XXX祝您圣诞快乐"></div>
要点: 定位 左:120像素 顶: 56像素 宽: 760像素 高:105 像素 alt属性可以去除

C. <Iframe>标签调用第三方网页
<IFRAME style="WIDTH: 320px; HEIGHT: 300px" src="你想调用的网页,htm格式" scrolling=no>

D. 弹出窗口代码
<IFRAME style="WIDTH: 0px; HEIGHT: 0px" src="你想调用的网页,htm格式" scrolling=no>

E. 代码很多 创意更多 大家自己去玩拉 因为连position:absolute;和iframe都给你了。还有什么不能的?

[FAQ]:懂网页设计的就别看了,不懂的这里解释一下

1.在网页里播放的FLASH文件只能是SWF格式,懂的人都知道,但是要是盖住广告需要横向长度比较长的FLASH,这个要注意,不然很难看。

2.如果放图片的,可以自己做,记住长760像素,宽56像素,这是SOHU的广告条长度。如果不会做,打开Photoshop,新建图片,长宽设置好,随便涂点颜色,写几个字,然后存储为GIF格式。

3.IFREAME就不说了,估计能看懂这个词什么意思的家伙,也不需要我教。

4.不知道FLASH和图片放在哪里?我只能怀疑你的智商,如果是南大的家伙,有百合帐号吧,知道百合信箱能上传文件吧,然后把上传后给你的地址贴在src=后面那个引号里面就行了,替换掉那几个汉字哈。如果不是南大,你总有个BBS或者什么网络硬盘网络相册等等一些地方能上传文件吧。用老罗的话说,几样你总得沾一样吧,不然怎么混那。

5.如果使用百合的地址,注意了,把bbs.nju.edu.cn改成lilybbs.net,这样速度比较快。还有,不建议使用SPACE的图片地址,因为它是外网,你总不想教育网内的同学什么都看不见吧。

Part2:非管理员篇(非星级会员照样发布多彩文本留言

1. 点击"班级共享"按钮
2. 选择"添加文件"
3. 选择"添加链接" [其他也可以, 随便] 并胡乱输入信息

4. 点击"添加共享"按钮
5. 回到班级首页, 在班级留言部分选择"我要留言"
6. 在刚刚发布的"我刚刚在班级共享上传了一个好东东"项, 选择"编辑留言"图标

7. 删掉这个信息, 现在可以自由的多彩留言啦!

[注] 好像最近发现“我刚刚在班级共享上传了一个好东东”没有了,大家看着办吧,如果还有的话,你真幸运。

[PS]:本篇文章感谢Belem发现的技巧,主要引用自他的Blog,但有修改补充。出于自己的小私心,嘿嘿,既然是借花献佛,主人还是神秘些为好,就不给链接了。

还有,我可不希望这篇文章被转载到百合,要知道百合的信息流动速度,今天你转上去了,明天可能SOHU就封了,就没的搞了嘛。多没意思。还有,就算SOHU不封,那么多人知道,当你用的时候就会有人给你说,切,老把戏了还在玩。你爽啊?

还有,不建议在IFREAM里面调用网页木马,如果有人这样做,实在非此文章本意,做人要厚道,同学也坑就太损了。不过也给大家提个醒,没什么网页是安全的,所以,不要有什么信任站点,也不要随便点开链接。

好了,祝大家玩得开心,新年快快乐乐,小日子过得滋滋润润。

重写主页

今天晚上数值实验,Givens方法化矩阵为三对角形,做完比较早。

没有自习,回来把自己的主页代码重新写了一遍。主要是因为原来的代码重用性太低,每个页面是自己独立的元素,左侧和上下方的属于基本不变的类型,应该有更优化的方法来处理。而且原来的页面固定元素如果改变的话比较困难,容易出现各种问题。

今天使用了内嵌页面的iframe标签,以前不是很了解,所以用了很长时间去找它的使用方法,总算把模板写好了,填内容相对来说就容易了。

明天用一点时间把原来的东西改一下,这样原来的好多重复的内容只有一个文件就可以解决了,更改也比较容易,上传文件的话也属于比较简单的处理了。

而且由于目的的不同,重新安排了文章位置。

以后有机会的话再加些漂亮的内容。

关于成绩排名问题的程序

/*
说明:
[1]运行:
程序有一点小问题,运行输出的时候会出现重复。
*/
#include <iostream.h>
#include <fstream.h>
#include <stdlib.h>
#include <string.h>

#define MAX_NUM 100

class Information{
public:
char id[8], name[12];
int score;

public:
Information & operator=(Information &b)
{ strcpy(id,b.id);
strcpy(name,b.name);
score=b.score;
return *this;
}
};

void main()
{

char filename1[256],filename2[256];

cout<<"输入源文件全路径:";
cin>>filename1;

cout<<"输入目的文件全路径:";
cin>>filename2;

ifstream infile(filename1,ios::in|ios::nocreate);
ofstream outfile(filename2);

if(! infile){
cout<<"不能打开输入文件:"<<filename1<<’/n’;
exit(1);
}
if(! outfile){
cout<<"不能打开目的文件:"<<filename2<<’/n’;
exit(2);
}

Information student[MAX_NUM],temp;

char t_id[8],t_name[12];

int t_score,count=0,i;

while(infile>>t_id)
{
infile>>t_name>>t_score;
strcpy(student[count].id,t_id);
strcpy(student[count].name,t_name);
student[count].score=t_score;
count++;
}

for( i=0;i<count;i++)
for(int j=i+1;j<count;j++)
if(student[i].score<student[j].score)
{
temp=student[i];
student[i]=student[j];
student[j]=temp;
}

for(i=0;i<count;i++)
outfile<<student[i].id<<"(A)"<<student[i].name<<"(B)"<<student[i].score<<’

’;
cout<<"已将排序后成绩写入"<<filename2<<"中。"<<endl;

infile.close();
outfile.close();

}
源文件内容:
45991901 王成 89
45991902 李名 75
45991903 张三 95
45991904 赵五 87
45991905 王立 88
45991906 孙羽政 86
45991907 周小国 85
45991908 范预 98
45991909 刘二 92
45991910 张宗 74
45991911 苏栏 73
45991912 赵新 71
45991913 曾梅每 70
45991914 李开创 69
45991915 姚为国 79
45991916 武彬 68
45991917 马三轮 61
45991918 左右 65

运行结果:
45991908范预(A)范预(B)98
45991903张三(A)张三(B)95
45991909刘二(A)刘二(B)92
45991901王成(A)王成(B)89
45991905王立(A)王立(B)88
45991904赵五(A)赵五(B)87
45991906孙羽政(A)孙羽政(B)86
45991907周小国(A)周小国(B)85
45991915姚为国(A)姚为国(B)79
45991902李名(A)李名(B)75
45991910张宗(A)张宗(B)74
45991911苏栏(A)苏栏(B)73
45991912赵新(A)赵新(B)71
45991913曾梅每(A)曾梅每(B)70
45991914李开创(A)李开创(B)69
45991916武彬(A)武彬(B)68
45991918左右(A)左右(B)65
45991917马三轮(A)马三轮(B)61

排序函数:第二种做法(内有三种子方法)

第二种做法:内有三种子方法;

// stdafx.h : include file for standard system include files,
// or project specific include files that are used frequently, but
// are changed infrequently
//

#if !defined(AFX_STDAFX_H__E3A2A4D8_18B3_40A0_A613_E0CEA5245DC1__INCLUDED_)
#define AFX_STDAFX_H__E3A2A4D8_18B3_40A0_A613_E0CEA5245DC1__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers

#include <stdio.h>
#include<iostream.h>
#include<fstream.h>
#include<string.h>
#include<iomanip.h>
#include <stdlib.h>

// TODO: reference additional headers your program requires here

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before

the previous line.

#endif // !defined(AFX_STDAFX_H__E3A2A4D8_18B3_40A0_A613_E0CEA5245DC1__INCLUDE

D_)

**************************************

// stdafx.cpp : source file that includes just the standard includes
// GradeResort2.pch will be the pre-compiled header
// stdafx.obj will contain the pre-compiled type information

#include "stdafx.h"

// TODO: reference any additional headers you need in STDAFX.H
// and not in this file

**************************************

#ifndef RECORD_RESORT
#define RECORD_RESORT

#include "stdAfx.h"

#define BUF_SIZE 1024
#define MAX_NUM 200

struct Grade {
char id[12];
char name[20];
int grade;
};

int compare(const void *arg1, const void* arg2);

#endif

***************************************

// GradeResort2.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "GradeResort2.h"
#include <strstrea.h>

int compare(const void *arg1, const void* arg2)
{
Grade *g1, *g2;
g1 = (Grade *) arg1;
g2 = (Grade *) arg2;

if(g1->grade < g2->grade) return -1;
else if(g1->grade == g2->grade) return strcmp(g1->id, g2->id);
else return 1;
}

int main(int argc, char* argv[])
{
Grade gArray[MAX_NUM], tempGrade;
int count = 0;

///*
ifstream fin("oldgrade.txt");
char buf[BUF_SIZE];
while(fin.getline(buf, BUF_SIZE)) {
strstreambuf sbuf = strstreambuf(buf, BUF_SIZE);
istream in(&sbuf);
in >> tempGrade.id >> tempGrade.name >> tempGrade.grade;
gArray[count++] = tempGrade;
}
//*/

/*
ifstream fin("oldgrade.txt");
char buf[BUF_SIZE];
while(fin.getline(buf, BUF_SIZE)) {
sscanf(buf, "%s%s%d", tempGrade.id,
tempGrade.name, &tempGrade.grade);
gArray[count++] = tempGrade;
}
*/

/*
FILE *fin = fopen("oldgrade.txt", "r");
while(fscanf(fin, "%s%s%d", tempGrade.id,
tempGrade.name, &tempGrade.grade) == 3) {
gArray[count++] = tempGrade;
}
*/
qsort(gArray, count, sizeof(Grade), compare);

ofstream fout("sortedGrade.txt");
fout.setf(ios::left);
for(int i=0; i<count; i++) {
tempGrade = gArray[i];
fout << setw(12) << tempGrade.id << " "
<< setw(20) << tempGrade.name << " "
<< setw(4) << tempGrade.grade << endl;
}

return 0;
}

排序函数:第一种做法:用STL:

这是老师给的程序:************stdafx.h**************
// stdafx.h : include file for standard system include files,
// or project specific include files that are used frequently, but
// are changed infrequently
//

#if !defined(AFX_STDAFX_H__12C9FDA9_9863_4D79_8C94_1739D2996569__INCLUDED_)
#define AFX_STDAFX_H__12C9FDA9_9863_4D79_8C94_1739D2996569__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers

#include<iostream>
#include<fstream>
#include<algorithm>
#include<string>
#include<vector>
#include<iomanip>

//#include <afxtempl.h>
//#include <afx.h>
#include <time.h>

// TODO: reference additional headers your program requires here

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before

the previous line.

#endif // !defined(AFX_STDAFX_H__12C9FDA9_9863_4D79_8C94_1739D2996569__INCLUDE

D_)

************stdafx.cpp**************
// stdafx.cpp : source file that includes just the standard includes
// GradeResort.pch will be the pre-compiled header
// stdafx.obj will contain the pre-compiled type information

#include "stdafx.h"

// TODO: reference any additional headers you need in STDAFX.H
// and not in this file

*********************gradesort.h*****************
#ifndef SORT_METHODS_H
#define SORT_METHODS_H

#include "stdAfx.h"

using namespace std;

class Grade{
public:
string id;
string name;
int grade;
public:

void SetId(string i) { id = i; }
void SetName(string n) { name = n; }
void SetGrade(int g) { grade = g; }
string GetId() { return id; }
string GetName() { return name; }
int GetGrade() { return grade; } const

Grade(string i,string n,int g):id(i),name(n),grade(g){}
Grade() { id="";name="";grade=0;}
bool operator<(const Grade& y) const
{
if(this->grade < y.grade)
return true;
else if(this->grade == y.grade && this->id < y.id)
return true;
else
return false;
}
bool operator==(const Grade& y) const
{
return (this->grade == y.grade && this->id == y.id) ? true : false;
}
};

#endif SORT_METHODS_H

**********************graderesort.cpp*********************************
// GradeResort.cpp : Defines the entry point for the console application.
//

#include "stdAfx.h"
#include "GradeSort.h"

int main(int argc, char* argv[])
{
ifstream fin("oldgrade.txt");
vector<Grade> gVector;

Grade tempGrade;
do {
fin >> tempGrade.id >> tempGrade.name >> tempGrade.grade;
gVector.push_back(tempGrade);
} while(!fin.eof());

ofstream fout("sortedGrade.txt");
sort(gVector.begin(), gVector.end());
int cnt =gVector.size();

fout.setf(ios::left);
for(int i=0; i<cnt; i++) {
tempGrade = gVector[i];
fout << setw(15) << tempGrade.id << " "
<< setw(15) << tempGrade.name << " "
<< setw(5) << tempGrade.grade << endl;
}
return 0;
}