1. 程式人生 > >位操作演算法面試題

位操作演算法面試題


方案一

我們可以利用為位與操作,依次判定各個位上是否為1。

    public int hammingWeight(int n) {//依次檢測各個位
    	int count=0;
    	int location=1,value=1;
    	while(location<=32){
    		if((n&value)!=0){
    			count++;
    		}
    		location++;
    		value<<=1;
    	}
		return count; 
    }

陷阱

    public static int hammingWeight(int n) {//錯誤程式碼,不斷檢測最低位
    	int count=0;
    	int t=n;
    	while(t!=0){
    		count+=t&1;
    		t>>=1;
    	}
		return count; 
    }

如果我們通過不斷檢測最低位來統計1的個數,與此同時整數不斷右移。對於正整數我們似乎可以得到正確的結果,那是因為右移後高位補0。

但是對於負整數,右移後高位補1,那麼最終整數將會變成0xFFFFFFFF而進入死迴圈。

修正

    public static int hammingWeight(int n) {//不斷檢測最高位
    	int count=0;
    	int t=n,max=1<<31;
    	while(t!=0){
    		count+=(t&max)==0?0:1;
    		t=t<<1;
    	}
		return count; 
    }
對於左移,由於正數和負數低位都是補0。因此我們可以不斷檢測最高位來統計1的個數。

優化

    public static int hammingWeight(int n) {
    	int count=0;
    	int t=n;
    	while(t!=0){ 
    		t=t&(t-1);
    		count++;
    	}
		return count; 
    }
上面的演算法複雜度為O(k),k為二進位制的位數(這裡為32)。而該方法將複雜度降低為O(m),m為二進位制中1的個數,顯然m<=k。

原理:例如n=1100 1100,n-1=1100 1011,n&(n-1)=11001000,不難發現,n&(n-1)可以消除最低位的那個1。


分析

方案一:對於2的冪,其二進位制表示中,1的個數必定只有一個。此外,還需要注意2的冪必為正數。

public class Solution {
    public boolean isPowerOfTwo(int n) {
        return hammingWeight(n)==1&&n>0;
    }
    public  int hammingWeight(int n) {
    	int count=0;
    	int t=n;
    	while(t!=0){ 
    		t=t&(t-1);
    		count++;
    	}
		return count; 
    }
}

方案二:我們知道n&(n-1)可以消除掉最低位的1,那麼2的冪二進位制位中只有一個1,因此我們只需判斷n&(n-1)==0即可。

public class Solution {
    public boolean isPowerOfTwo(int n) {
        return (n&(n-1))==0&&n>0;
    }
}

方案三:我們知道正整數中最大的數是1<<30,我們直接判定1<<30能不能被n整除即可。

public class Solution {
    public boolean isPowerOfTwo(int n) {
        return n>0&&((1<<30)%n)==0;
    }
}

方案四:我們知道整數範圍內2的冪就那麼幾個,我們直接用switch語句進行比較就可以了。略。

方案五:將n不斷除以2,並且沒有餘數,即可判定其是否為2的冪。顯然除法效率沒有位移運算高效,我們可以利用右移代替除2。


分析

方案一:首先我們可以判定其是否為2的冪,然後再判定1的位置。

    public boolean isPowerOfFour(int num) {
        if(!isPowerOfTwo(num)){
        	return false;
        }
        int location=1,value=1,n=num;
    	while(location<=32){
    		if((n&value)!=0){
    			break;
    		}
    		location++;
    		value<<=1;
    	}
        return location%2==0;
    }
    public boolean isPowerOfTwo(int n) {
        return (n&(n-1))==0&&n>0;
    }

如果不用迴圈或者遞迴,我們該如何處理呢?

方案二:對於1在哪個位置上的判斷我們可以做如下優化。

public class Solution {
    public boolean isPowerOfFour(int num) {
        return num > 0 && (num&(num-1)) == 0 && (num & 0x55555555) != 0;
        //5的二進位制表示為0101,因此0x55555555覆蓋了所有我們期望1出現的位置
    }
}

方案三:對於區別4的冪和2的冪,還可以如下判斷。

public class Solution {
    public boolean isPowerOfFour(int num) {
        return num > 0 && (num&(num-1)) == 0 && (num-1)%3==0;
        //n-1,的低位是連續的1,1的個數為偶數,而3的二進位制位11,1111=1100+0011也必定是3的整數被,因此偶數個1就是3的整數倍。
    }
}

方案四:整數範圍內4的冪就那麼幾個,我們直接利用switch語句進行比較就可以了。


分析

方案一:整數中最大的3的冪為3^19(1162261467),如果3^19能被n整除,n就是3的冪。

public class Solution {
    public boolean isPowerOfThree(int n) {
        // 1162261467 is 3^19,  3^20 is bigger than int  
        return ( n>0 &&  1162261467%n==0);
    }
}

方案二:用switch語句。

方案三:可以不斷除3,以判斷餘數。


分析

對於a=0100 1100,b=0111 0100。

a^b= 0011 1000,代表各個位對應相加後該位的結果,不考慮進位。

a&b=0100 0100,表示各個位對應相加後的進位情況,我們需要將進位累加到高一位上去,(a&b)<<1=1000 1000。.

因此a+b=a^b+(a&b)<<1,1100 0000。加法的實質,也就是第一個運算元的特定位上需要進位,而第二個運算元指明是哪些位。

這樣遞迴處理,最終每個位都不需要進位的時候,就得到了我們計算的結果。

	public int getSum(int a, int b) {
		if(b == 0){//沒有進位的時候完成運算
	        return a;
	    }
	    int sum,carry;
	    sum = a^b;//第一步完成各個位上的加發運算
	    carry = (a&b)<<1;//第二步完成進位運算(左移運算)
	    return getSum(sum,carry);//
	}


分析

我們可以用等長的二進位制位000分別表示對應元素是否選取。000表示三個元素都不選取,結果為[],101表示選取第一個和第三個元素,結果為[1,3]。

我們將二進位制位從000一直加到111,就得到了所有的子集合。

public class Solution {
	int[] choose=null;
	private boolean isEnd(){//是否到結尾了,全選
		for(int i:choose){
			if(i==0) return false;
		}
		return true;
	}
	private void next(){//模擬二進位制加1
		int add=1;
		for(int i=0;i<choose.length;i++){
			if(add==0) return;
			int sum=add+choose[i];
			add=sum/2;
			choose[i]=sum%2;
		} 
	}
    public List<List<Integer>> subsets(int[] nums) {
    	List<List<Integer>> res=new ArrayList<List<Integer>>();
    	if(nums.length==0){
    		res.add(new ArrayList<Integer>());
    		return res;
    	} 
    	choose=new int[nums.length]; 
    	while(true){
    		List<Integer> r=new ArrayList<Integer>();
    		for(int i=0;i<choose.length;i++){
    			if(choose[i]==1){
    				r.add(nums[i]);
    			}
    		}
    		res.add(r);
    		if(isEnd()) break;
    		next();
    	}
		return res; 
    }
}

對於組合問題,我們還可以利用回溯或者分期攤還的方法進行處理,參考:

https://discuss.leetcode.com/topic/19110/c-recursive-iterative-bit-manipulation-solutions-with-explanations

http://blog.csdn.net/sunxianghuang/article/details/51887505


分析

如果a=b,那個a^b=0。因此,我們將所有元素進行異或就可以得到結果。

public class Solution {
    public int singleNumber(int[] nums) {
    	int res=0;
    	for(int i:nums){
    		res^=i;
    	}
    	return res;
    }
}
此外,我們還可以利用雜湊表來進行頻率統計,但是效率沒有位運算高。


分析

遍歷元素,我們可以分別統計每個二進位制位上出現1的次數。對於三個相同的元素,他們對特定位上1的個數的貢獻肯定是3的倍數,因此我們將所有二進位制位上1的次數%3,最後每個二進位制位上的1就是那個單獨元素貢獻的,因此我們就可以計算出最終的結果了。

public class Solution {
    public int singleNumber(int[] nums) {
    	int length = nums.length;
    	int result = 0;
    	for(int i = 0; i<32; i++){  
            int count = 0;//統計所有元素,該位上1出現的次數   
            int mask = 1<< i;  
            for(int j=0; j<length; j++){  
                if((nums[j]&mask)!=0)    
                    count++;  
            }  
          if(count%3!=0)  
                result |= mask;  
        }  
        return result; 
    }
}

擴充套件

問題轉換為:給定陣列中,只有一個元素出現 P 次,其餘元素都出現K 次,且p>=1&&p%k!=0,找出這個出現 P 次的唯一元素。

方法論

依照之前的思想,我們對每一個二進位制位統計出現1的次數,然後對k求餘。最終次數不為零的位,就是唯一元素二進位制為1的位。

網上還存在一種全部使用位操作實現的演算法,但很少給出具體解析,這裡進行一下整理。

對於二進位制位中1出現次數的統計,我們可以採用位操作實現,這樣更加高效。分析如下:

1、如何用二進位制來表示1出現的次數

對於一般元素,出現次數為 K ,要想表示整數K, 我們需要 m 個二進位制位來表示( 2^m >= k)。由於我們需要統計整數的32個位,因此我們需要32*m個二進位制位來統計1的出現次數。如下圖所示,例如用X1{5}X2{5}....Xm{5}(低位->高位)組成的二進位制數來表示第5個二進位制位上1出現的次數。其中,Xi(1<=i<=m)是一個32位整數,Xi{5}表示整數Xi二進位制表示中的第5個二進位制位。


2、如何用二進位制位來統計1的次數

每處理一個元素 E ,特定位上要麼是1要麼是0。對於第5個二進位制位,如果二進位制數X1{5}X2{5}....Xm{5}的最高位Xm{5}想要增加(必須通過低位進位來實現,也就是說低位必須全部都為1且需要加上1),其需要滿足的條件是:E 的第五個二進位制位為1,記做 E{5}=1,且X1{5}==1&&X2{5}==1&&....&& Xm-1{5}==1。同理,Xi{5}想要增加的條件是,X1{5}==1&&X2{5}==1&&....&& Xi-1{5}==1。

因此,對於二進位制數X1{5}X2{5}....Xm{5},Xi{5}增加的條件滿足時我們需要對其+1,條件不滿足時什麼都不做。

回想一下,如果第二個運算元為0則第一個運算元不會改變的運算有哪些?你會想到x=x|0和x=x^0。 

這裡我們利用異或運算(^),例如Xi{5}=Xi{5}^(X1{5}==1&&X2{5}==1&&....&& Xi-1{5}==1),如果條件不成立,Xi{5}保持不變,如果條件成立Xi{5}就是加1,原來是1就變成0,原來是0就變成1。這樣,我們就實現了利用二進位制進行1的次數統計。

3、統計次數的重置

例如,我們的為5,5需要3位二進位制表示,5的二進位制表示為"101",那麼我們的統計次數達到"101"之後應該重新置為零,否則,繼續往上統計達到“111”之後會產生溢位,統計就會出錯。也就是說我們需要找到一個特定的條件,當這個條件滿足時進行置零,條件不滿足時什麼都不會發生,這裡我們依舊使用與運算(&)和非運算(~)。例如二進位制數X1{5}X2{5}....Xm{5},代表第5個二進位制位上1的次數,假設K為5,那麼m=3,如果想要對其置零,需要滿足的條件就是X1{5}==1&&X2{5}==0&&X3{5}==1。

X1{5}X2{5}....Xm{5}=X1{5}X2{5}....Xm{5} &(~ (X1{5}==1&&X2{5}==0&&X3{5}==1) )。這樣我們實現了次數的重置。

分析到這裡,我們現在跳出對單個二進位制位的分析。而對整數中32個二進位制位統一考慮。

因此對於次數統計,Xm=Xm^(Xm-1&..&X2&X1),這裡利用與運算(&)代替條件判定。

對於次數的重置,Xm=Xm&condition,condition滿足這樣的性質:當且僅當1的次數統計為K時其結果為0,其餘情況都為1。

condition= ~(x1' & x2' & ... xm'),where xj' = xj if kj = 1 and xj' = ~xj if kj = 0 (j = 1 to m),其中kj 就是K的二進位制表示中第j個二進位制位的值,例如K為5("101"),那麼condition=~(X1&~X2&X3)。

對於此題,K=3(二進位制表示"11")、P=1,因此m=2,condition=~(X1&X2)。得到如下演算法:

public class Solution {
    public int singleNumber(int[] nums) {
        int x1 = 0;   
        int x2 = 0; 
        int condition = 0;
        for (int i : nums) {
            x2 ^= x1 & i;
            x1 ^= i;
            condition = ~(x1 & x2);
            x2 &= condition;
            x1 &= condition;
         }
        return x1; 
    }
}
這裡的結果直接就是x1,因為p=1,二進位制表示位“1”,x1中每個二進位制位上的值就是最終結果對應二進位制位上的值。如果p=2呢,2的二進位制表示位“10”,因此x2中每個二進位制位上的值就是最終結果對應二進位制位上的值,而x1最終為零。

如果K=9、P=7,那麼m=4,7的二進位制位“0111”(高位到低位),因此x1、x2、x3中二進位制位上的值都與結果對應二進位制位上的值相同,因此都能作為最終結果,只有x4最終為零。

參考

https://discuss.leetcode.com/topic/11877/detailed-explanation-and-generalization-of-the-bitwise-operation-method-for-single-numbers


分析

將所有元素異或後的結果即為5^3,5和3的二進位制表示分別為0101和0011,異或的結果為0110,我們根據元素的二進位制表示中從低位開始第二個(2^1)二進位制位是否為1將陣列劃分為兩部分[1,1,3],[2,3,2],這樣問題就退化為問題137啦。

public class Solution { 
    public int[] singleNumber(int[] nums) {
        int[] res=new int[2];
        int two=singleNumberSimple(nums);
        int location=0; 
        while(location<=31){
        	if(((1<<location)&two)!=0){
        		break;
        	}
        	location++;
        } 
        ArrayList<Integer> first=new ArrayList<Integer>();
        ArrayList<Integer> second=new ArrayList<Integer>();
        for(int i:nums){
        	if(((1<<location)&i)==0){
        		first.add(i);
        	}else{
        		second.add(i);
        	}
        }
        
        res[0]=singleNumberSimple(first);
        res[1]=singleNumberSimple(second);
        return res;
    }
    public int singleNumberSimple(ArrayList<Integer> nums) {
    	int res=0;
    	for(int i:nums){
    		res^=i;
    	}
    	return res;
    }
    public int singleNumberSimple(int[] nums) {
    	int res=0;
    	for(int i:nums){
    		res^=i;
    	}
    	return res;
    }
}


分析

對於長度為n的陣列,缺失數的範圍為0~n。缺失,也就是說我們需要進行存在性判斷,因此我們可以使用雜湊表,有因為需要判定的資料在限定範圍內,因此我們可以使用陣列來實現雜湊表,此外也可以使用HashMap記錄0~n-1的存在性。

public class Solution {
    public int missingNumber(int[] nums) {
    	int n=nums.length;
    	if(n==0) return 0;
    	int[] mark=new int[n];
    	for(int i:nums){
    		if(i>=0&&i<n){
    			mark[i]=1;
    		}
    	}
    	for(int i=0;i<n;i++){
    		if(mark[i]==0){
    			return i;
    		}
    	}
		return nums.length;
        
    }
}
此種解法的空間複雜度為O(n),時間複雜度為O(n)。那麼如何將空間複雜度降低為O(1)呢?

我們可以利用陣列本身來做存在性判定,如果存在值 i 我們保證a[i]存放的就是值 i ,然後依次檢查a[i]上存放的是不是 i 即可。

public class Solution {
    public int missingNumber(int[] nums) {
    	int n=nums.length;
    	if(n==0) return 0;
    	for(int i=0;i<n;i++){ 
    		//將a[i]放到到a[a[i]]處,其中0<=a[i]<n,且a[a[i]]處不是存放的a[i]
    		while(0<=nums[i]&&nums[i]<n&&nums[nums[i]]!=nums[i]){
    			int change=nums[i],t;
    			t=nums[i];nums[i]=nums[change];nums[change]=t;
    		}
    	}
    	for(int i=0;i<n;i++){
    		if(nums[i]!=i){
    			return i;
    		}
    	}
		return nums.length;
        
    }
}

分析

對於單詞字串的字元出現情況我們可以使用一個雜湊表來記錄,由於範圍限定在‘a’~'z',我們可以直接使用26位的陣列,由於只需標記字元是否出現(狀態只有0和1),因此我們只需要用一個位來表示該字元是否存在,因此我們只需要一個32位的整型變數,其32個二進位制位就可以標記字元出現情況,並且我們在匹配兩個字串出現字元是否有重複時,我們只需要使用位與(&)即可。

    public int maxProduct(String[] words) {
    	int res=0,n=words.length;
    	if(n==0) return res;
    	int[] mark=new int[n];
    	for(int i=0;i<n;i++){
    		for(int j=0;j<words[i].length();j++){
    			mark[i]|=(1<<(words[i].charAt(j)-'a'));
    		}
    	}
    	for(int i=0;i<n;i++){
    		for(int j=i+1;j<n;j++){
    			if((mark[i]&mark[j])==0){
    				res=Math.max(res, words[i].length()*words[j].length());
    			} 
    		}
    	}
		return res; 
    }


分析

n的二進位制表示中1個數等於n>>1的二進位制表示中1的個數+n的二進位制表示中最低位1的個數(n&1)。因此,我們就避免了對每一個正數進行計算二進位制中1的個數。

public class Solution {
    public int[] countBits(int num) {
    	int[] res=new int[num+1];
    	for(int i=1;i<=num;i++){
    		res[i]+=(res[i>>1]+(i&1));
    	}
    	return res; 
    }
}

變形

如果我們不是計算每個整數的二進位制表示中1的個數。而是統計0至n所有整數的二進位制表示中1的個數呢?或者更精確的講,統計

每個二進位制位上1的出現次數呢?

對於所有整數二進位制表示中1的個數,顯然可以按照上面的方法,先依次計算再進行求和。時間複雜度和空間複雜度都是O(n)。

而對於每個二進位制位上1的出現次數,我們可以迭代所有整數,利用位與(&)判定各個位上的值,並進行統計,時間複雜度O(n)。

接下來介紹一種更加高效的演算法,時間複雜度O(1)。

首先我們觀察0000->1111的變化情況,低位到高位

0000

1000

0100

1100

0010

1010

0110

1110

0001

1001

0101

1101

0011

1011

0111

1111

發現第一個二進位制位變化週期為1,第二個二進位制位變化週期為2,第三個二進位制位變化週期為4,第四個二進位制位變化週期為8。

因為0~1的變化是呈現週期性的,故0000->1111,各個位上出現1的情況都是一半。總共2^4=16個數,每個位1出現次數為(2^4)/2。

現在我們來分析更一般的情況:0000 0000->0010 0101。我們將這樣的變化過程進行分解為:

第一步:0000 0000->1111 1110

第二步:0000 0001->1111 1001

 第三步:0000 0101->1100 0101

第四步:0010 0101

第一步:0000 0000->1111 1110總共2^7個數,前7個二進位制位,每個二進位制位上1的個數(2^7)/2。

第二步:0000 0001->1111 1001總共2^5個數,前5個二進位制位,每個二進位制位上1的個數(2^5)/2,第8個二進位制位永遠是1。

第三步:0000 0101->1100 0101總共2^2個數,前2個二進位制位,每個二進位制位上1的個數(2^2)/2,第6、8個二進位制位永遠是1。

第四步:1個數,第3、6、8個二進位制位是1。

因此我們不難得出如下演算法:

    public int[] countBits(int num) {
    	int[] res=new int[32]; 
    	ArrayList<Integer> indexs=new ArrayList<Integer>();//1出現的位置
    	int location=30;//因為32位的int,正數高位為0
    	while(location>=0){
    		if((num&(1<<location))!=0){
    			int sum=1<<location;
    			for(int i:indexs){//高位1出現的次數增加
    				res[i]+=sum;
    			}
    			for(int i=0;i<location;i++){//低位1出現的次數增加
    				res[i]+=sum>>1;
    			}
    			indexs.add(location);
    		}
    		location--;
    	} 
    	for(int i:indexs){
    		res[i]+=1;
    	}
    	return res; 
    }

擴充套件

將問題擴充套件到十進位制中。對於0~n的整數,分別統計0~9出現的次數?分別統計每個位上0~9出現的次數呢?

相關推薦

操作演算法試題

方案一 我們可以利用為位與操作,依次判定各個位上是否為1。 public int hammingWeight(int n) {//依次檢測各個位 int count=0; int location=1,value=1; while(

操作系統試題

分配 共享 chan 全部 有一點 art 計算機系 面試 資源分配 https://blog.csdn.net/youngchang06hpu/article/details/8009947 一、進程和線程之間的區別 進程是系統進行資源分配和調度的一個獨立單位。線程是進程

前端常見演算法試題之 - 從尾到頭列印連結串列[JavaScript解法]

前端常見演算法面試題之 - 從尾到頭列印連結串列[JavaScript解法] 題目描述 實現思路 程式碼實現 題目描述 輸入一個連結串列的頭結點,從尾到頭反過來打印出每個結點的值 實現思路 前端工程師看到這個題目,直接想到的就是

前端常見演算法試題之 - 二維陣列中的查詢[JavaScript解法]

前端常見演算法面試題之 - 二維陣列中的查詢[JavaScript解法] 題目描述 輸入輸出分析 實現思路 程式碼實現 題目描述 在一個二維陣列中,每一行都按照從左到右遞增的順序排序,每一列都按照從上到下遞增的順序排序。請完成一個

常見的js演算法試題收集,es6實現

常見的js演算法面試題收集,es6實現 1、js 統計一個字串出現頻率最高的字母/數字 let str = 'asdfghjklaqwertyuiopiaia'; const strChar = str => { let string = [...str], m

試題演算法試題(一)

問題: 1. 輸入一個個數較大的正整數陣列[數字範圍在0~9999],將部分陣列元素的十進位制表示連線起來排成一個數,輸出能排出的所有數字中最大的一個。find例如輸入陣列{3423,33,456,9,8,7,21}和3,則輸出的最大數為:456342333。 要求: (1) 輸出數字用

海量資料處理:十道試題與十個海量資料處理方法總結(大資料演算法試題

第一部分、十道海量資料處理面試題 1、海量日誌資料,提取出某日訪問百度次數最多的那個IP。       首先是這一天,並且是訪問百度的日誌中的IP取出來,逐個寫入到一個大檔案中。注意到IP是32位的,最多有個2^32個IP。同樣可以採用對映的方法

資料結構與演算法試題

由於這些題,實在太火了。所以,應廣大網友建議要求,在此把之前已整理公佈的前80題, 現在,一次性分享出來。此也算是前80題第一次集體亮相。 此些題,已有上萬人,看到或見識到,若私自據為己有,必定為有知之人識破,付出代價。 所以,作者宣告: 本人

java 演算法題 - 面試中常見的操作演算法

前言 上一篇部落格 聊一聊 Android 中巧妙的位操作 中,我們講解了 java 中常用的位運算及常用應用場景,今天,讓我們一起來看一下,面試中常見的位操作的演算法題。 兩個只出現一次的數字 【題目描述】一個整型數組裡除了兩個數字之外,其他的數字都出現了兩次。請寫程

演算法-試題2萬多名員工按年齡排序

2萬多名員工按年齡排序演算法, 演算法思想是選擇排序演算法的思想,同時考慮到2萬多員工,在某個年齡取值時會有多位員工的實際情況。 思路 第一遍搜尋找出,最小年齡為m個minAge, 最大年齡為n個

PHP版今日頭條演算法試題(持續更新)

1,現在有一個字串,你要對這個字串進行 n 次操作,每次操作給出兩個數字:(p, l) 表示當前字串中從下標為 p 的字元開始的長度為 l 的一個子串。你要將這個子串左右翻轉後插在這個子串原來位置的正後方,求最後得到的字串是什麼。字串的下標是從 0 開始的,你可以從樣例中得

n對括號可以有多少種匹配排列方式(演算法試題)

問題:n對括號可以有多少種匹配排列方式?比如兩對括號可以有兩種:()()和(()) 思路:問題可轉化為:在一個string中包含兩個字元:'('和')',他們出現的次數都為n,並且任何時候'('出現的次數總是>=')'出現的次數。 解決方案(遞迴): 標誌:l: 左括

九章演算法試題77 插入區間

原網址: 問題詳情  給出一個無重疊的按照區間起始端點排序的區間列表。 在列表中插入一個新的區間,你要確保列表中的區間仍然有序且不重疊(如果有必要的話,可以合併區間)。線上評測本題: http://www.lintcode.com/zh-cn/problem/insert

PHP演算法試題

氣泡排序演算法基本思想: 對需要排序的陣列從後往前(逆序)進行多遍的掃描,當發現相鄰的兩個數值的次序與排序要求的規則不一致時,就將這兩個數值進行交換。這樣比較小(大)的數值就將逐漸從後面向前面移動。 <?php function mysort($arr)

九章演算法試題1 落單的數

九章演算法官網-原文網址: 題目 初階:有2n+1個數,其中2n個數兩兩成對,1個數落單,找出這個數。要求O(n)的時間複雜度,O(1)的空間複雜度。 進階:如果有2n+2個數,其中有2個數落單,該怎麼辦? 線上測試本題 解答 初階:將2n+1個數異或起來,相同的數會

九章演算法試題61 克隆圖

九章演算法官網-原文網址 題目 給出一個圖,並且給出圖的起始節點,知道這個圖的節點的定義,要求克隆這個圖,返回克隆圖的起始節點。 線上測試本題 解答 這一類克隆一個圖的題目,我們都可以分為兩步。 第一步: 克隆點。 只需要用廣度優先搜尋的方法從根節點出發去遍歷一遍圖,

Java演算法試題(008) 字串反轉

簡介 這是一個Java面試中被經常問及的問題,也有很多種實現方式。我在這裡一一列出,如果你有更好的解法,也可留言。 解法一:首尾字元調換順序 由於String物件是隻讀型別,不能對其進行直接操作,因此需要轉換成字元陣列,然後調換字元陣列中的各個字元。 public St

九章演算法——試題思路

面試題1 落單的數 題目描述: 有2n+1個數,其中2n個數兩兩成對,1個數落單,找出這個數。要求O(n)的時間複雜度,O(1)的空間複雜度。進階問題:如果有2n+2個數,其中有2個數落單,該怎麼辦? 答: 初階:將2n+1個數異或起來,相同的數會抵消,異或的答

九章演算法試題80 接雨水

原文網址: 問題詳情  給出 n 個非負整數,代表一張X軸上每個區域寬度為 1 的海拔圖, 計算這個海拔圖最多能接住多少(面積)雨水線上評測本題: http://www.lintcode.com/zh-cn/problem/trapping-rain-water/解答 

九章演算法試題32 小球排序

九章演算法官網-原文網址 題目 有紅黃藍三色的小球若干排成一列,這些小球進行排序,請使用盡量少的空間和時間。解答 假設順序為紅色黃色藍色。用兩根指標從頭開始遍歷,第一根指標遇到非紅色時停下,如果第二根指標找到第一根指標之後的第一個紅色停下,交換兩根指標所指顏色。重複上述過