Leetcode 151总结

刷了若干天leetcode, 总算弄完了。代码在这里。 Reverse Words in a String 模拟 字符串 Evaluate Reverse Polish Notation 模拟 后缀表达式求值 Max Points on a Line 平面给出N个点,找一个直线,使得经过的点数最多。枚举每个点,以此为原点坐标,求出相对原点坐标,然后计算y/x,用hash表计数求出最大的重复值。O(N^2) Sort List QuickSort和MergeSort链表版本. O(NlogN) 值得注意的情况是所有元素都相同时,假设qsort分段从左到右的话,qsort会退化O(N^2). Insertion Sort List 插入排序链表实现. O(N^2) LRU Cache LRU-Cache算法。最有复杂度保证每次get,set操作都为O(1). 双向链表+Hash。 用C++10的STL的LIST和MAP的GET,SET复杂度O(logN) Binary Tree Postorder Traversal 智商着急,写个栈模拟后序遍历都卡半天。 网上有很简洁的写法。 void postOrderTraversalIterativeTwoStacks(BinaryTree *root) { if (!root) return; stack<BinaryTree*> s; stack<BinaryTree*> output; s.push(root); while (!s.empty()) { BinaryTree *curr = s.top(); output.push(curr); s.pop(); if (curr->left) s.push(curr->left); if (curr->right) s.push(curr->right); } while (!output.empty()) { cout << output.top()->data << " "; output.pop(); } } Binary Tree Preorder Traversal Reorder List 翻转后半段链表,然后间隔一个拼接。O(N) ...

July 20, 2014 · Zheng Hu

Zookeeper的客户端Kazoo

Zookeeper自然不用我多讲了,一个分布式协调工具。有几个问题我比较好奇: Zookeeper如何实现watcher的异步回调? (代码细节) Zookeeper的分布式锁如何实现? Zookeeper的Queue, barrier等东东怎么玩的? 看了下python版本的ZK客户端kazoo的实现,明白了个大概。 举个简单客户端编程的例子 #!/usr/bin/python import logging from time import sleep from kazoo.client import KazooClient # print log to console logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.DEBUG) zk = KazooClient('127.0.0.1:2181') zk.start() def children_callback(children): print '****' , children children = zk.get_children('/zookeeper', children_callback) zk.create('/zookeeper/goodboy') #zk.delete('/zookeeper/goodboy') while True: sleep(1) Kazoo实现异步的大致思路 首先有个前提:每一个Client向服务器发送Request的时候,都会带有一个xid , 每请求一次,xid加1, 同时zk服务端对单个客户端的请求处理士严格按照xid从小到大的顺序来处理并返回。 在这个条件下,客户端每次发送请求之前,先把(request, async_object, xid)这个元组放到一个pending队列里面(其中request包含了请求信息, async_object里面含有回调函数),然后当zk服务端有任何response返回的时候,直接从pending队列中取队首元素就可以完成之前注册的回调函数。 其实更一般的实现是这样的: 客户端发送异步请求时,都在本地存放一个(request,async_object, xid) 元组到map里面。 然后当异步返回response的时候, 根据返回的xid到map里面找出相应的(request, async_objec, xid), 这样就可以执行回调函数了。 鉴于zookeeper处理请求的有序性,所有只用一个pending队列求能轻松搞定。 有几个问题需要考虑: 每个API既可以异步调用,又可以同步调用。当然同步调用可用在异步调用的基础上实现。 每个Znode上面的Watcher都要采用异步触发的方式实现。 不能阻塞主线程,因为主线程要执行上层开发者的代码逻辑。 Kazoo的实现原理(以上述代码片段为例) 给出几点解释: 2步中, KazooClient的主线程通过os的pipe来做线程间通信。这个还挺有意思的。 主线程会往writepipe里面写一个字节,通知thread_1 3,4步中, thread1是通过 select([socket, readpipe],[],[]) 来检测到socket和readpipe上的读事件的。 当socket上有读事件,说明Zookeeper-Server有Response返回。这时候可以去读取socket上的数据。 当readpipe上有读事件时,说明主线程又往queue这个队列发送请求了。因为主线程会往queue里放请求,然后往writepipe写字节。 5步中,thread-1将自己Client的Xid自增之后,发送给Zookeeper服务端。就返回了。thread-1自己用了一个While True去不断的探测socket和write_pip上的读事件去了。其实就干上面讲的两步。 ...

June 7, 2014 · Zheng Hu

从第K元素看数据结构

本文涉及的源代码及文章请点击这里下载。 这篇文章讨论的是序列中第K大或第K小元素,由于第K大元素可以转化为求第N-K+1小元素(N为序列的长度),所以,本文专注于讨论第K小元素。 本文讨论的几个问题: 对给定整数序列,求该序列中第K小的元素。 对某一整数序列,允许动态更改序列中的数。动态查询序列中第K小元素。 给定一个整数序列和若干个区间,回答该区间内第K小元素。 对某一整数序列,允许动态更改序列中的数。动态查询序列中的第K小元素。 关键字 第K小元素 树状数组 线段树 平衡二叉树 归并树 划分树 单调队列 堆 块状表 问题一 问题描述: 给出一个乱序整数序列a[1…n] ,求该序列中的第K小元素。(1<=K<=N)。 算法分析: 用基于快速排序的分治算法,期望复杂度为O(N)。 代码: int qs(int *a , int l , int r , int k){ if(l == r) return a[l] ; int i = l , j = r , x = a[(l+r)>>1] , temp ; do{ while(a[i] < x) ++ i ; while(a[j] > x) -- j ; if(i <= j){ temp = a[i] ; a[i] = a[j] , a[j] = temp ; i++ ; j-- ; } }while(i<=j) ; if(k <= j) return qs(a , l , j , k); if(k >= i) return qs(a , i , r , k); return x ; } 练习 RQNOJ 350 这题数据量比较小1≤N≤10000,1≤M≤2000 。所以计算量不会超过10^7。当然用到后面的归并树或划分树,能将复杂度降低。 【问题二】 问题描述: 给出一个乱序整数序列a[1…n] ,有3种操作: 操作一:ADD NUM 往序列添加一个数NUM。 操作二:DEL NUM 从序列中删除一个数NUM(若有多个,只删除一个)。 操作三:QUERY K 询问当前序列中第K小的数。 输出每次询问的数。假设操作的次数为M。 算法分析: 这题实际上就是一边动态增删点,一边查询第K小数。这类题有两种思维方法:一是二分答案,对当前测试值mid,查询mid在当前序列中的排名rank , 然后根据rank决定向左边还是右边继续二分。另一种是直接求第K小元素。 这个题可以用各种类型的数据结构解决,其时间复杂度和编程复杂度稍有区别: 线段树:运用第一种思维,当添加(删除)一个数x时,相当于往线段树上添加(删除)一条(x , maxlen)(注意是闭区间)长度的线段。这样询问时,覆盖[mid , mid]区间的线段数就是比mid小的数,加上1就是rank。二分次数为log(maxlen) ,查一次mid的rank , 复杂度为O(logN) 。所以总复杂度上界为O(MlogNlogN) 。为方便比较,这里认为log(maxlen)等于logN。 ...

March 2, 2014 · Zheng Hu

谈谈Redis字典的实现

Hash表(Hash Table) hash表实际上由size个的桶组成一个桶数组table[0…size-1] 。当一个对象经过哈希之后,得到一个相应的value , 于是我们把这个对象放到桶table[ value ]中。当一个桶中有多个对象时,我们把桶中的对象组织成为一个链表。这在冲突处理上称之为拉链法。 负载因子(load factor) 假设一个hash表中桶的个数为 size , 存储的元素个数为used .则我们称 used / size 为负载因子loadFactor . 一般的情况下,当loadFactor<=1时,hash表查找的期望复杂度为O(1). 因此,每次往hash表中添加元素时,我们必须保证是在loadFactor<1的情况下,才能够添加。 容量扩张(Expand)& 分摊转移 当我们添加一个新元素时,一旦loadFactor大于等于1了,我们不能单纯的往hash表里边添加元素。因为添加完之后,loadFactor将大于1,这样也就不能保证查找的期望时间复杂度为常数级了。这时,我们应该对桶数组进行一次容量扩张,让size增大 。这样就能保证添加元素后 used / size 仍然小于等于1 , 从而保证查找的期望时间复杂度为O(1).但是,如何进行容量扩张呢? C++中的vector的容量扩张是一种好方法。于是有了如下思路 : Hash表中每次发现loadFactor==1时,就开辟一个原来桶数组的两倍空间(称为新桶数组),然后把原来的桶数组中元素全部转移过来到新的桶数组中。注意这里转移是需要元素一个个重新哈希到新桶中的,原因后面会讲到。 这种方法的缺点是,容量扩张是一次完成的,期间要花很长时间一次转移hash表中的所有元素。这样在hash表中loadFactor==1时,往里边插入一个元素将会等候很长的时间。 redis中的dict.c中的设计思路是用两个hash表来进行进行扩容和转移的工作:当从第一个hash表的loadFactor=1时,如果要往字典里插入一个元素,首先为第二个hash表开辟2倍第一个hash表的容量,同时将第一个hash表的一个非空桶中元素全部转移到第二个hash表中,然后把待插入元素存储到第二个hash表里。继续往字典里插入第二个元素,又会将第一个hash表的一个非空桶中元素全部转移到第二个hash表中,然后把元素存储到第二个hash表里……直到第一个hash表为空。 这种策略就把第一个hash表所有元素的转移分摊为多次转移,而且每次转移的期望时间复杂度为O(1)。这样就不会出现某一次往字典中插入元素要等候很长时间的情况了。 为了更深入的理解这个过程,先看看在dict.h中的两个结构体: typedef struct dictht { dictEntry **table; unsigned long size; unsigned long sizemask; unsigned long used; } dictht; typedef struct dict { dictType *type; void *privdata; dictht ht[2]; int rehashidx; /* rehashing not in progress if rehashidx == -1 */ int iterators; /* number of iterators currently running */ } dict; dictht指的就是上面说的桶数组,size用来表示容量,一般为2^n ,sizemask(一般为2^n-1,二进制表示为n个1)用来对哈希值取模 , used表示hash表中存储了多少个元素。 dict表示字典,由两个桶数组组成,type是一些函数指针(哈希函数及key,value的一些处理函数)。 ...

February 13, 2014 · Zheng Hu

平面扫描思想在ACM竞赛中的应用

 摘要: 平面扫描思想在计算几何,计算机图形学,网格计算等计算机理论领域有广泛的应用。有非常多的经典算法借助平面扫描的思想极大的降低了算法时间复杂度。例如线段相交问题、平面上多矩形轮廓算法、平面多矩形求交、空间冲突检测算法、Voronoi图构造算法、平面最近点对等等。 本文介绍了在ACM程序设计竞赛中经常用到的几个平面扫描算法。根据这些算法的作用,大致分为以下几类: 数据统计; 几何实体位置关系的检测; 最近点对。 本文依次选取了三类算法中具有代表性的经典算法加以介绍,并有针对性的剖析了大量经典ACM算法竞赛试题,以期对ACM程序设计竞赛参赛者起到抛砖引玉的作用。 关键字 平面扫描 ; ACM大学生程序设计竞赛 ; 算法 ; 数据统计 ; 几何实体位置关系; 最近点对 第一章:引言 ACM国际大学生程序设计竞赛(英文全称:ACM International Collegiate Programming Contest(ACM-ICPC或ICPC)是由美国计算机协会(ACM)主办的,一项旨在展示大学生创新能力、团队精神和在压力下编写程序、分析和解决问题能力的年度竞赛。经过近30多年的发展,ACM国际大学生程序设计竞赛已经发展成为最具影响力的大学生计算机竞赛。 竞赛的历史可以上溯到1970年,当时在美国德克萨斯A&M大学举办了首届比赛。当时的主办方是the Alpha Chapter of the UPE Computer Science Honor Society。作为一种全新的发现和培养计算机科学顶尖学生的方式,竞赛很快得到美国和加拿大各大学的积极响应。1977年,在ACM计算机科学会议期间举办了首次总决赛,并演变成为目前的一年一届的多国参与的国际性比赛。迄今已经举办了35届。 平面扫描思想是一种在计算几何、计算机图形学等领域经常用到的算法优化思想。由于ACM程序设计竞赛是一类对算法时间复杂度和空间复杂度要求非常高的高水平竞赛。竞赛中出现的许多的计算几何题目和高级数据结构题目都可以通过运用平面扫描的思想优化时空复杂度,使得程序能在题目给定的时间限制和空间限制下快速求出问题的解。 平面扫描算法一般由扫描线、事件点和当前扫描线事件点集合三个部分组成。扫描线一般是一根平行于坐标轴的水平线(或垂直线)。它按照从上到下(或从左到右)的顺序,依次检测事件点,通过删除或新增事件点来维护当前扫描线事件点集合。当前扫描线事件点集合通常都是用线段树、树状数组、红黑树等平衡二叉树来维护的,特殊情况下也需要用Hash表、块状表、跳跃链表等高级数据结构来达到维护目的。通过查询当前扫描线事件点集合的相关信息,我们就可以获得问题的答案。 正文将ACM竞赛中的扫描线算法分成三类,并依次介绍相关类型的算法。希望对参赛者的算法学习有所帮助。 第二章:算法介绍及试题剖析 第一节:数据统计 经典问题A 平面坐标系上有N个矩形,这些矩形的四条边都平行于X轴或Y轴。每个矩形可以被其他矩形部分或者完全遮盖,所有矩形合并成区域的边界周长称为轮廓周长。例如图1中所有矩形的轮廓如图2所示: 请设计一个算法,计算所有矩形的轮廓周长。 输入 平面上N个矩形,第i个矩形用左下顶点坐标(Xi, Yi)和右上顶点坐标(UXi, UYi)表示。 输出 所有矩形轮廓周长。 算法分析 先离散化。用每个矩形的四边所在直线将二维平面切割。这样,就只要考虑AB这样的单元线段了。把组成轮廓的单元线段长度相加就是矩形轮廓周长和。假设L1, L2 …, L8 各竖直线经过映射后对应于mapx1,mapx2 …, mapx8。为方便描述,令mapx0=mapx1。 这样,考虑mapx(i-1)到mapxi之间的横向单元线段属于轮廓的总长度。在[mapx(i-1), mapx(i)]之间任作一竖直线L,将所有与L相交的矩形在L上的投影线段求并后的独立不相交线段数计为count,则共有2count(mapxi - mapx(i-1))长度的横向线段为轮廓长度。 例如,在L6和L7之间的作一条竖直线段L,与L相交的矩形有两个,它们在L上的投影分别为AB和CD,将AB和CD求并后,算出的独立不相交的线段数count=2。所以,在之间的横线单元线段属于轮廓的总长度就等于2count(L7-L6)=4*(L7-L6)。 通过上面分析,我们已经能够计算出所有轮廓周长中平行于x轴的总长度了。当然,可以通过类似的方法求出轮廓周长中平行于y轴的总长度。但是,在竖直线从左到右扫描的过程中,我们可以通过更为简洁的方法得到轮廓周长中平行于y轴的总长度。 考虑与L7相交的矩形在L7上的投影为[E,F]U[G,H],与L8相交的矩形在上的投影为[I,J],当竖直线扫描线从L7扫描到L8时,[E,F]就“露”了出来,成为竖直轮廓的一部分。这个[E,F]正好是L7上投影和L8上投影绝对值之差。如果相邻的扫描线的矩形投影分别为M1, M2 , 那么,在扫描过程中“露出”的纵向边长度为|M1-M2|。 ...

January 1, 2013 · Zheng Hu

我的公开分享

2021.01.08 Flink Forward Asia 2021 Beijing: The Best Practice of Integrating Apache Flink with Apache Iceberg 2021.04.25 Flink+Iceberg Shanghai Meetup: How Flink and Iceberg Solve the Challenges of Data Lake Ingestion 2020.12.15 Flink Forward Asia 2020 Beijing: How to Analyze CDC Data in Iceberg Data Lake Using Flink, Blog 2019.07.20 HBaseConAsia 2019 Beijing: Further GC optimization for HBase 2.x: Reading HFileBlock into offheap directly. 2018.10.18 小米内部科普HBase读路径 PDF 2018.06.18 HBaseConWest2018 美国湾区-圣何塞(San Jose)分享《HBase Practice In XiaoMi》 PPT Video 2017.08.04 HBaseConAsia2017 主题《HBase Practice At XiaoMi》。 2016.03.24 公司内部分享: 《TokuDB索引结构》PPTX版本 PDF版本 2016.04.13 公司内部分享:《LevelDB存储引擎》 PPTX版本 PDF版本 2015年 Vitess解析 2011年 湖南师范大学第三届大学生计算机程序设计竞赛命题。 试题PDF 解答PPT 参考程序 2011年 湖南师范大学2010年12月份月赛命题。试题PDF 解答PPT 参考程序。

January 1, 2012 · Zheng Hu