• 剑指offer—java版
  • 01-二维数组中的查找
    • 思路
    • 实现
  • 02-替换空格
    • 思路
    • 实现
  • 03-从尾到头打印链表
    • 思路
    • 实现
  • 04-旋转数组的最小数字
    • 思路
    • 05-斐波那契数列
    • 06-青蛙跳
    • 07-变态青蛙跳
    • 08-矩阵覆盖
    • 09-二进制中1的个数
      • 思路
      • 实现
    • 10-数值的整数次方
    • 11-调整数组顺序奇数在偶数前面
    • 二叉搜索树的后序遍历序列
      • 实现
    • 复杂链表的复制
      • 思路
    • #
    • 二叉搜索树与双向链表
    • 字符串的排列

    剑指offer—java版

    代码格式按牛客网在线答题格式编写

    01-二维数组中的查找

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

    思路

    坑:不要从中间去,采用二分查找。如果中间查找会有两个区域需要查找,增加复杂度。
    真确思路:从右上角开始遍历,有三种情况:

    1. 如果相等,则直接返回。
    2. 如果值大于目标值,列减小。(因为从坐到右增大,因此右边的值不可能存在目标值)
    3. 如果值小于目标值,增大行。(从上往下增大,该值在最右上角为当前行最大,因此当前行没有更大的,可以往下一行找)

    思考,是否可以从左上角开始、右下角、左下角开始?
    左上角不可以,右下角不可以。因为这这两个移动后还是会形成两个区域需要查找。
    左下角可以。

    实现

    1. public boolean Find(int [][] array,int target) {
    2. if (array == null || array[0].length == 0 ) {
    3. return false;
    4. }
    5. int row = 0;
    6. int col = array.length - 1;
    7. while ( row < array.length && col >=0 ) {
    8. if ( target > array[row][col]) {
    9. row ++;
    10. } else if (target < array[row][col]) {
    11. col --;
    12. } else {
    13. return true;
    14. }
    15. }
    16. return false;
    17. }

    02-替换空格

    请实现一个函数,将一个字符串中的空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。

    思路

    1. 先计算空格数目
    2. 计算新的字符串长度,并构建新字符数组
    3. 逐个遍历和copy, 遇到空格用户“%20”替换

    实现

    1. public String replaceSpace(StringBuffer str) {
    2. // 第一件重要的事
    3. if( str == null || str.length() == 0)
    4. return str.toString();
    5. // 长度计算
    6. int newlength = newLength(str);
    7. char[] newChar = new char[newlength];
    8. int idx = 0;
    9. for (int i = 0; i < str.length(); i++) {
    10. if (str.charAt(i) == ' ') {
    11. newChar[idx++] = '%';
    12. newChar[idx++] = '2';
    13. newChar[idx++] = '0';
    14. } else {
    15. newChar[idx++] = str.charAt(i);
    16. }
    17. }
    18. return new String(newChar);
    19. }
    20. private int newLength(StringBuffer str) {
    21. int spaceNum = 0;
    22. for (int i = 0; i < str.length(); i ++) {
    23. if (str.charAt(i) == ' ') {
    24. spaceNum++;
    25. }
    26. }
    27. // 或者 str.length() + spaceNum * 2
    28. return (str.length() - spaceNum) + spaceNum * 3;
    29. }

    03-从尾到头打印链表

    输入一个链表,从尾到头打印链表每个节点的值。

    思路

    利用栈,先进后出的思路。

    实现

    1. public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
    2. ArrayList<Integer> result = new ArrayList<Integer>();
    3. if (null == listNode) {
    4. return result;
    5. }
    6. Stack<ListNode> stack = new Stack<ListNode>();
    7. ListNode next = listNode;
    8. while(null != next) {
    9. stack.push(next);
    10. next = next.next;
    11. }
    12. while(!stack.isEmpty()) {
    13. result.add(stack.pop().val);
    14. }
    15. return result;
    16. }

    04-旋转数组的最小数字

    把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。

    思路

    二分查找,不断缩短查找范围。

    1. mid值与左右两端比较,如果小于右边,high=mid.
    2. mid大于左边,low=mid
    3. 如果左值=mid值=右值,顺序遍历
    1. public int minNumberInRotateArray(int [] array) {
    2. if ( array == null || array.length == 0 ) {
    3. return 0;
    4. }
    5. int low = 0, high = array.length - 1;
    6. int mid;
    7. while ( low < high && array[low] >= array[high]) {
    8. // array[low] 需要大于等于 array[high] 在内部进行 遍历
    9. if ( (low +1 )== high) { //这个很关键,说明只有两个元素
    10. return array[high];
    11. }
    12. if (array[low] == array[high]) {
    13. int min = array[low];
    14. for ( int i = low +1; i <= high; i++) {
    15. if(array[i] < min){
    16. min = array[i];
    17. }
    18. }
    19. return min;
    20. } else {
    21. mid = (low + high) >> 1;
    22. if (array[mid] < array[high]){
    23. high = mid;
    24. } else {
    25. low = mid;
    26. }
    27. }
    28. }
    29. // 只有一个元素 或者 array[low] < array[high]
    30. return array[low];
    31. }

    05-斐波那契数列

    1. public int Fibonacci(int n) {
    2. if(n <= 0) {
    3. return 0;
    4. }
    5. if (n == 1 || n == 2) {
    6. return 1;
    7. }
    8. int preOne = 1;
    9. int preTwo = 1;
    10. int temp;
    11. for ( int i = 3; i <= n; i++ ){
    12. temp = preOne;
    13. preOne = preOne + preTwo;
    14. preTwo = temp;
    15. }
    16. return preOne;
    17. }

    06-青蛙跳

    一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

    1. public int JumpFloor(int target) {
    2. if(target <= 0) return 0;
    3. if(target == 1) return 1;
    4. if(target == 2) return 2;
    5. int preOne = 2, preTwo = 1;
    6. for(int i = 3; i <= target; i++){
    7. int temp = preOne + preTwo;
    8. preTwo = preOne;
    9. preOne = temp;
    10. }
    11. return preOne;
    12. }

    07-变态青蛙跳

    一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

    1. public int JumpFloorII(int target) {
    2. if(target <= 0) return 0;
    3. int result = 1;
    4. target --;
    5. while(target !=0){
    6. result = result << 1;
    7. target --;
    8. }
    9. return result;
    10. }

    08-矩阵覆盖

    我们可以用21的小矩形横着或者竖着去覆盖更大的矩形。请问用n个21的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?

    1. public int RectCover(int target) {
    2. if (target <= 0) return 0;
    3. if(target == 1) return 1;
    4. if(target == 2) return 2;
    5. int preOne = 2, preTwo =1;
    6. for(int i = 3; i <= target; i++){
    7. int temp = preOne + preTwo;
    8. preTwo = preOne;
    9. preOne = temp;
    10. }
    11. return preOne;
    12. }

    09-二进制中1的个数

    输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。

    思路

    如果一个整数不为0,那么这个整数至少有一位是1。如果我们把这个整数减1,那么原来处在整数最右边的1就会变为0,原来在1后面的所有的0都会变成1(如果最右边的1后面还有0的话)。其余所有位将不会受到影响。
    举个例子:一个二进制数1100,从右边数起第三位是处于最右边的一个1。减去1后,第三位变成0,它后面的两位0变成了1,而前面的1保持不变,因此得到的结果是1011.我们发现减1的结果是把最右边的一个1开始的所有位都取反了。这个时候如果我们再把原来的整数和减去1之后的结果做与运算,从原来整数最右边一个1那一位开始所有位都会变成0。如1100&1011=1000.也就是说,把一个整数减去1,再和原整数做与运算,会把该整数最右边一个1变成0.那么一个整数的二进制有多少个1,就可以进行多少次这样的操作。

    实现

    1. public int NumberOf1(int n) {
    2. int count = 0;
    3. while(n!= 0){
    4. count++;
    5. n = n & (n - 1);
    6. }
    7. return count;
    8. }

    10-数值的整数次方

    给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。

    1. public double Power(double base, int exponent) {
    2. if (base == 0) {
    3. return 0;
    4. }
    5. boolean flag = false; // 是否exponent是负数
    6. if (exponent <= 0) {
    7. exponent = -1 * exponent;
    8. flag = true;
    9. }
    10. double result = 1.0;
    11. while (exponent > 0) {
    12. result = result * base;
    13. exponent--;
    14. }
    15. if (flag) {
    16. return 1.0 / result;
    17. } else {
    18. return result;
    19. }
    20. }

    11-调整数组顺序奇数在偶数前面

    输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。

    二叉搜索树的后序遍历序列

    输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。

    实现

    1. public boolean VerifySquenceOfBST(int [] sequence) {
    2. if (sequence == null || sequence.length ==0) {
    3. return false;
    4. }
    5. return verifySequenceOfBST(sequence, 0, sequence.length-1);
    6. }
    7. private boolean verifySequenceOfBST(int[] sequence,int begin,int end){
    8. if (begin == end) {
    9. return true;
    10. }
    11. int rightBegin = begin;
    12. int root = sequence[end];
    13. for (;rightBegin < end; rightBegin++ ) {
    14. if (sequence[rightBegin] > root ) {
    15. break;
    16. }
    17. }
    18. int i = rightBegin;
    19. for (; i < end; i ++ ) {
    20. if (sequence[i] < root) {
    21. return false;
    22. }
    23. }
    24. boolean left = (rightBegin == begin )? true : verifySequenceOfBST(sequence, begin, rightBegin -1);
    25. boolean right = (rightBegin == end) ? true : verifySequenceOfBST(sequence, rightBegin+1, end);
    26. return left & right;
    27. }

    复杂链表的复制

    输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)

    思路

    三步走:

    1. 在原来链中插入克隆节点,是的克隆节点和源节点相间出现。
    2. 复制random指针
    3. 从原链从剥离clone链。

    #

    1. public RandomListNode Clone(RandomListNode pHead)
    2. {
    3. cloneNodes(pHead);
    4. connectRandomNodes(pHead);
    5. return reconnectNodes(pHead);
    6. }
    7. private void cloneNodes(RandomListNode pHead){
    8. RandomListNode node = pHead;
    9. while(node != null){
    10. RandomListNode clone = new RandomListNode(node.label);
    11. RandomListNode temp = node.next;
    12. clone.next = temp;
    13. node.next = clone;
    14. node = temp;
    15. }
    16. }
    17. private void connectRandomNodes(RandomListNode pHead){
    18. RandomListNode node = pHead;
    19. while(node != null){
    20. RandomListNode clone = node.next;
    21. if(node.random != null)//这里一定要判断
    22. clone.random = node.random.next;
    23. node = clone.next;
    24. }
    25. }
    26. private RandomListNode reconnectNodes(RandomListNode pHead){
    27. RandomListNode node = pHead;
    28. RandomListNode cloneHead = null;
    29. RandomListNode cloneNode = null;
    30. if(node != null){
    31. cloneHead = cloneNode = node.next;
    32. node.next = cloneNode.next;
    33. node = node.next;
    34. }
    35. while(node != null){
    36. cloneNode.next = node.next;
    37. cloneNode = cloneNode.next;
    38. node.next = cloneNode.next;
    39. node = cloneNode.next;
    40. }
    41. return cloneHead;
    42. }

    二叉搜索树与双向链表

    输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。

    1. public class Solution {
    2. public TreeNode Convert(TreeNode root){
    3. TreeNode lastNode = null;
    4. lastNode = doConvert(root, lastNode);
    5. while(lastNode != null && lastNode.left != null) {
    6. lastNode = lastNode.left;
    7. }
    8. return lastNode;
    9. }
    10. /* 转换并返回链表最后一个结点
    11. */
    12. private TreeNode doConvert(TreeNode root, TreeNode lastNode){
    13. if (root == null) {
    14. return lastNode;
    15. }
    16. if (root.left != null) {
    17. lastNode = doConvert(root.left, lastNode);
    18. }
    19. if(lastNode != null) {
    20. lastNode.right = root;
    21. }
    22. root.left = lastNode;
    23. lastNode = root;
    24. if (root.right != null) {
    25. lastNode = doConvert(root.right, lastNode);
    26. }
    27. return lastNode;
    28. }
    29. }

    字符串的排列

    输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。

    1. import java.util.ArrayList;
    2. import java.util.Collections;
    3. public class Solution {
    4. public ArrayList<String> Permutation(String str) {
    5. ArrayList<String> result = new ArrayList<String>();
    6. if(str == null || str.length() == 0)
    7. return result;
    8. doPermutation(str.toCharArray(),0,result);
    9. // 实际中不需要排序
    10. Collections.sort(result);
    11. return result;
    12. }
    13. private void doPermutation(char[] array,int idx,ArrayList<String> result){
    14. if(idx >= array.length){
    15. String str = new String(array);
    16. result.add(str);
    17. return;
    18. }
    19. for(int i=idx; i < array.length; i++){ //i得从 idx开始
    20. if(i != idx && array[i] == array[idx] )
    21. continue;
    22. swap(array,idx,i);
    23. doPermutation(array,idx+1,result);
    24. swap(array,idx,i);
    25. }
    26. }
    27. private void swap(char[] array,int i,int j){
    28. char temp = array[i];
    29. array[i] = array[j];
    30. array[j] = temp;
    31. }
    32. }