算法题集锦

过年在家没怎么发博客,现在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;
}








  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值