| Moso's profile魔战士PhotosBlogLists | Help |
|
October 26 你离开的那一天你离开的那一天,我不能在你身边。 黄昏下没有色彩,茫然唯有茫然。
你将要离开,唠唠不休的是我的童年。破碎而错位的记忆,依然勾画我的身影。
你是那样的亲切,那样的善良。所有美好的回忆,从你这里开始。
你走了,我不能去送你。但我知道,
我知道,未来的日子里
我会时常在梦里流泪
体会极致的悲伤
一生
October 22 选择排序、快速排序、希尔排序、堆排序不是稳定的排序算法,而冒泡排序、插入排序、归并排序和基数排序是稳定的排序算法。这几天笔试了好几次了,连续碰到一个关于常见排序算法稳定性判别的问题,往往还是多选,对于我以及和我一样拿不准的同学可不是一个能轻易下结论的题目,当然如果你笔试之前已经记住了数据结构书上哪些是稳定的,哪些不是稳定的,做起来应该可以轻松搞定。本文是针对老是记不住这个或者想真正明白到底为什么是稳定或者不稳定的人准备的。
首先,排序算法的稳定性大家应该都知道,通俗地讲就是能保证排序前2个相等的数其在序列的前后位置顺序和排序后它们两个的前后位置顺序相同。在简单形式化一下,如果Ai = Aj, Ai原来在位置前,排序后Ai还是要在Aj位置前。 其次,说一下稳定性的好处。排序算法如果是稳定的,那么从一个键上排序,然后再从另一个键上排序,第一个键排序的结果可以为第二个键排序所用。基数排序就是这样,先按低位排序,逐次按高位排序,低位相同的元素其顺序再高位也相同时是不会改变的。另外,如果排序算法稳定,对基于比较的排序算法而言,元素交换的次数可能会少一些(个人感觉,没有证实)。 回到主题,现在分析一下常见的排序算法的稳定性,每个都给出简单的理由。 (1)冒泡排序 冒泡排序就是把小的元素往前调或者把大的元素往后调。比较是相邻的两个元素比较,交换也发生在这两个元素之间。所以,如果两个元素相等,我想你是不会再无聊地把他们俩交换一下的;如果两个相等的元素没有相邻,那么即使通过前面的两两交换把两个相邻起来,这时候也不会交换,所以相同元素的前后顺序并没有改变,所以冒泡排序是一种稳定排序算法。 (2)选择排序 选择排序是给每个位置选择当前元素最小的,比如给第一个位置选择最小的,在剩余元素里面给第二个元素选择第二小的,依次类推,直到第n-1个元素,第n个元素不用选择了,因为只剩下它一个最大的元素了。那么,在一趟选择,如果当前元素比一个元素小,而该小的元素又出现在一个和当前元素相等的元素后面,那么交换后稳定性就被破坏了。比较拗口,举个例子,序列5 8 5 2 9, 我们知道第一遍选择第1个元素5会和2交换,那么原序列中2个5的相对前后顺序就被破坏了,所以选择排序不是一个稳定的排序算法。 (3)插入排序 (4)快速排序 (5)归并排序 (6)基数排序 (7)希尔排序(shell) (8)堆排序
October 19 最长上升子序列这是一个很好的题目。题目的算法还是比较容易看出来的,就是求最长上升子序列的长度。不过这一题的数据规模最大可以达到40000,经典的O(n^2)的动态规划算法明显会超时。我们需要寻找更好的方法来解决是最长上升子序列问题。
先回顾经典的O(n^2)的动态规划算法,设A[i]表示序列中的第i个数,F[i]表示从1到i这一段中以i结尾的最长上升子序列的长度,初始时设F[i] = 0(i = 1, 2, ..., len(A))。则有动态规划方程:F[i] = max{1, F[j] + 1} (j = 1, 2, ..., i - 1, 且A[j] < A[i])。
现在,我们仔细考虑计算F[i]时的情况。假设有两个元素A[x]和A[y],满足
(1)x < y < i (2)A[x] < A[y] < A[i] (3)F[x] = F[y]
此时,选择F[x]和选择F[y]都可以得到同样的F[i]值,那么,在最长上升子序列的这个位置中,应该选择A[x]还是应该选择A[y]呢?
很明显,选择A[x]比选择A[y]要好。因为由于条件(2),在A[x+1] ... A[i-1]这一段中,如果存在A[z],A[x] < A[z] < a[y],则与选择A[y]相比,将会得到更长的上升子序列。
再根据条件(3),我们会得到一个启示:根据F[]的值进行分类。对于F[]的每一个取值k,我们只需要保留满足F[i] = k的所有A[i]中的最小值。设D[k]记录这个值,即D[k] = min{A[i]} (F[i] = k)。
注意到D[]的两个特点:
(1) D[k]的值是在整个计算过程中是单调不上升的。
(2) D[]的值是有序的,即D[1] < D[2] < D[3] < ... < D[n]。 利用D[],我们可以得到另外一种计算最长上升子序列长度的方法。设当前已经求出的最长上升子序列长度为len。先判断A[i]与D[len]。若A[i] > D[len],则将A[i]接在D[len]后将得到一个更长的上升子序列,len = len + 1, D[len] = A[i];否则,在D[1]..D[len]中,找到最大的j,满足D[j] < A[i]。令k = j + 1,则有D[j] < A[i] <= D[k],将A[i]接在D[j]后将得到一个更长的上升子序列,同时更新D[k] = A[i]。最后,len即为所要求的最长上升子序列的长度。
在上述算法中,若使用朴素的顺序查找在D[1]..D[len]查找,由于共有O(n)个元素需要计算,每次计算时的复杂度是O(n),则整个算法的时间复杂度为O(n^2),与原来的算法相比没有任何进步。但是由于D[]的特点(2),我们在D[]中查找时,可以使用二分查找高效地完成,则整个算法的时间复杂度下降为O(nlogn),有了非常显著的提高。需要注意的是,D[]在算法结束后记录的并不是一个符合题意的最长上升子序列! October 04 quick_sort非递归版先贴个最笨的,stack空间会涨得很快,最差达到O(n).
A,0,r.A是数组初址,0是左限,r是右限);
q = partition( A,0,r); //q初次分成两半
void quick_sort_iter1()
{
stack s; s.push(0,q-1);s.push(q+1,r);//push入两个范围 while(!stack.isempty()) { int left = s.pop,right=s.pop; int qq = partition( A,left,right); if(qq>left+1)
s.push(left,qq-1); if(qq<right-1)
s.push(qq+1,right);
} 改进的方法,在于,压栈时,先压大的区间,再压小的区间
这样可以保证小区间先被pop处理,使栈的空间最大只有O(lgn). October 02 生日paradax, 收集者问题,硬币问题,在线招聘者问题CLRS 5.4有几个经典的概率例子,包括了前面的牺牲品是必须品的问题。摘要如下:
1. Birthday Paradax
party上有30人,他们中有两人同一天生意的概率是多少?
答案是70%+.你可能很吃惊,因为它不是你预料的30/365=8.1%.
分析如下:30个人的生日都是独立并且均匀随机分布的。第1个天的生意为某天,第2天与之不同日的概率为
364/365,第3天与前两天不同的概率为363/365.最终30人全不同的概率为II(365-i)/365(i为1到29)。
1减去它就得0.7+.
2.收集者问题。
某公司在出售的食品中附送了生肖卡,每份一张,概率均等。为收集全12生肖,预期需要买多少包?
答案是blnb,b是个数,在这里是12.
分析:我们称买到1张未收集到的卡为1 hit.买第一包时hit率为1,第二包时为(b-1)/b,第i包时为(b-i+1)/b.
于是所有的的hit为b/b+b/(b-1)+b/(b-2)+...b/(b-b+1)=blnb+O(1).
3.连续投N次币,连续向上的次数,最大有可能是多少(预期)。
答案lnN/ln2.
4.问题同前文牺牲品是必须品。
分析如下:
N个人中,前K个被抛弃。最佳人选在第i个被选中的概率为(i>K): k/(i-1)*1/n.前者表示,前i-1个人中,最优者出现在前K个中。后者表示,第i个为最佳的可能性为1/n.
所有,挑中最佳的概率为:IIk/(i-1)*1/n (i从K+1到n).
得k/n(lnn-lnk).对K求导,发现当K=n/e时,有最大值,1/e. October 01 CLRS习题5.1-3,比较不错Exercises 5.1-3: ★
Suppose that you want to output 0 with probability 1/2 and 1 with probability 1/2. At your disposal is a procedure BIASED-RANDOM, that outputs either 0 or 1. It outputs 1 with some probability p and 0 with probability 1 - p, where 0 < p < 1, but you do not know what p is. Give an algorithm that uses BIASED-RANDOM as a subroutine, and returns an unbiased answer, returning 0 with probability 1/2 and 1 with probability 1/2. What is the expected running time of your algorithm as a function of p? 题意:假设你希望以各1/2的比率输出0和1。你可以自由使用一个输出0或者1的过程BIASED-RAND
牺牲品是必须品牺牲品是必须品。费话不多说,直接正题。
有家公司招人,只录一个,有10人面试。这个面试很特殊,有如下规则:
1.每面完一个人,就要当场决定是否录用。如录用,后面的人则不再面试。
2.不能吃回头草。 公司想得到最佳人选,他们采取怎样的策略呢? 答案是,前4个面试的人无论好坏,全部否决。 从第5个开始,如果有人比前4个任何一个都好,就录用。如果一直没有,则录用最后1人。 事实上,如果有N个人面试,将采取如下策略: 1.面前面的[n/e]个,全拒 2.开始面后面的人,如果有人比前面的n/e个都好,录用。 3.如果一直没有,则录用最后一个。 理论证明如下: 前k-1个全锯,从第k个开始比前面那些牺牲品都好的就是真命天子。 不同的k取到最好的概率是 {(k-1)/n}*{[1/(k-1)]+1/k+[1/(k+1)]+...+1/(n-1)} 当n趋于无穷时,最佳的k趋于n/e。取得最好的概率是1/e. 所以说,牺牲品是必须品。 |
|
|