これらのメソッドを使用することにより、我々は質問が容易になるいくつかのアルゴリズムを作ることができます。
1、N・(N - 1)のn最後の1を除去
n個のバイナリ表現で、我々は、nを実行した場合
N = N・(N - 1)
Nは、左及び右側は、例えば、1を除去することができます
N = 1001n - 1 = 1000N = N・(N - 1)=(1001)および(1000)= 1000
この式には何を使用ですか?
多くの場合の例をテストし、実際には、まだ利用が多いが、しばしば遭遇する質問をするときも、私はいくつかの古典の下にリストされています。
(1)、nが2の正の整数乗であるか否かを判断します
数が2のべき乗であり、nはバイナリ表現を意味する場合、1ビットのみを1、他は0です。I、例えば、など
2 ^ 0 = 0 ... ..0001
2 ^ 1 = 0 ... ..0010
2 ^ 2 = 0 ... 0.0100
2 ^ 3 = 0..01000
... ..
そこで、我々はただそれだけで1がある場合は2進数でNを決定する必要があります。通常の実施によると、私たちはその後、n個のシフトnは1の数のバイナリ表現を判断する必要があります。以下のように練習があります
ブールjudege(int型N){int型のカウント= 0; INTのK = 1。一方、(!K = 0){(!(N&K)= 0)場合、{++数えます。} K = K << 1。}カウント== 1を返します。}
しかしながら、(N - 1)nはIFと、その後、直接、1Nの除去、及びnは次のように0を決定することができるです。
ブールjudege(int型N){リターンN・(N - 1)== 0; //}
そして、この方法の時間計算量I O(1)。
(2)、整数nは、2進数の1の数であります
この問題のために、我々は&(N - 1)n個の実装を続行することができ、nが0であるとき、それぞれの時間はあなたが1を排除することができ、計算は次のように何回、の合計を実行することができます。
公共int型NumberOf12(int型N){int型のカウント= 0; INTのK = 1。ながら(!N = 0){++数えます。N =(N - 1)&N。}カウントを返します。
(3)、整数n mに変換され、どのように多くのビットを変更する必要が?
実際には、この質問及び(2)は、ほぼその質問と同じで、我々は2つだけの数を計算する必要があり、n及びmビットの数がそれに同じではないが、我々は異なっていても又はさせることができ、nおよびm、次いで結果は、その上にXORどのように多くの1の計算で得られました。例えば
令T = N&M
Tは、その後、缶内のビットの数を計算し、問題は、その問題(2)に変換することができます。
2、ダブルポインタの応用
以前の記事では、私がここで私はいくつかの例を追加する方法について話しています、ダブルポインタについて話しています。
(1)のリストでアプリケーションを、
2ポインタの場合、私はそれがためにほとんどが中にあると思うリスト「リングがあった場合、裁判官単独リンクリスト」のような、ここでは、「どのようにリストの中間位置のノードを見つけるためにトラバース」など「最後から二番目のk個のノード内の単一のリスト」、問題。この問題については、我々は2-ポインタを使用し、非常に簡単にできます。I 2ポインタでそれを解決するためにどのようにこれらの3つの質問の方法によって。
たとえば、最初の質問
私たちは遅くポインタと、リストをトラバースするための簡単なポインタを設定することができます。第二のトラバースするときリングがリストにリンクされていない場合、ノードへのポインタ、2つのノードの速い動きにポインタを移動遅く、高速トラバースされてテーブルへのポインタは、リングと、高速および低速カーソルポインタが遭遇します。
2番目の質問については
これは、高速と低速のポインタへのポインタとして設定されています。第一のモバイルノードが遅い、及び2つの高速。リストを横断するとき、高速ポインタトラバーサルが完了したときに、遅いポインタがちょうど中間点に達しました。
第三の問題については
设置两个指针,其中一个指针先移动k个节点。之后两个指针以相同速度移动。当那个先移动的指针遍历完成的时候,第二个指针正好处于倒数第k个节点。
有人可能会说,采用双指针时间复杂度还是一样的啊。是的,空间复杂度和时间复杂度都不会变,但是,我觉得采用双指针,更加容易理解,并且不容易出错。
(2)、遍历数组的应用
采用头尾指针,来遍历数组,也是非常有用的,特别是在做题的时候,例如我举个例子:
题目描述:给定一个有序整数数组和一个目标值,找出数组中和为目标值的两个数。你可以假设每个输入只对应一种答案,且同样的元素不能被重复利用。
示例:给定 nums = [2, 7, 11, 15], target = 9因为 nums[0] + nums[1] = 2 + 7 = 9所以返回 [0, 1]
其实这道题也是 leetcode 中的两数之和,只是我这里进行了一下改版。对于这道题,一种做法是这样:
从左到右遍历数组,在遍历的过程中,取一个元素 a,然后让 sum 减去 a,这样可以得到 b,即 b = sum - a。然后由于数组是有序的,我们再利用二分查找,在数组中查询 b 的下标。
在这个过程中,二分查找的时间复杂度是 O(logn),从左到右扫描遍历是 O(n),所以这种方法的时间复杂度是 O(nlogn)。
不过我们采用双指针的方法,从数组的头尾两边向中间夹击的方法来做的话,时间复杂度仅需为 O(n),而且代码也会更加简洁,这里我给出代码吧,代码如下:
public int[] twoSum1(int[] nums, int target) { int[] res = new int[2]; int start = 0; int end = nums.length - 1; while(end > start){ if(nums[start] + nums[end] > target){ end--; }else if(nums[start] + nums[end] < target){ start ++; }else{ res[0] = start; res[1] = end; return res; } } return res;}
这个例子相对比较简单,不过这个头尾双指针的方法,真的用的挺多的。
3、a ^ b ^ b = a 的应用
两个相同的数异或之后的结果是 0,而任意数和 0 进行异或的结果是它本身,利用这个特性,也是可以解决挺多题,我在 leetcode 碰到过好几道,这里我举一些例子。
(1)数组中,只有一个数出现一次,剩下都出现两次,找出出现一次的数
这道题可能很多人会用一个哈希表来存储,每次存储的时候,记录 某个数出现的次数,最后再遍历哈希表,看看哪个数只出现了一次。这种方法的时间复杂度为 O(n),空间复杂度也为 O(n)了。
我们刚才说过,两个相同的数异或的结果是 0,一个数和 0 异或的结果是它本身,所以我们把这一组整型全部异或一下,例如这组数据是:1, 2, 3, 4, 5, 1, 2, 3, 4。其中 5 只出现了一次,其他都出现了两次,把他们全部异或一下,结果如下:
由于异或支持交换律和结合律,所以:
1^2^3^4^5^1^2^3^4 = (1^1)^(2^2)^(3^3)^(4^4)^5= 0^0^0^0^5 = 5。
通过这种方法,可以把空间复杂度降低到 O(1),而时间复杂度不变,相应的黛米如下
int find(int[] arr){ int tmp = arr[0]; for(int i = 1;i < arr.length; i++){ tmp = tmp ^ arr[i]; } return tmp;}
总结
这阵子由于自己也忙着复习,所以并没有找太多的例子,上面的那些题,有些在之前的文章也是有写过,这里可以给那些看过的忘了的复习一些,并且也考虑到可能还有一大部分人没看过。
品略图书馆 http://www.pinlue.com/
转载于:https://blog.51cto.com/14325182/2410147