剑指offer二刷知识点总结

全排列问题

JZ27-字符串的排列

  • 题目描述
    输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
  • 思路
    首先对于有重复字母的字符串进行全排列,如果需要去重,那么可以使用TreeSet来存储,可以自动去重并排序。
    思路是把每一个字符与当前第一个字符进行交换,然后递归处理以当前第一个字符为首的后面子串,待到子串处理完毕后,再把前面交换的字符再次交换过来;
    递归结束的条件是待处理的子串只有一个字符,这个时候就可以直接输出当前字符串了。
    数字的全排列跟字幕全排列的思路一模一样,没有任何区别
  • 实现代码
//4号题:JZ27
    //字符串的排列(按照字典的顺序对一个自渡序列进行排列)
    //相当于全排列
    public ArrayList Permutation(String str) {
        if ("".equals(str) || str == null || str.length() == 0)
            return list;
        //转换成数组
        char[] ch = str.toCharArray();
        //递归处理
        perm(ch, 0);
        for (String s : set)
            list.add(s);
        return list;
    }

    private void perm(char[] ch, int i) {
        //如果只有一个字符的时候,直接输出即可
        if (i == ch.length - 1) {
            set.add(String.valueOf(ch));
        } else {
            //循环把每个字符与当前i处的字符交换,再递归处理
            for (int j = i; j < ch.length; ++j) {
                char t = ch[i];
                ch[i] = ch[j];
                ch[j] = t;
                //递归处理
                perm(ch, i + 1);
                //再交换回来
                t = ch[i];
                ch[i] = ch[j];
                ch[j] = t;
            }
        }
    }

递归

JZ10-矩形覆盖

  • 题目描述
    我们可以用2x1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2x1的小矩形无重叠地覆盖一个2xn的大矩形,总共有多少种方法?
  • 思路
    当只有1个2x1的小矩形时,只有一种摆法;当有2个2x1的小矩形时,有2种摆法;当有3个2*1的小矩形时,有3种摆法;
    当有n个2x1的小矩形时,需要考虑第一个小矩形的摆法,如果第一个小矩形竖着摆(如下图),则有f(n-1)种摆法;剑指offer二刷知识点总结_第1张图片
    如果第一个小矩形横着摆,那么该小矩形下面也需要横着摆一个小矩形(如下图),则有f(n-2)种摆法;剑指offer二刷知识点总结_第2张图片
    所以,n个2x1的小矩形总共有f(n-1)+f(n-2)种摆法。
    与斐波那契数列的实现一样。

JZ8-跳台阶问题

  • 题目描述
    一次可以跳1个或2个台阶,共有n个台阶,问共有多少种跳法?
  • 思路
    关键还是看第一步的跳法,如果第一步跳1个台阶,那么共有f(n-1)种跳法;如果第一步跳2个台阶,那么共有f(n-2)种跳法;故,跳n个台阶共有f(n-1)+f(n-2)种跳法。

位运算相关

JZ40-只出现一次的数字

  • 题目描述
    一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
  • 思路
    异或运算^特点: 两个相同的数字进行异或,结果为0;任何一个数字与0异或,结果为其本身。
    将数组中所有数字进行异或运算,结果必为那两个只出现一次的数字异或后的结果(设为a);a的为1的最低位的位置(设为pos),因为在a中pos为是1,那么所求的两个数字在pos位必定是一个为1,另一个为0,;以此为依据,将原数组分为两组;然后分别将这两组数字全部异或,结果必为所求的两个数字;
  • 实现代码
 public void FindNumsAppearOnce(int[] array, int num1[], int num2[]) {
        int res = 0;
        for (int i = 0; i < array.length; ++i) {
            res ^= array[i];
        }
        int pos = findFirstOne(res); //寻找res最低位1的位置
        //根据pos为是否为1进行分组
        for (int i = 0; i < array.length; ++i) {
            if (isPosBit(array[i], pos)) {
                num1[0] ^= array[i];
            } else {
                num2[0] ^= array[i];
            }
        }
    }

    //判断num的pos位是否为1
    private boolean isPosBit(int num, int pos) {
        if (((num >> pos) & 1) == 1)
            return true;
        return false;
    }

    private int findFirstOne(int res) {
        int index = 0;
        while ((res & 1) == 0) {
            res >>= 1;
            ++index;
        }
        return index;
    }

JZ48-不使用加减乘除对两个数进行相加运算

  • 思路
    首先,先看普通的运算时如何进行的,例如:5+7=12:
    step1:5+7不进位结果为2;
    step2:计算其进位为10;
    step3:将前两者相加,得最终结果;

计算不进位的结果可以使用异或^ 来运算;
计算进位的结果可以使用按位与然后向左移一位来运算;
循环终止的条件是进位的结果是0,即不需要进位;
此时,可得最终结果。

  • 实现代码
 //5号题:JZ-48
    //写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。
    public int Add(int num1, int num2) {
        int nowNum = 0;
        while (num1 != 0) {
            //计算不进位的结果
            nowNum = num1 ^ num2;//异或
            //计算进位
            num1 = (num1 & num2) << 1; //按位与后左移一位
            num2 = nowNum;
        }
        return num2;
    }

你可能感兴趣的:(算法)