算法題集錦

過年在家沒怎麼發博客,現在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;
}








發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章