《剑指offer》算法题第一天
按照个人计划,从今天开始做《剑指offer》上面的算法题,练习平台为牛客网,上面对每道题都有充分的测试实例,感觉还是很不错的。今天下午做了四道题,分别为:
1. 二叉树的深度(书55题)
二叉树的深度
判断平衡二叉树
2. 数组中数字出现的次数(书56题)
数组中只出现一次的两个数字
3. 和为S的数字(书57题)
和为S的连续正数序列
和为S的两个数字
4. 翻转字符串(书58题)
左旋转字符串
翻转单词顺序列
二叉树类型的问题在leetcode上多次遇到,剑指上的题也比较简单,这边就不再说明,只在最后贴出代码,需要注意的是判断平衡二叉树如何使用一次遍历来判断(利用后序遍历,从叶节点往上判断)。
和为S的数字利用的是Two Pointers的思想,也算比较容易,所以今天的重点是第2,4题。
2. 数组中数字出现的次数
题目描述:一个整型数组里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。
本题采用位运算,与leetcode上137. Single Number II类似,且在Discuss中有大神贴出了解法和数学解释,感兴趣的同学可以了解一下。与leetcode上不同的是,本题有两个数字只出现一次,因此较为麻烦。
思路:
如果能把原数组分为两个只有一个数字出现的子数组,就能够用异或运算得出这两个数。
从头到尾异或数组中的每个数字,最终得到两个只出现一次的数字的异或结果。
由于这两个数字不一样,异或的结果肯定不为0,也就是说这个结果数字的二进制表示中至少有一位为1。
在结果数字中找到第一个为1的位的位置,记为第n位,以第n位是不是1为标准把原数组中的数字分成两个子数组,第一个子数组中每个数字的第n位都是1,而第二个子数组中每个数字的第n位都是0。这样,就把原数组分为了两个子数组,在每个子数组中只有一个数出现一次,其他数字都出现了两次。
实现代码:
1 //num1,num2分别为长度为1的数组。传出参数 2 //将num1[0],num2[0]设置为返回结果 3 public class Solution { 4 public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) { 5 int t = 0; //第一轮异或的结果 6 for(int n:array){ 7 t ^= n; 8 } 9 int firstOne = FindFirstOne(t);//找到第一位1的位置 10 num1[0] = 0; 11 num2[0] = 0; 12 for(int n:array){ 13 if(IsBit1(n,firstOne)){//根据第n位是否为1将数组分开 14 num1[0] ^= n; 15 }else{ 16 num2[0] ^= n; 17 } 18 } 19 } 20 21 public int FindFirstOne(int t){ 22 int first = 0; 23 while((t & 1) == 0 && first < 32){//int型为32位 24 t = t>>1; 25 first++; 26 } 27 return first; 28 } 29 30 public boolean IsBit1(int num,int indexBit){ 31 num = num >> indexBit; 32 return (num & 1) == 1; 33 } 34 }
4. 翻转字符串
题目描述: 翻转单词顺序: 输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。 为简单起见,标点符号和普通字母一样处理。如“I am a student.”,则输出为"student. a am I"。 左旋转字符串: 把字符串前面的若干个字符转移到字符串的尾部。 如输入"abcdefg"和数字2,则输出“cdefgab”。
这题考察的如何灵活的运用字符串翻转来处理新的问题,下面给出字符串翻转的实现代码,代码中,字符串首先被转化为字符数组再进行操作。
1 public void ReverseString(char[] array,int start,int end){ 2 if(start >= end) 3 return; 4 while(start < end){ 5 char tmp = array[start]; 6 array[start] = array[end]; 7 array[end] = tmp; 8 start++; 9 end--; 10 } 11 }
思路如下:
翻转单词顺序: 首先将句子中的所有字符翻转,如"I am a student."翻转得到".tneduts a ma I" 再将句子内的单词翻转回来得到"student. a am I" 左旋转字符串: 以"abcdefg"为例,把字符串根据n分为两部分 先对每一部分分别翻转,得到"bagfedc" 然后再对整个字符串进行翻转,得到"cdefgab"
代码如下:
翻转单词顺序:
public String ReverseSentence(String str) { char[] array = str.toCharArray(); ReverseString(array,0,array.length-1); int s = 0; for(int end = 0; end < array.length; end++){ if(array[end] == ' '){ ReverseString(array,s,end-1); s = end+1; } } ReverseString(array,s,array.length-1); return String.valueOf(array); }
左旋转字符串:
public String LeftRotateString(String str,int n) { int len = str.length(); if(len <= 1 || n%len == 0) return str; n %= len; char[] array = str.toCharArray(); ReverseString(array,0,n-1); ReverseString(array,n,len-1); ReverseString(array,0,len-1); return String.valueOf(array); }
最后贴上第1和第3题的代码:
二叉树:
//二叉树的深度 public class Solution { public int TreeDepth(TreeNode root) { if(root == null) return 0; int left = TreeDepth(root.left); int right = TreeDepth(root.right); return Math.max(left,right)+1; } } //判断平衡二叉树 public class Solution { public boolean IsBalanced_Solution(TreeNode root) { if(root == null) return true; return maxDepth(root) != -1; } public int maxDepth(TreeNode node){ if(node == null) return 0; int left = maxDepth(node.left); int right = maxDepth(node.right); if(left == -1 || right == -1 || Math.abs(left-right) > 1){ return -1; } return Math.max(left,right)+1; } }
和为S的数字:
//和为S的两个数字 import java.util.ArrayList; public class Solution { public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) { ArrayList<Integer> res = new ArrayList(); if(array.length == 0) return res; int head = 0, tail = array.length-1; while(head < tail){ int p = array[head]; int q = array[tail]; if(p+q == sum){ res.add(p); res.add(q); break; }else if(p+q > sum){ tail --; }else{ head ++; } } return res; } } //和为S的连续正数序列 import java.util.ArrayList; public class Solution { public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) { int small = 1, big = 2, mid = (sum+1)/2; ArrayList<ArrayList<Integer>> res = new ArrayList(); ArrayList<Integer> tmp = new ArrayList(); tmp.add(small); tmp.add(big); int total = small+big; while(small < mid){ if(total == sum){ res.add(new ArrayList(tmp)); } if(total > sum){ total -= small; small++; tmp.remove(0); }else{ big++; total += big; tmp.add(big); } } return res; } }