浅析12306售票算法(java版)
作者:全城热恋
1.以G71列车为例,首先对车次站台进行占位编码(从1开始到最后一站递加)
对以上占位简单描述以下:G71总共18个站点那么我们的单个座位的座位标识可以用十八位长度的二进制字符串表示10000000000000000每一位代表一个站点,每天放票前初始化到下面的订票表中,数据如下余票根据座位标识中的0的个数决定最大余票数量
订票表中的始发受限站点和终到受限站点可以灵活搭配(这个就可以实现限制站点发售)
2.查询余票
如果我们要查询日期为2016-06-11,始发站保定东站(3)到韶关站(15)的G71二等座F座位余票情况只需要执行如下sql(该SQL可以实现选座位和选车厢等功能)
select GUID,车次编码,车次类型,座位类型,车厢号码,座位编码,座位位置 from 订票表
where to_number(substring(座位标识,3,15))=0
and 发车日期='2016-06-11'
and 车次编码='G71'
and substring(始发受限车站,3,4)=1
and substring(终到受限车站,15,16)=1
and 车票状态='待售'
and 车次类型='二等座'
and 座位位置='F'
3.预定票
3.1根据第二步中查询条件获取一条记录然后将车票状态改为锁定
3.2待锁定成功后进行支付
3.2支付成功后然后将保定到韶关的票(000111111111111000这里的始发站标记为0)与原有的票进行或运算,并将车票状态改为待售
100000000000000000 | 000111111111111000 = 100111111111111000 这个时候的余票标识即为动态余票
3.3如果指定时间没有支付,那么可以将这条记录的车票状态恢复为待售
100111111111111000^000111111111111000 = 100000000000000000 这个时候的余票及自动还原回去了
4.退票
获得该车次保定到韶关的票 (000111111111111000)与对应的票进行非运算,则即可回归票池子了
以下为相关java代码
import java.math.BigDecimal; public class MainTest { public static void main(String[] args) { String ticketFlag = ""; int beginStation = ; int endStation = ; long beginTime = System.currentTimeMillis(); String result = orderTicket(ticketFlag, beginStation, endStation); if (result.equals(ticketFlag)) { System.out.println("订票失败"); } else { System.out.println("订票后的结果:" + result); // 如果要取消的话,就进行这个操作 String b = buildTicket(ticketFlag.length(), beginStation, endStation); System.out.println("释放后的结果:" + releaseTicket(ticketFlag, b)); } long endTime = System.currentTimeMillis(); System.out.println("耗时:" + (endTime - beginTime)); } /** * 订票 * * @param ticketFlag * @param beginStation * @param endStation * @return */ private static String orderTicket(String ticketFlag, int beginStation, int endStation) { String result = ""; if (checkCanTicket(ticketFlag, beginStation, endStation)) { String b = buildTicket(ticketFlag.length(), beginStation, endStation); String currentTicked = toTicket(ticketFlag, b); System.out.println("预占票前结果:" + ticketFlag); result = currentTicked; } else { result = ticketFlag; } ; return result; } /** * 取消已定票 * * @param ticketFlag * @param b * @return */ private static String releaseTicket(String ticketFlag, String b) { StringBuilder tempSt = new StringBuilder(""); int length = ticketFlag.length(); for (int i = ; i < length; i++) { char tempA = ticketFlag.charAt(i); char tempB = b.charAt(i); if (tempA == '' && tempB == '') { tempSt.append(""); } else { tempSt.append(tempA); } } return tempSt.toString(); } /** * 创建区间占位票 * * @param length * @param beginStation * @param endStation * @return */ private static String buildTicket(int length, int beginStation, int endStation) { StringBuilder st = new StringBuilder(""); for (int i = ; i < length; i++) { if (i >= beginStation && i < endStation) { st.append(""); } else { st.append(""); } } System.out.println("创建区间票:" + st.toString()); return st.toString(); } /** * 生成订票后的结果 * * @param ticketFlag * @param b * @return */ private static String toTicket(String ticketFlag, String b) { StringBuilder tempSt = new StringBuilder(""); int length = ticketFlag.length(); for (int i = ; i < length; i++) { char tempA = ticketFlag.charAt(i); char tempB = b.charAt(i); if (tempA == '' || tempB == '') { tempSt.append(""); } else { tempSt.append(tempA); } } return tempSt.toString(); } /** * 是否可以订票 * * @param ticketFlag * @param beginStation * @param endStation * @return */ private static boolean checkCanTicket(String ticketFlag, int beginStation, int endStation) { boolean result = false; String tempTicket = ticketFlag.substring(beginStation, endStation); BigDecimal b = new BigDecimal(tempTicket); if (b.equals(new BigDecimal(""))) { result = true; } return result; } }