算法题集锦

来源:互联网 发布:淘宝返利网商家 编辑:程序博客网 时间:2024/05/24 00:22

过年在家没怎么发博客,现在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("*");}elsediv++;}}

三.求二叉树的高度

求二叉树的深度,当只有根节点的时候,二叉树的深度为1。看到这题,这里的节点是不保存高度信息的,不像AVL树。而且它就是一个普通的二叉树。既然求高度,那对于某一个节点,必然要求它的左节点的高度和右节点的高度,然后比较它们,取较大的值再加1就是该节点的高度。想到这里,应该就是采用递归的方法,也有非递归方法。java程序如下:

//求二叉树的高度,根节点的高度为1public 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
原创粉丝点击