《剑指offer》算法题第二天

今日题目(分别对应剑指书3~9题):

  1. 数组中重复的数字
  2. 二维数组中的查找
  3. 替换空格
  4. 从尾到头打印链表
  5. 重建二叉树
  6. 二叉树的下一个节点
  7. 用两个栈实现队列

今日重点为1,2,5,6,后面会有详细的思路解析,现在先来简单地提一下其他题目:

3. 替换空格:

题目将一个句子中的空格转化为“%20”,为使时间复杂度达到O(n),采用从后往前遍历字符串的方法,即先遍历一遍字符串记录空格的个数,以此计算出转化完之后字符串新的长度,重新设定字符串长度之后,再从后往前逐字符修改字符串。

4. 从尾到头打印链表:

比较简单,可采用栈遍历链表,之后打印,也可以利用递归的方式。在这边博主建议大家要熟悉一下链表的各种操作,如翻转连表等。

7. 用两个栈实现队列:

一个栈作为入口,另一个栈作为出口,当出口栈空时,将入口栈的内容放进出口栈内,另外注意判断栈空的情况。

接下来是今天的重点内容:

1. 数组中重复的数字

题目描述:
在一个长度为n的数组里的所有数字都在0~n-1的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。

本题在leetcode中有相似的题目,为287. Find the Duplicate Number,但是稍微有一点不一样的是这题还需要判断是否有重复的数字,因此使用floyd判圈法就显得没那么理想。

思路:从头到尾依次骚面这个数组中的每个数字,当扫描到下标为i的数字时,首先比较这个数字(用m表示)是不是等于i。如果是,则接着扫描下一个数字;如果不是,则再拿他和第m个数字进行比较。如果他和第m个数字相等,就找到了一个重复的数字;如果不等,就把第i个数字和第m个数字交换。接下来重复这个比较、交换的过程,直到我们发现一个重复的数字。

代码如下:

 1 public class Solution {
 2     // Parameters:
 3     //    numbers:     an array of integers
 4     //    length:      the length of array numbers
 5     //    duplication: (Output) the duplicated number in the array number,length of duplication array is 1,so using duplication[0] = ? in implementation;
 6     //                  Here duplication like pointor in C/C++, duplication[0] equal *duplication in C/C++
 7     //    这里要特别注意~返回任意重复的一个,赋值duplication[0]
 8     // Return value:       true if the input is valid, and there are some duplications in the array number
 9     //                     otherwise false
10     public boolean duplicate(int numbers[],int length,int [] duplication) {
11         if(length <= 1) return false;
12         for(int i = 0; i < length; i++){
13             while(numbers[i] != i){
14                 if(numbers[i] == numbers[numbers[i]]){
15                     duplication[0] = numbers[i];
16                     return true;
17                 }
18                 int swap = numbers[numbers[i]];
19                 numbers[numbers[i]] = numbers[i];
20                 numbers[i] = swap;
21             }
22         }
23         return false;
24     }
25 }

2. 二维数组中的查找

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

思路:
首先选取数组中右上角的数字。如果该数字等于要查找的数字,则查找过程结束;如果该数字大于要查找的数字,则剔除这个数字所在的行。也就是说,如果要查找的数字不在数组的右上角,则每一次都在数组的查找范围中剔除一行或者一列,这样每一步都可以缩小查找的范围,直到找到要查找的数字,或者查找范围为空。

 代码如下:

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

5. 重建二叉树

题目描述:
输入二叉树的前序和中序遍历序列,构造二叉树。

思路:
利用前序和中序遍历序列的特点,每次找到根节点及其左右子树的节点,递归地建立二叉树。

这类型的题目在leetcode上有两题:

105. Construct Binary Tree from Preorder and Inorder Traversal

106. Construct Binary Tree from Inorder and Postorder Traversal

都是利用了遍历序列的特点来构造二叉树。

代码如下:

 1 /**
 2  * Definition for binary tree
 3  * public class TreeNode {
 4  *     int val;
 5  *     TreeNode left;
 6  *     TreeNode right;
 7  *     TreeNode(int x) { val = x; }
 8  * }
 9  */
10 public class Solution {
11     public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
12         return constructTree(pre,0,pre.length-1,in,0,in.length-1);
13     }
14     public TreeNode constructTree(int[] pre,int preStart,int preEnd,
15                                  int[] in,int inStart,int inEnd){
16         if(preStart > preEnd || inStart > inEnd)
17             return null;
18         TreeNode root = new TreeNode(pre[preStart]);
19         int r_index = inStart;
20         while(in[r_index] != root.val)
21             r_index++;
22         root.left = constructTree(pre,preStart+1,preStart+r_index-inStart,
23                                  in,inStart,r_index-1);
24         root.right = constructTree(pre,preStart+r_index-inStart+1,preEnd,
25                                   in,r_index+1,inEnd);
26         return root;
27     }
28 }

6. 二叉树的下一个节点

题目描述:
给定一棵二叉树和其中的一个节点,如何找出中序遍历序列的下一个节点?树中的节点除了有两个分别指向左右孩子的指针外,还有一个指向父节点的指针。

思路:
根据中序遍历的特点,如果一个节点有右孩子,那么它的下一个节点就是它的右子树中的最左子节点。如果没有右子树,那么下一个节点就是这个节点的祖先节点中,离它最近的第一个身为左孩子的节点的父节点。

 其实这题的思路并不难,放到重点来的原因是博主在看到这题的时候并没有第一时间反应出这个思路,所以拿来反思反思。。。

代码如下:

 1 /*
 2 public class TreeLinkNode {
 3     int val;
 4     TreeLinkNode left = null;
 5     TreeLinkNode right = null;
 6     TreeLinkNode next = null;
 7 
 8     TreeLinkNode(int val) {
 9         this.val = val;
10     }
11 }
12 */
13 public class Solution {
14     public TreeLinkNode GetNext(TreeLinkNode pNode)
15     {
16         if(pNode.right != null){
17             TreeLinkNode p = pNode.right;
18             while(p.left != null)
19                 p = p.left;
20             return p;
21         }else{
22             TreeLinkNode p = pNode;
23             while(p.next != null && p == p.next.right){
24                 p = p.next;
25             }
26             return p==null?null:p.next;
27         }
28     }
29 }
原文地址:https://www.cnblogs.com/wezheng/p/8352897.html