算法备忘

箴言:

      The key is to implement the solution cleanly without dealing with each edge case separately. (关键是要清晰的实现,而不是针对每种边界情况都特殊处理)

 

写了这么多程序,总结出以下容易出错的地方

  1. 溢出,不管在程序的哪个部分,都要考虑所做的运算,是否会导致溢出
  2. 空数组,空链表,空对象,传到函数里的任何参数都有为空的可能,一般都会导致程序bug,需要特别注意是否特殊处理为空的情况
  3. 传进来的参数是否合法
  4. 有时候一些题目会让我们做一些变换,需要特别注意变换后的对象是否合法

 

1.二分搜索(迭代),要根据左右边界(left, right),确定中间位置(middle),每一轮迭代都要更新左右边界,在更新的时候要用到middle,这时候要注意更新方法!

我们要知道,middle的时候是靠左的。这是因为,如果数组元素个数为奇数,那么middle=(left+right)/2肯定能整除,middle位于中间位置,两边数量相等。

但如果数组元素个数为偶数,那么middle=(left+right)/2不能整除,根据Java的除法,middle会偏左。比如,4个元素的数组middle=(0+3)/2=1,就位于第二个位置上

要确保在更新的时候不能漏掉元素,并且要可以跳出循环(跳出循环的条件是left < right,非常重要)。

要更新left,让left=mid + 1;(更新后,可以确保left <= right)

要更新right,让right = mid; (更新后,可以确保left <= right)

这样子,在跳出循环的时候,肯定left==right,返回left或right都行

下面给一个例子,LeetCode上一个题目,Find Peak Element,也就是找到数组中峰值所在位置,如果有多个峰值的话,返回任意一个的位置就ok

代码如下,仔细体会边界更新的写法

public int findPeakElement(int[] num) {
        int left = 0;
        int right = num.length - 1;
        while (left < right) {
            int mid = (left + right) / 2;
            if (num[mid] < num[mid+1]) {
                left = mid + 1;
            } else {
                right = mid;
            }
        }
        return left;
    }

  

 根据上面的思路写出的二分搜索如下:

public int binarySearch(int[] num, int target) {
        if (num.length == 0) {
            return -1;
        }
        int left = 0, right = num.length-1;
        while (left < right) {
            int mid = (left + right) / 2;
            if (num[mid] == target) {
                return mid;
            } else if (num[mid] < target){
                left = mid + 1;
            } else {
                right = mid;
            }
        }
        if (num[left] == target) {
            return left;
        }
        return -1;
    }

  

2.将二维数组的位置信息转化为一维存储!

有时候需要存储二维数组的坐标信息,一般来说可以定义一个Point对象存储坐标,还有一种方法,将二维数组的X,Y转化为一个数字,并且可以还原回去

比如char型二维数组m*n(m行,n列),如果一个坐标为(x,y),那么可以做变换为k = x*n+y,只存储k即可,想要还原时,可以做变换x = k / n; y = k % n;

这样子就可以解决二维存储困难的问题,但代价就是需要额外的计算,这也是用时间换空间的例子

第一种用Point的方案需要额外的对象存储,第二种需要额外的计算

 

下面以char型数组m[3][4]为例,定义数组行数为int rows = m.length = 3;数组列数为int colums = m[0].length = 4;

如果要记录一个位置(2,1),就可以存储2*4+1=9,这是按行来计算的,每行4个位置,那么这个(2,1)就代表第九个位置(从0开始)

如果按列来计算,可以存储2+1*3=5,在还原的时候,用x = k % m; y = k / m;

 

3.有些二维数组的不同信息比较少,比如存的都是0或1,在操作这个二维数组的时候,需要标记某些位置不处理,那么可以直接修改这个数组,待操作完毕后再恢复这些位置原来的信息即可。

具体可以参考Leetcode上Surrounded Regions这道题,可以直接在二维数组中标记哪些为'O'的位置不需要处理,比如把这些位置的'O',修改为除了'X',‘O'之外的'P',那么在修改完二维数组之后,再遍历一遍,把值为'P'的位置恢复为'O'

 

4.今天在做回文判断(不允许使用额外的空间)这道题目的时候,又产生了思维缺陷。想用异或的方法来判断,思路比较简单,当数字有偶数位的时候,如果是回文数,那么对称的位置疑惑为0,最后的异或结果也是0,但如果是奇数位,那么异或的结果是中间那个数字的(比如12321,异或结果就是3)。这里有一个重大缺陷,如果相等的两个数不在对称的位置,那么异或的结果还是0(比如111222),也就是说异或的结果与数字的位置是没有关系的,那么显然就不能用来判断数字是否回文了!!!

posted @ 2015-03-07 17:20  Truezion  阅读(143)  评论(0)    收藏  举报