摘要:
堆排序需先建立堆,然后对堆进行操作,每次取根节点值,将它与数组右边位置(即叶子节点)的值交换,交换后完全二叉树的叶子节点数减1,由于此时可能破坏堆的性质,需要向下将新的根节点与它的后代节点交换,直到叶子节点。首先建立堆的过程可以采用Floyd算法(heapify):完全二叉树(complete binary tree)和二叉堆都可以用一维数组表示用层序(level order)遍历的结果。Floyd算法将数组表示的任意一颗完全二叉树转换为堆,它无需开辟额外的空间,直接在数组中交换数据(即in-place做法),转换成堆结构,它的时间复杂度只有O(n),而不是预想的O(nlogn)。具体做法是自 阅读全文
摘要:
伸展树(splay tree)是一种能自我调整的二叉搜索树(BST)。虽然某一次的访问操作所花费的时间比较长,但是平摊(amortized) 之后的访问操作(例如旋转)时间能达到O(logn)的复杂度。对于某一个被访问的节点,在接下来的一段时间内再次频繁访问它(90%的情况下是这样的,即符合90-10规则,类似于CPU内或磁盘的cache设计原理)的应用模式来说,伸展树是一种很理想的数据结构。这是因为最近被访问的节点一直位于根节点的附近,从而再次被访问时的搜索路径长度比较小。这点与平衡的二叉树(比如AVL树和红黑树)不一样。另外一点与其他平衡二叉树的区别是,伸展树不需要存储任何像AVL树中平衡 阅读全文
摘要:
插入排序的基本思想是每次取右边的第一个元素,将它与左边已经排好序的元素一一比较,如果该元素比第j个元素大,就将该元素插入到第j和第j+1个元素之间。最坏的情况下(比如输入序列是个逆序),每个元素都需要跟左边已经排好序的所有元素比较,总的比较次数为:1+2+...+(n-2)=(n-1)(n-2)/2 ~ O(n^2); 插入操作需要将插入位置右边的元素平移一个位置,总得移动操作次数也为O(n^2)。插入排序是稳定的(stable),这与选择排序不同(选择排序用的是交换非相邻元素位置,所以不稳定),并且如果原序列基本上排好序的情况下,比较和移动操作的次数可以大大降低,因为它只需比较一部分元素,在 阅读全文
摘要:
冒泡排序的基本思想是每次在左边未排好序元素序列中依次挨个比较大小,如果相邻两个元素不是正确顺序(正确顺序为左边元素小于右边元素),交换该两个元素。直到左边未排好序的元素中最大的元素进入(冒泡)右边已经排好序的元素中,并作为最小元素落在已经排好序的元素序列的最左端。总的比较操作次数为:(n-1) + (n-2) + ... + (1)=n(n-1)/2; 最坏的情况总的交换次数等于总的比较操作次数, 比如输入的元素为从大到小顺序,需要每次比较(if(A[j-1]>A[j]))完之后做一次交换。在下面的实现,考虑到每一次外层循环中,如果内层循环中没有发生任何交换(exchanged = fa 阅读全文
摘要:
选择排序的基本思想是每次从右边未排好序的元素中找出最小的元素,然后将该元素和左边已经排好序的元素中最大的元素(它位于左边元素序列的最右端)交换,这样每次交换都将一个元素排好序。 总的比较操作次数为(n-1) + (n-2) + ... + (1)=n(n-1)/2;总的交换操作为n-1(所以相对其他的排序算法,它的交换操作次数比较少)。注意选择排序的一个缺点是,它是不稳定的(unstable),比如对序列(a,b,c,d),设a=b,d = min(b,c,d),那么第一次选择到最小元素d之后需要交换:a<->d,得到的结果为(d,b,c,a),因为a=b,原来a和b的位置(a.. 阅读全文
摘要:
爬楼梯问题:一段楼梯共n级台阶,每次只能走一级、两级或三级台阶,问共有多少种走法?列出n=10时的所有走法。分析:首先考虑第一步的走法:第一步可以跨1级台阶,也可以跨2级和3级台阶。设S(n)表示走n级台阶的走法数量。如果第一步跨1级台阶,剩下的台阶数为n-1, 也就是说这种情况下(即第一步跨1级台阶的)走法相当于S(n-1);如果第一步跨2级台阶,剩下的台阶数为n-2,也就是说如果第一步跨2级台阶的走法相当于S(n-2);如果第一步跨3级台阶,剩下的台阶数为n-3,也就是说如果第一步跨3级台阶的走法相当于S(n-3)。从而得到递推关系式:*************************** 阅读全文
摘要:
参考原文(原文作者:Timothy J. Rolfe):http://penguin.ewu.edu/~trolfe/DSWpaper/DSW算法用于平衡BST(二叉查找树),而且平衡后的二叉树是一颗完全二叉树(complete binary tree-即叶子节点顶多位于最后两层,而且从左到右排列)。该算法的优点是:无须开辟额外的空间用于存储节点,时间复杂度线性O(n)。该算法分两个阶段进行:首先将任意一颗二叉树通过一序列右旋转(right rotations)转换成一个单链结构(称作vine,即每个非叶子节点只有右孩子,没有左孩子);然后在第二阶段中对单链结构通过几个批次左旋转(left r 阅读全文
摘要:
AC解 - 用动态规划解决一道排列组合计数问题(序关系计算)原题如下:http://acm.nankai.edu.cn/problem.php?problem=1134[同时请参考网友pandm发的帖子:http://topic.csdn.net/u/20110525/01/adf4d0b0-2b8e-4c0a-b8da-07b27f1711cc.html?seed=589816236&r=73524109#r_73524109]There are 13 possible orderings for three numbers, if we sort them with the rel 阅读全文
摘要:
不使用递归遍历二叉树有几种方法:迭代,线索二叉树和Morris算法(通过临时转换二叉树变成一个类似链表的结构)。下面是迭代方法和递归的对比,后面单独列出Morris算法的实现。迭代方法和递归的对比实现:import java.util.Stack;/** * * Using recursive & iterative(ie. non-recursive) methods to traverse binary tree in * inorder, preorder and postorder * * @author ljs * 2011-05-25 * * */public class 阅读全文
摘要:
如果用一颗二叉树表示加减乘除及0-9的数字构成的数学表达式,对二叉树进行后序遍历得到的就是后缀表达式。后缀表达式可以通过堆栈直接计算其值。如果输入的是中缀表达式,下面的方法toBinaryTree()将它转化为二叉树。代码:import java.util.ArrayList;import java.util.List;import java.util.Stack;/** * * @author ljs * 2011-05-24 * * 把中缀表达式转换成一棵二叉树,然后通过后序遍历计算表达式的值 * 1. unary operator like -3 is not supported * 2 阅读全文
摘要:
在我的另外一篇文章"正整数中数字1的计数问题(上)"中,实现了一个简单的算法来计算f(n)。该算法由于只考虑相邻两个数的变化规律,因此在计算单个长整数时(比如n=911111111099999009L),可能要很长时间才能算完,显然该算法适用范围比较狭小。本文继续围绕那道Google面试题,探讨一个计算单个值f(n)的快速算法以及如何求出所有满足f(i)=i这样的整数(对很大的数计算时间能达到毫秒数量级)。分析:首先注意到,比如要计算i=3260这样的f(i),在算到i=3200的时候,其实又重复了以前的计算,即重新计算1到60的值。本算法就是基于这个考虑,将一个数拆成左右 阅读全文
摘要:
正整数中数字1的计数问题(上)原题出自Google的一道比较老的面试题:Consider a function which, for a given whole number n, returns the number of ones required when writing out allnumbers between 0 and n. For example, f(13)=6. Notice that f(1)=1. What is the next largest n such that f(n)=n?本实现没有利用可能存在的数学公式,只考虑相邻两个数得变化规律,因此运行效率并不理想。 阅读全文
摘要:
(原题出自微软公司面试题)问题如下:有两个序列a,b,大小都为n,序列元素的值任意整数,无序;要求:通过交换a,b中的元素,使[序列a元素的和]与[序列b元素的和]之间的差最小。例如: var a=[100,99,98,1,2, 3];var b=[1, 2, 3, 4,5,40];分析:通过交换的方式,最终的状态是在保证两个序列中元素个数相同的条件下,任何一个元素都可以位于两个序列中的任何一个。这样问题可以转化为:在一个长度为2*n的整数序列中,如何将元素个数分成两个子集,记每个子集的元素之和分别为S1和S2,使得|S1-S2|最小。显然这是一个最优化问题,如果用brute-force方法, 阅读全文
摘要:
二进制方法中,只需要移位(<<和>>)和加减操作(+和-),不像欧几里德算法中需要乘法和除法运算。虽然算法效率更高,但是程序的可读性和可维护性差一些。如果设d=gcd(u,v) = u.x + v.y, 本算法涉及到六种操作:1)已知ext_gcd(u,v)如何求ext_gcd(u,2v)=u'.x' + v'.y',其中u为奇数,v可奇可偶,d=gcd(u,v)为奇数;2)已知ext_gcd(u,v)如何求ext_gcd(2u,v)=u'.x' + v'.y',其中v为奇数,u可奇可偶,d=gcd(u,v 阅读全文
摘要:
扩展的gcd算法即除了计算gcd(m,n)还要计算整数x和y,使之满足gcd(m,n) = m.x + n.y。 下面的算法中使用迭代方式。 extendedGCD2方法是extendedGCD的简化版本,考虑到在初值向量r{-1} = [1 0], r{0} = [0 1]下,满足递推关系:r{i} = r{i-2} - q{i}.r{i-1}。采用Euclid's算法时,不仅要r(余数)的值,还需要q(商)的值。本例实现参考了Wikipedia中介绍的迭代方法:http://en.wikipedia.org/wiki/Extended_Euclidean_algorithm/** 阅读全文
摘要:
二进制GCD算法基本原理是:先用移位的方式对两个数除2,直到两个数不同时为偶数。然后将剩下的偶数(如果有的话)做同样的操作,这样做的原因是如果u和v中u为偶数,v为奇数,则有gcd(u,v)=gcd(u/2,v)。到这时,两个数都是奇数,将两个数相减(因为gcd(u,v) = gcd(u-v,v)),得到的是偶数t,对t也移位直到t为奇数。每次将最大的数用t替换。二进制GCD算法优点是只需用减法和二进制移位运算,不像Euclid's算法需要用除法,这在某些嵌入式系统中可能排上用场。本例实现参考了<<计算机编程的艺术>>(Knuth)第二卷中介绍的算法。/** * 阅读全文
摘要:
求两个整数的GCD有两个方法:采用欧几里得算法(Euclid's Algorithm)和二进制GCD算法, 这里实现的是欧几里得算法。欧几里得算法基本原理很简单,即: m = q1.n + r1m2= q2.n2 + r2 ....mi = qi.ni + ri其中m2=n, n2=r1....gcd(m,n) = gcd(m2,n2) = gcd(mi,ni)....直到ri=0(因为0<=ri<ni,所以ri可以收敛到0)。实现:/** * * @author ljs 2011-5-17 * * solve gcd(m,n) using Euclid's Alg 阅读全文
摘要:
用Eratosthenes筛子法:import java.util.ArrayList;import java.util.List;/** * * @author ljs * Sieve of Eratosthenes: determine the prime numbers within the range 1....n (n>0) * */public class Sieve { //intput: n is an positive integer (n>0) //output: a list of primes from 1 to n, as 2,3,5.... public 阅读全文
摘要:
原问题位于:http://poj.org/problem?id=1015以下为问题描述的摘录:In Frobnia, a far-away country, the verdicts in court trials are determined by a jury consisting of members of the general public. Every time a trial is set to begin, a jury has to be selected, which is done as follows. First, several people are drawn r 阅读全文
摘要:
整数的任意拆分问题(不允许重复)问题:输入两个整数n 和m,从数列1,2,3.......n 中随意取几个数(不许重复), 使其和等于m (m<=1+2+...n),要求将其中所有的可能组合列出来.分析:记整数p可以用1,2...q的所有不重复数之和表示的组合为C(p,q),则C(p,q)可以表示为以下的组合:{q} + C(p-q,q-1) (如果q<=m)或者C(p,q-1)即要么包含q,要么不包含q。另外有C(0,K)={}, C(K,0)无解(K>0)因此可以使用动态规划的方法解决。实现:import java.util.ArrayList;import java.u 阅读全文