给你一个长度为N的链表。N很大,但你不知道N有多大。你的任务是从这N个元素中随机取出k个元素。你只能遍历这个链表一次。你的算法必须保证取出的元素恰好有k个,且它们是完全随机的(出现概率均等)。
解:先选中前k个, 从第k+1个元素到最后一个元素为止, 以k/i (i=k+1, k+2,...,N)的概率选中第i个元素,并且随机替换掉一个原先选中的元素,这样遍历一次得到k个元素, 可以保证完全随机选取。这个算法叫做蓄水池抽样
有20个数组,每个数组里面有500个数组,降序排列,每个数字是32位的unit,求出这10000个数字中最大的500个。
将 20 个数组合并为 1 个,挨着连接起来即可,不必保证有序。在合并的数组中随机选取一个元素,然后将所有小于此元素的元素放在其左侧,大于到右侧。完成操作后,如果原来被选中的元素刚好处在右数第 500 的位置,那从它开始向右的元素即为所求。否则,如果右端元素数目大于 500,则对右端序列递归使用此方法;否则,如果左端序列数目大于 10000-500,则对左端序列递归使用此方法。复杂度 expected O(n)
在一个数组中除两个数字只出现1次外,其它数字都出现了2次, 要求尽快找出这两个数字。
位操作方法
单链表:
找出单链表的倒数第4个元素:建立两个指针,第一个先走4步,然后第2个指针也开始走,两个指针步伐(前进速度)一致。
从无头单链表中删除节点:这里采用了“移花接木”的方法。设该节点为B,下一个节点为C。那么,首先将B节点的内容替换为C节点的内容,然后,将C节点删除
判断两个链表是否相交并找出交点:先遍历第一个链表到他的尾部,然后将尾部的next指针指向第二个链表(尾部指针的next本来指向的是null)。这样两个链表就合成了一个链表,判断原来的两个链表是否相交也就转变成了判断新的链表是否有环的问题了
找出带环链表中环的起点:用两个步长分别为1和2的指针遍历链表,直到两者相遇,此时慢指针走过的长度就是环的长度。另外相遇后把其中指针重新设定为起始点,让两个指针以步长1再走一遍链表,相遇点就是环的起始点。
单链表反序,并返回新链表的头指针:
1. struct ListNode *reverseList(struct ListNode *head)
2. {
3. struct ListNode *newHead = NULL;
4. struct ListNode *tmp = NULL;
5. while(head != NULL)
6. {
7. tmp = head;
8. head = head -> next;
9. tmp->next = newHead;
10. newHead = tmp;
11. }
12. return newHead;
13. }
栈问题:
如何用一个数组实现两个栈:分别用数组的两端作为两个栈的起点,向中间扩展,两个栈中的元素总和不超过n时,两个栈不会相遇。
二叉树:
找出二叉树上任意两个结点的最近共同父结点:首先数一下两个结点的深度,然后比较深的那个往上走(深-浅)步,最后同时往上走,肯定会命中最近共同父节点的。如果你把二叉树的所有节点看成N的话,我这个算法只需要lg(N)就可以搞定了
在二叉树中找出和为某一值的所有路径:到达一个节点之后计算当前节点和sum的和,如果为target,输出路径返回,如果大于target,则直接返回,如果小于,则将当前节点的值入栈,更新sum的值,继续遍历,遍历完成之后,也就是从当前节点返回的时候,将其从栈中弹出,更新sum