1.剑指 Offer 03. 数组中重复的数字
方法一:哈希表法
-
看到重复的数字,首先想到哈希表(set)记录数组中各个数字,具体方法是先新建一个hashset,然后遍历数组中的每一个数字num:当num在表中,则返回num,最后将Num添加到哈希表中。返回-1.因为本题中一定有重复数字,返回其他值也行。
-
代码:
class Solution { public int findRepeatNumber(int[] nums) { HashSet <Integer> set = new HashSet<>(); for(int x : nums){ if(set.contains(x)){ return x; } set.add(x); } return -1; } }
方法二:原地交换法
-
数组的索引和值是一对多的关系。首先判断索引和索引对应的值是否相等,如果相等,则不需要交换;判断索引 nums[i]处和索引 i处对应的元素值都为Nums[i],即重复,返回nums[i];否则将索引i和nums[i]的元素值交换
-
代码
class Solution { public int findRepeatNumber(int[] nums) { for(int i=0;i<nums.length;i++){ while(nums[i] != i){ if(nums[i]==nums[nums[i]]){ return nums[i]; } int temp=nums[i]; nums[i]=nums[temp]; nums[temp]=temp; } } return -1; } }
2.剑指 Offer 04. 二维数组中的查找
方法一:暴力解法
-
双循环,一个一个查找
public boolean findNumberIn2DArray(int[][] matrix, int target) { int m = matrix.length; if (m == 0) return false; int n = matrix[0].length; for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { if (matrix[i][j] == target) { return true; } } } return false; }
方法二:二分查找
-
思路:首先,要先比较m,n二者的大小,其中较小的值就是对角线的个数,也就是循环的次数,然后分别在行和列方向上做二分查找。定义两个方法,如果其中的一个为真,则返回true。在行上,首先将行值给最低的数,因为要从这行开始找,然后开始循环,找到中位数,此时固定行,列是变量,列就为Mid一直在变,和目标值进行比较,小于目标值就将左指针右移到mid右边,大于就将右指针左移一位,相等直接返回true,否则返回false。在列方法里面,和行一样,也就是把row换成了col。
-
代码
public boolean findNumberIn2DArray(int[][] matrix, int target) { int m = matrix.length; if (m == 0) return false; int n = matrix[0].length; int shortDim = Math.min(m, n); for (int i = 0; i < shortDim; i++) { boolean rowFound = binarySearchRow(matrix, i, target); boolean colFount = binarySearchCol(matrix, i, target); if (rowFound || colFount) { return true; } } return false; } private boolean binarySearchRow(int[][] matrix, int row, int target) { int lo = row; int hi = matrix[0].length - 1; while (lo <= hi) { int mid = lo + (hi - lo) / 2; if (matrix[row][mid] == target) { return true; } else if (matrix[row][mid] < target) { lo = mid + 1; } else { hi = mid - 1; } } return false; }
方法三:线性查找
-
思路:发现,左下角的元素值大于它上面的元素,小于它右边的元素,因此如果目标值大于矩阵中的某一个数,那么就像列元素加一,如果小于,就将行元素减一,找到了就返回true,否则返回false。
-
代码:
public boolean findNumberIn2DArray(int[][] matrix,int target) { int m = matrix.length; if (m == 0) return false; int n = matrix[0].length; int i = m - 1; int j = 0; while (i >= 0 && j < n) { if (matrix[i][j] < target) j++; else if (matrix[i][j] > target) i--; else return true; } return false; }
3.剑指 Offer 05. 替换空格
方法一:StringBuilder
-
思路:java中的StringBuilder是表达可变的字符串的。首先初始化一个实例,然后遍历,如果遍历到非空字符串时,直接将字符串append到实例中,否则将%20append到实例中,最后返回toString
-
代码
public String replaceSpace(String s) { StringBuilder sb = new StringBuilder(); for (char c : s.toCharArray()) { if (c == ' ') sb.append("%20"); else sb.append(c); } return sb.toString(); }
方法二:数组
-
思路:首先,初始化一个静态数组,长度为3*n,再初始化两个指针,i指向静态数组的第一个位置,j指向元数组位置,
-
代码
public String replaceSpace(String s) { int n = s.length(); char[] newArr = new char[3 * n]; int j = 0; for (int i = 0; i < n; i++) { char c = s.charAt(i); if (c == ' ') { newArr[j++] = '%'; newArr[j++] = '2'; newArr[j++] = '0'; } else { newArr[j++] = c; } } return new String(newArr, 0, j); }
或者
public String replaceSpace(String s) { int n = s.length(); int cnt = 0; for (char c : s.toCharArray()) { if (c == ' ') cnt++; } char[] newArr = new char[n + 2 * cnt]; int j = 0; for (int i = 0; i < n; i++) { char c = s.charAt(i); if (c == ' ') { newArr[j++] = '%'; newArr[j++] = '2'; newArr[j++] = '0'; } else { newArr[j++] = c; } } return new String(newArr); }
思路:要返回一个数组,首先要计算数组的长度,等于链表的节点个数:从头节点往后遍历,求出节点个数之后u,将遇到节点的值从后往前放。
代码:
public int[] reversePrint(ListNode head){
if(head = null){
return new int[]{};
}
int length = 0;
ListNode curr = head;
while(curr != null){
length++;
curr = curr.next;
}
int[] res = new int[length];
int i = length-1;
curr = head;
while(curr != null){
res[i] = cur.val;
i--;
curr = curr.next;
}
return res;
}
或者采用栈的方法
public int[] reversePrint(ListNode head) {
if (head == null) return new int[]{};
// 1. 先将链表从头至尾的节点依次添加到栈中
Stack<ListNode> stack = new Stack<>();
ListNode curr = head;
while (curr != null) {
stack.push(curr);
curr = curr.next;
}
// 2. 依次从栈中取出节点,然后添加到数组中
int[] res = new int[stack.size()];
int i = 0;
while (!stack.isEmpty()) {
res[i] = stack.pop().val;
i++;
}
return res;
}
思路:
- 前序遍历性质: 节点按照
[ 根节点 | 左子树 | 右子树 ]
排序。
中序遍历性质: 节点按照[ 左子树 | 根节点 | 右子树 ]
排序。
代码:
class Solution{
HashMap<Integer,Integer> map = new HashMap<>();//标记中序遍历
int[] preorder;//保留的先序遍历,方便递归时依据索引查看先序遍历的值
public TreeNode buildTree(int[] preorder,int[] inorder){
this.preorder = preorder;
//将中序遍历的值及索引放在map中,方便递归时获取左子树与右子树的数量及其根的索引
for(int i =0;i<inorder.length;i++){
map.put(inorder[i],i);
}
return recur(0,0,inorder.length-1);
//三个索引分别为
//当前根的的索引
//递归树的左边界,即数组左边界
//递归树的右边界,即数组右边界
}
TreeNode recur(int pre_root,int in_left,int in_right){
if(in_left > in_right) return null;
TreeNode root = new TreeNode(preorder[pre_root]);//获取root节点
int idx = map.get(preorder[pre_root]);//获取在中序遍历中根节点所在索引,以方便获取左子树的数量
root.left = recur(pre_root + 1,in_left,idx-1);
//左子树的根的索引为先序中的根节点+1
//递归左子树的左边界为原来的中序in_left
//递归右子树的右边界为中序中的根节点索引-1
root.right = recur(pre_root + (idx-in_left)+1,idx+1,in_right);
//右子树的根的索引为先序中的 当前根位置 + 左子树的数量 + 1
//递归右子树的左边界为中序中当前根节点+1
//递归右子树的有边界为中序中原来右子树的边界
return root;
}
}