交易系统开发技能及面试之TechCoding

文章目录

概要

编程面试是整体面试中最核心的部分,毕竟开发者都是手艺者,需要动手跟思维都达到公司候选人的要求,而这部分通常包括以下几个知识要点:

1、链表、二叉树、字典树等数据结构
2、使用哈希表、栈、队列、优先队列来解决问题
3、数组、字符串的搜索及排序
4、位运算在低时延开发过程中是优化利器
5、动态规划虽然是算法中的经典也是最难的,但通常用的比较少,可以查看大厂是否有相关面试题
6、路径算法通常也较少出现,但推荐掌握BFS/DFS算法

关于面试题,建议多刷leetcode

Q1设计股票订单撮合系统,支持5000以上不同品种,单品种每天上百万订单

因为每个品种的订单簿是隔离的,相互不影响,所以采用独立线程或者独立进程处理某个品种的交易订单,这样做的目的可以让品种的处理进程水平扩展,可以将不同品种的订单处理部署在不同的机器,再通过交易网关进行路由。可以根据对品种的交易量进行预测,对交易活跃的品种升级CPU或者内存完成垂直扩展

Q2 设计订单簿,用于支持下单、撤单、撮合功能,撮合采用价优方式

//订单簿由买方、卖方两个二叉树构成
struct OrderBook {
   
	Limit* buyTree;
	Limit* sellTree;
	Limit* lowestSell;
	Limit* highestBuy;
};

//二叉树节点记录当前价格档位以及订单数量
//节点上记录当前价位的订单队列(时间优先)
struct Limit {
   
	int limitPrice;
	int size;
	Limit* parent;
	Limit* leftChild;
	Limit* rightChild;
	Order* headOrder;
	Order* tailOrder;
};

//订单只需唯一标识id、交易方向、数量、价格
//采用列表方式存储订单
//
struct Order {
   
	int orderId;
	bool buyOrSell;
	int num;
	int limitPrice;
	Order* nextOrder;
	Order* prevOrder;
	Limit* parentLimit;
};

//采用订单id为key, 订单为value,用于撤单
map<int, Order*> orderMap;
//采用limitPrice为key, Limit节点为value, 用于
map<int, Limit*> sellLimitPriceMap;
map<int, Limit*> buyLimitPriceMap; 

采用订单id为key,通过map存储所有的订单

1、下单
当下单时,如果二叉树不存在该价位的订单,那么添加二叉树节点,时间复杂度为O(log m)。
如果limitPriceMap存在该价位,那么时间复杂度:O(1)

2、撤单
通过orderMap进行索引,时间复杂度 : O(1)

3、成交
通过map找到对手价的节点,遍历节点的订单列表进行撮合

4、获取最高最低价
通过OrderBook的lowestSell、lowestBuy指针进行查找

Q3 设计一个网关用于接收用户订单并将其转发给交易撮合服务,该网关需要实现限流功能,给定 N,T两个值,表示在T微妙时间内只处理N个消息,相当于滑动窗口控制。要求设计订单控制类:根据N、T、Input,处理接收的订单流,返回pass 或者 fail。

N:5 T:20
消息流:
1640277839000
1640277839000
1640277839012
1640277839014
1640277839014
1640277839014
1640277839015
1640277839020
1640277839037
1640277839039
输出:
1640277839000 pass
1640277839000 pass
1640277839012 pass
1640277839014 pass
1640277839014 pass
1640277839014 fail
1640277839015 fail
1640277839020 fail
1640277839037 pass
1640277839039 pass

要点:
采用queue队列来接收消息,在限制时间T内,队列的大小不能超过N个。

/*
实现订单网关的限流
给定 n,t两个参数
表示在t毫秒内,只处理n个order
*/

#include <deque>

class rateLimiter {
   
private:
	//缓冲窗口队列,接收订单的时间戳
	std::deque<uint64_t> _dq;
	int _numMsgs;
	int _timeWindow;

public:
	rateLimiter(int n, int t) : _numMsgs(n), _timeWindow(t) {
   

	}

	/// <summary>
	/// 判断订单是否可被处理
	/// </summary>
	/// <param name="ts">订单的时间戳</param>
	/// <returns></returns>
	bool IsRateLimiterOK(uint64_t ts) {
   
		if (_dq.empty()) {
   
			_dq.push_front(ts);
			return true;
		}

		// 最新的订单时间戳在对头,队尾的订单如果与新订单的时间戳超过了时间窗口,那么可以弹出,剩余的表示在时间窗口内已处理的订单
		while (!_dq.empty() && (ts - _dq.back() >= _timeWindow)) {
   
			_dq.pop_back();
		}

		// 已出来的数量小于被允许的数量,说明新订单可被处理,记录其时间戳并返回true
		if (_dq.size() < _numMsgs) {
   
			_dq.push_front(ts);
			return true;
		}

		return false;
	}

	int GetCountInWindow() {
   
		return _dq.size();
	}
};

Q4 实现前缀树的插入跟搜索

/*
前缀树 也叫字典树,常用于压缩字典内容
*/

#include <string>
using namespace std;

class PreFixTree {
   
	struct Node {
   
		//需要确认字符内容不超过26个字母
		Node* child[26];
		bool isWord;
		//初始化子节点为nullptr
		Node() {
   
			for (int i = 0; i < 26; i++) {
   
				child[i] = nullptr;
			}
			isWord = false;
		}
	};

	Node* root;
public:
	PreFixTree() {
   
		root = new Node();
	}

	void Insert(string word) {
   
		Node* curr = root;
		for (auto ch : word) {
   
			int index = ch - 'a';
			//已存在相同字符的节点,继续遍历
			if (curr->child[index]) {
   
				curr = curr->child[index];
				continue;
			}
			//新建节点
			curr->child[index] = new Node;
			curr = curr->child[index];
		}
		curr->isWord = true;
	}

	bool Search(string word) {
   
		Node* curr = root;

		for (auto ch : word) {
   
			int index = ch - 'a';
			//不存在节点
			if (!curr->child[index]) {
   
				return false;
			}
			curr = curr->child[index];
		}
		return curr->isWord ? true : false;
	}
};

Q5 给定一个存储股票价格的数组,代表某股票每天的价格,采用t+1方式,当天买隔天卖。计算出最优的盈利价格,比如:

输入:[7,1,5,3,6,4]
输出:5
即买入价格 1 ,到 卖出价格为6时,收益最大为5.

#include <vector>
#include <math.h>

using namespace std;

//给出股票的价格数组,找出最佳买卖点,得出收益
class FindStockBuySell {
   
public:
	static int FindResult(vector<int> prices) {
   
		if (prices.empty()) {
   
			return 0;
		}
		int result = 0;
		int minPrice = prices[0];
		for (int i = 1; i < prices.size(); ++i) {
   
			result = max(result, prices[i] - minPrice);
			minPrice = min(minPrice, prices[i]);
		}

		return result;
	}

	//动态规划
	static int SumBuySell(vector<int> prices) {
   
		if (prices.empty()) {
   
			return 0;
		}

		int n = prices.size();
		int** dp = new int*[n];
		for (int i = 0; i < n; ++i) {
   
			dp[i] = new int[2];
		}
		dp[0]
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值