剑指offer(一)

最近在看《剑指offer》,写几篇文章来记录一下,今天有以下几道算法题:

  • 斐波那契数列
  • 二进制中1的个数
  • 数组中重复的数字
  • 二维数组中的查找
  • 替换空格

斐波那契数列

写一个函数,输入n,求斐波那契数列的第n项。

递归解法重复计算太多,效率太低,这里我们使用时间复杂度为O(n)的方法。

解题思路

从下往上计算,首先根据f(0)和f(1)算出f(2),再根据f(1)和f(2)算出f(3)…….以此类推就可以算出第n项。

代码实现

public class Fibonacci {

    public static long fibonacci(long n){
        
        if (n<0)  throw new RuntimeException("无效的输入");
        if (n==0) return 0;
        if (n==1) return 1;

        long fibOne=0;
        long fibTwo=1;
        long fibSum=0;

        for (int i = 2; i <= n; i++) {            
            fibSum=fibOne+fibTwo;
            fibOne=fibTwo;
            fibTwo=fibSum;
        }
        return fibSum;
    }
}

二进制中1的个数

请实现一个函数,输入一个整数,输出该数二进制表示中1的个数。

例如,把9表示成二进制就是1001,则输出2.

代码实现

解法一(可能引起死循环)

如果一个整数与1做 &(与)运算的结果是1,则表示该整数最右边一位是1,否则是0

public static int Solution1(int n) {

    int count = 0;
    //JAVA语言规范中,int整形占四个字节,总计32位
    //对每一个位置与1进行求与操作,再累加就可以求出当前数字的表示是多少位1
    for (int i = 0; i < 32; i++) {
        if ((n & 1) == 1) {
            count++;
        }
        n = n >> 1;
    }
    return count;
}

解法二(常规解法)

首先把n和1做与运算,判断n的最低位是不是1。接着把1左移一位得到2,再和n做&运算,就能判断n的次低位是不是1,这样反复左移,每次都能判断n的其中一位是不是1

public static int Solution2(int n) {

    int count = 0;
    int k = 1;
    for (int i = 0; i < 32; i++) {
        if ((n & k) == k) {
            count++;
        }
        k = k << 1;
    }
    return count;
}

解法3(大神解法)

把一个整数减去1,再和原整数做与运算,会把该整数最右边的1变为0
该方法整数中有几个1就只需要循环几次

public static int Solution3(int n) {

    int count = 0;
    
    //数字的二进制表示中有多少个1就进行多少次操作
    while (n != 0) {
        count++;
      //从最右边的1开始,每一次操作都使n的最右的一个1变成了0
        n = n & (n - 1);
    }
    return count;
}

数组中重复的数字

在一个长度为n的数组里的所有数字都在0到n-1的范围。数组中某些数字是重复的,但不知道有几个数字重复,也不知道每个数字重复的次数。 请找出数组中任意一个重复的数字。例如如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是重复的数字2或者3。

解题思路

我们注意到数组中的数字都在0n-1的范围内。如果这个数组中没有重复的数字,那么当数组排序后数字i将出现在下标为i的位置。

步骤

从头到尾扫描这个数组中的每个数字。当扫描到下标为i的数字的时候,首先比较这个数字(用m表示)是不是i。 如果是,接着扫描下一个数字。如果不是,再拿它和第m个数字进行比较。 如果它和第m个数字相等,就找到一个重复的数字(该数字在下标为i和m的位置都出现了)。 如果它和第m个数字不想等,就把第i个数字和第m个数字交换,把m放到属于它的位置。 接下来再重复这个比较,交换的过程,直到发现一个重复的数字。

以数组{2,3,1,0,2,5,3}为例来分析找到重复数字的步骤。数组的第0个数字(从0开始计数,和数组的下标保持一致)是2, 与它的下标不相等,于是把它和下标为2的数字1交换,交换后的数组是{1,3,2,0,2,5,3}。 此时第0 个数字是1,仍然与它的下标不相等,继续把它和下标为1的数字3交换,得到数组{0,1,2,3,2,5,3}。 此时第0 个数字为0,接着扫描下一个数字,在接下来的几个数字中,下标为1,2,3的三个数字分别为1,2,3, 他们的下标和数值都分别相等,因此不需要做任何操作。 接下来扫描下标为4的数字2.由于它的值与它的下标不相等,再比较它和下标为2的数字。 注意到此时数组中下标为2的数字也是2,也就是数字2和下标为2和下标4的两个位置都出现了,因此找到一个重复的数字。

代码实现

public class Test {

    public static int duplication;//用来保存重复数字
    
    public static boolean duplicate(int[] arr) {
        //判断输入是否合法
        if (arr == null || arr.length == 0) {
            return false;
        }
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] < 0 || arr[i] >= arr.length) {
                return false;
            }
        }
        //核心代码
        for (int i = 0; i < arr.length; i++) {
            while (arr[i] != i) {
                //如果两个数相等则返回true
                if (arr[i] == arr[arr[i]]) {
                    duplication = arr[i];
                    System.out.println(arr[i]);
                    return true;
                }
                //否则交换数字
                int temp = arr[i];
                arr[i] = arr[temp];
                arr[temp] = temp;
            }
        }
        return false;
    }
}

二维数组的查找

在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。 请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

解题思路

(1)首先选取数组中右上角的数字。如果该数字等于要查找的数字,则结束查找过程。
(2)如果该数字大于要查找的数字,则剔除这个数字所在的列。
(3)如果该数字小于要查找的数字,则剔除这个数字所在的行。

代码实现

public class Test {

    public static boolean find(int[][] matrix, int number) {
 
        if (matrix == null || matrix.length < 1 || matrix[0].length < 1) {
            return false;
        }
 
        int rows = matrix.length; // 数组的行数
        int cols = matrix[1].length; // 数组行的列数
 
        int row = 0; // 起始开始的行号
        int col = cols - 1; // 起始开始的列号
 
        while (row >= 0 && row < rows && col >= 0 && col < cols) {           
            if (matrix[row][col] == number) { // 如果找到了就直接退出
                return true;
            } else if (matrix[row][col] > number) {
                col--; // 列数减一,代表向左移动
            } else { // 
                row++; // 行数加一,代表向下移动
            }
        } 
        return false;
    } 
}
  

替换空格

请实现一个函数,把字符串中的每个空格替换成”%20″,例如“We are happy”,则输出“We%20are%20happy”。

很容易想到可以直接遍历字符串替换空格,但是这样的话需要重复移动多个元素,时间复杂度为O(n^2),我们来看下只需要移动一次的方法。

解题思路

首先遍历字符串,统计出空格数,计算出替换后的字符串的总长度。 每替换一个空格,长度增加2,因此替换后的字符串长度等于原来的长度加上2*空格数。 我们从字符串后面开始复制和替换。准备两个指针P1和P2,P1指向原始字符串末尾,P2指向替换之后的字符串的末尾。 接下来我们向前移动指针P1,逐个把它指向的字符复制到P2指向的位置,当碰到空格后,把P1向前移动1格, 同时把P2向前移动3格。当P1和P2指向同一位置时,表明所有空格已替换完毕。

代码实现

public class Test {
        
    public static void replaceBlank(char[] c) {
        // 判断输入是否合法
        if (c == null || c.length <= 0) {
            return;
        } 
        
        // 统计字符数组中的空白字符数
        int numberOfBlank = 0;
        //统计c实际的字符数量
        int numberOfPractical=0;
        int i=0;
        //'\u0000'是char默认值
        while(c[i]!='\u0000'){
            
            numberOfPractical++;
            if(c[i]==' ')
                numberOfBlank++;
            
            i++;
        }
 
        // 计算转换后的字符长度
        int newLength = numberOfBlank * 2 + numberOfPractical;
        if (newLength > c.length) { // 如果转换后的长度大于数组的最大长度,直接返回失败
            return;
        }
 
        // 如果没有空白字符就不用处理
        if (numberOfBlank == 0) {
            return;
        }
        
        // indexOfOriginal 为指向原始字符串末尾的指针
        int indexOfOriginal=numberOfPractical;
        // indexOfNew 为指向替换之后的字符串末尾的指针
        int indexOfNew=newLength;
        // 当 indexOfOriginal = indexOfNew 时表明空格已全部替换完毕
        while (indexOfOriginal >= 0 && indexOfOriginal < indexOfNew) {
            // 如是当前字符是空白字符,进行"%20"替换
            if (c[indexOfOriginal] == ' ') {
                c[indexOfNew--] = '0';
                c[indexOfNew--] = '2';
                c[indexOfNew--] = '%';
            } else { // 否则移动字符
                c[indexOfNew--] = c[indexOfOriginal];
            }
            indexOfOriginal--;
        }
  }    
}
    原文作者:陈污龟
    原文地址: https://segmentfault.com/a/1190000020741403
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞