By using these methods, we can make some algorithms questions become easier.

1, with n & (n - 1) n the last one eliminated 1

In the binary representation of n, if we perform n

n = n & (n - 1)

N then the left and right side can be eliminated 1, e.g.

n = 1001n - 1 = 1000n = n & (n - 1) = (1001) & (1000) = 1000

This formula What use is it?

In fact, there are still a lot of use, but also when doing the question will often encounter, I have listed below a few classics, often test the example.

(1), it is determined whether n is a positive integer power of two

If a number is a power of 2, n means the binary representation, only one bit is 1, the other is 0. I, for example, such as

2^0 = 0…..0001

2^1 = 0…..0010

2^2 = 0….0100

2^3 = 0..01000

…..

So, we just need to determine N in binary notation if there is only a 1 on it. According to the usual practice, we may have to shift n, then n judge binary representation of the number of 1. So practice is as follows

    boolean judege(int n) {        int count = 0;        int k = 1;        while (k != 0) {            if ((n & k) != 0) {                count++;            }            k = k << 1;        }        return count == 1;    }

However, if n & (n - 1), then, a direct elimination of 1 n, and n is 0 can be determined, as follows:

boolean judege(int n){     return n & (n - 1) == 0;// }

And time complexity of this method I O (1).

(2), the integer n is the number of binary 1

For this problem, we can continue with the implementation of n & (n - 1), Each time you can eliminate a 1, when n is 0, the calculation can be performed a total of how many times, as follows:

    public int NumberOf12(int n) {        int count = 0;        int k = 1;        while (n != 0) {            count++;            n = (n - 1) & n;        }        return count;

(3), will be converted to an integer n m, how many bits need to be changed?

In fact, this question and (2) is almost the same as that question, we only need to calculate two numbers n and m are the number of bits is not the same on it, then we can be different or let n and m, then the results obtained in the calculation of XOR how many 1 on it. E.g

令 t = n & m

T is then calculated the number of bits in a can, the problem can be converted to that question (2) of the.

2, the application of double pointer

In previous articles, I have talked about a double pointer, here I am talking about, the way to add some examples.

(1) the application, in the list of

For two-pointer, I think it's the most for is in the list here, such as "judge singly linked list if there ring", "how a traverse to find the list middle position node", "single list in the penultimate k nodes", etc. problem. For this problem, we can use the two-pointer, and a lot easier. I by the way these three questions how to solve it with a two-pointer.

For example, the first question

We can set a slower pointer and a quick pointer to traverse the list. Slow moving a pointer to a node, and a pointer to the fast movement of the two nodes, if the ring is not linked list, the pointer to the fast been traversed the table, if the ring, the fast and slow cursor pointer will encounter when traversing the second .

For the second question

It is set as a pointer to the fast and slow pointer. A first mobile node slow, and fast two. When traversing the list, when the fast pointer traversal is complete, slow pointer just reached the midpoint.

For the third issue

设置两个指针,其中一个指针先移动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/