过年在家没怎么发博客,现在3月份在学校又被老板逼着做他的控制理论。时间正是金钱,能挤出一点就是一点,下面是我自己遇到的比较好的算法题,有的是leetcode上的题目,有的是一些公司的面试题目。
一.查询已排序的两个数组的中位数
这也是leetcode的第二题,看似很简单(如果不考虑时间因素的话确实很容易),但是考虑效率的话,这不就是随便看看就能出来的。我一看到这道题,就想起我之前看到的归并排序的合并方法,将已经排序的两个数组合并为一个,则中间那个数就是中位数,不过这个算法的时间效率是O(N+M)。下面给出我写的合并方法,用的是java:
public class Solution {
public double findMedianSortedArrays(int A[], int B[]) {
//我这里采用了归并排序的merge方法 但是时间为O(N+M);还有logN的算法
int i=0;
int j=0;
int pos=0;
int[] ss=new int[A.length+B.length];
while(i<A.length && j<B.length)
{
if(A[i]<B[j])
ss[pos++]=A[i++];
else
ss[pos++]=B[j++];
}
while(i<A.length)
{
ss[pos++]=A[i++];
}
while(j<B.length)
{
ss[pos++]=B[j++];
}
if((A.length+B.length)%2==0)
return (double)(ss[(A.length+B.length-1)/2]+ss[(A.length+B.length+1)/2])/2;
else
return (double)ss[(A.length+B.length-1)/2];
}
}
其实它有更好的算法,时间复杂度为O(log[(N+M)/2])。即先取数组A和B的中位数k,进行比较,如下三种情况:k1为数组A的中位数,k2为数组B的中位数
且k=k1+k2
1.A[k1]>B[k2]:则数组B中前一半的数肯定不是我们要找的中位数
2.A[k1]<B[k2]:则数组A中前一半的数肯定不是我们要找的中位数
3.A[k1]=B[k2]:则A[k1]或者B[k2]就是我们要找的中位数
如果是上面情况的1或者2,则我们可以剔除A或B中的一半的数。这里设为情况2,在从新得到的数组中取中位数(已经剔除了一个数组的一半,则中位数为k-k1),如果k/2大于数组的长度,则取该数组的最大值m,另一个数取k-k1-m。
递归直到一个数组为0或者判段的两个数相等,或者中位数取到了0,程序如下
C++版:
class Solution {
public:
double findMedianSortedArrays(int A[], int m, int B[], int n) {
int len = m+n;
if(len%2==0) {
return (rec(A, m, B, n, len/2) + rec(A, m, B, n, len/2+1))/2.0;
} else {
return rec(A,m,B,n,len/2+1);
}
}
double rec(int A[], int m, int B[], int n, int k) {
if(m<=0) return B[k-1];
if(m>n) return rec(B,n, A, m, k);
if(k<=1) return min(A[0], B[0]);
int pa = min(k/2, m);
int pb = k-pa;
if( A[pa-1]<B[pb-1] ) {
return rec(A+pa, m-pa, B, n, k-pa);
} else {
return rec(A, m, B+pb, n-pb, k-pb);
}
}
};
Java版:看上去的话用C++对数组处理更方便
public class Solution {
public double findMedianSortedArrays(int A[], int B[]) {
int len=A.length+B.length;
if(len%2==0)
return (rec(A,0,A.length-1,B,0,B.length-1,len/2)+rec(A,0,A.length-1,B,0,B.length-1,len/2+1))/2;
else
return rec(A,0,A.length-1,B,0,B.length-1,len/2+1); //这里是k个个数,不是坐标
}
//sa和sb分别代表需要数组A的开始和结束位置,需要保证小数组在前,这里的k代表第k个小的元素
public double rec(int[] A,int sa,int ea,int[] B,int sb,int eb,int k)
{
int alen=ea-sa+1;
int blen=eb-sb+1;
if(alen>blen) //保证长度小的数组在前
return rec(B,sb,eb, A,sa,ea,k);
if(alen==0)
return B[sb+k-1];
if(k<=1)
return A[sa]<B[sb]?A[sa]:B[sb];
int pa=(alen<k/2)?alen:k/2;
int pb=k-pa;
if(A[sa+pa-1]<B[sb+pb-1])
return rec(A,sa+pa,ea,B,sb,eb,k-pa);
else
return rec(A,sa,ea,B,sb+pb,eb,k-pb);
}
}
这个问题引申为对已排序的两个数组,求它们的第k个小的元素(数组升序排列)。
二.质因数分解
质因数分解,给定一个整数,求出该数的所有质因数,如90=2*3*3*5;这个题目我很早就写过。即从2开始,用n不断相除,判断是否有余数,如果没有余数,用刚才相除的数继续除2,如果有余数,则让2+1,3作除数。一直循环直到被除数<除数。
虽然这个程序简单,我还是把它列了出来,java版
public static void main(String[] args) {
int num=1092; //被除数
int div=2; //质因数
System.out.print(num+"=");
while(div<=num)
{
if(num%div==0)
{
num=num/div;
System.out.print(div);
if(div<num)
System.out.print("*");
}
else
div++;
}
}
三.求二叉树的高度
求二叉树的深度,当只有根节点的时候,二叉树的深度为1。看到这题,这里的节点是不保存高度信息的,不像AVL树。而且它就是一个普通的二叉树。既然求高度,那对于某一个节点,必然要求它的左节点的高度和右节点的高度,然后比较它们,取较大的值再加1就是该节点的高度。想到这里,应该就是采用递归的方法,也有非递归方法。java程序如下:
//求二叉树的高度,根节点的高度为1
public static int rec(Node node)
{
if(node==null)
return 0;
int rh=0;
int lh=0;
rh=rec(node.right);
lh=rec(node.left);
return (rh>lh)?rh+1:lh+1;
}
</pre><p>C++非递归算法 求高度:</p><p></p><pre class="cpp" name="code">int BiTreeDepthHierarchy(BiThrTree T) //非递归类层次遍历求二叉树深度
{
int depth=0,hp,tp,lc; //hp为已访问的结点数,tp历史入队的结点总数,lc为每层最后一个结点标记
LinkQueue Q; BiThrNode *p;
if(T)
{
p=T;
hp=0;
tp=1;
lc=1;
InitQueue(Q);
EnQueue(Q,p);
while(!QueueEmpty(Q))
{
DeQueue(Q,p);
hp++; //hp为已访问的结点数
if(p->lchild)
{
EnQueue(Q,p->lchild);
tp++; //tp记录历史入队的结点总数
}
if(p->rchild)
{
EnQueue(Q,p->rchild);
tp++;
}
if(hp==lc) //当hp=lc时,表明本层结点均已访问完
{
depth++;
lc=tp; //lc=tp,更新下层的末结点标记
}
}
}
return depth;
}
三.求一个小写字母串的最长不重复子串
如:字符串abcafegdcdf的最长不重复子字符串为:bcafegd,相同长度取第一个。
一般像遇到字符串的题目,一般的做法时间肯定不是O(N^2),要么是O(N),或者O(logN)或者 O(N*logN)。一般线性算法是最优的,所以要向这方面考虑。
这个问题:要设立一个int[]数组,数组坐标代表27个字母,用如str.charAt()-‘a',数组的值代表该字母的坐标。另设立一个pos代表没遇到重复字母前的位置。对字符串循环遍历,对每一个字符判断,如果它每出现过,就设定它的值;如果它出现过,则判断i到pos间的长度是否大于子串,如果大于就重新赋值。一次遍历下来能求出不重复的子字符串。这一题的关键是设定一个pos代表位置,并且每个设定的int[]数组里面存放的是每个字符的坐标。java代码如下:
//求给定字符串中最长不重复字符子串,因为题目给的字符串只有小写字母
public static String nomulStr(String str)
{
int[] con=new int[127]; //
String sub="";
int pos=-1;
for(int i=0;i<127;i++)
con[i]=-1; //因为值要存放坐标,因此不能使用默认的0,改为-1
//foreach无法赋值
for(int i=0;i<str.length();i++)
{
if(con[str.charAt(i)]>pos)
pos=con[str.charAt(i)];
int max=i-pos;
if(max>sub.length())
sub=str.substring(pos+1,i+1);
con[str.charAt(i)]=i; //更新重复字母的坐标
}
return sub;
}