剑指Offer题解
剑指 Offer 03. 数组中重复的数字
思路一:哈希表O(n)
class Solution {
public int findRepeatDocument(int[] documents) {
int len = documents.length;
int[] arr = new int[100010];
for(int i = 0; i < len; i ++ ) {
arr[documents[i]] ++;
if (arr[documents[i]] == 2) {
return documents[i];
}
}
return 0;
}
}
思路二: 原地交换O(n)
class Solution {
public int findRepeatDocument(int[] documents) {
int len = documents.length;
for(int i = 0; i < len; i ++ ){
while (documents[i] != i) {
if(documents[i] == documents[documents[i]]) {
return documents[i];
}
int t = documents[i];
documents[i] = documents[t];
documents[t] = t;
}
}
return -1;
}
}
剑指 Offer 04. 二维数组中的查找
思路一:单调性扫描O(n+m)
class Solution {
public boolean findTargetIn2DPlants(int[][] plants, int target) {
int len1 = plants.length;
if (len1 <= 0) return false;
int len2 = plants[0].length;
if (len2 <= 0) return false;
for(int i = len1 - 1, j = 0; ;) {
if(plants[i][j] <target) {
j ++;
} else if(plants[i][j] > target){
i --;
} else {
return true;
}
if(i < 0 || j >= len2) {
break;
}
}
return false;
}
}
思路二:二分O(nlogn)
class Solution {
public boolean findTargetIn2DPlants(int[][] plants, int target) {
int len1 = plants.length;
if (len1 <= 0) return false;
int len2 = plants[0].length;
if (len2 <= 0) return false;
for(int i = 0; i < len1; i ++ ) {
if (midf(plants, i, target)) {
return true;
}
}
return false;
}
public boolean midf(int[][] plants, int i, int target) {
int l = 0;
int r = plants[i].length - 1;
while(l < r) {
int mid = l + r >> 1;
if(plants[i][mid] >= target) {
r = mid;
} else {
l = mid + 1;
}
}
if(plants[i][l] == target) return true;
return false;
}
}
剑指 Offer 05. 替换空格
思路一:遍历添加O(n)
class Solution {
public String pathEncryption(String path) {
StringBuilder sb = new StringBuilder();
char[] c = path.toCharArray();
for(char ct : c) {
if(ct == '.') sb.append(' ');
else sb.append(ct);
}
return sb.toString();
}
}
思路二:正则表达式替换O(n)
class Solution {
public String pathEncryption(String path) {
return path.replaceAll("\\.", " ");
}
}
剑指 Offer 06. 从尾到头打印链表
思路一:辅助栈法O(n)
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public int[] reverseBookList(ListNode head) {
Stack<Integer> stack = new Stack();
int[] q;
ListNode dummy = head;
int length = 0;
while(dummy != null) {
stack.push(dummy.val);
dummy = dummy.next;
length ++;
}
q = new int[length];
for(int i = 0; i < length; i++ ) {
q[i] = stack.pop();
}
return q;
}
}
思路二:递归法O(n)
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
ArrayList<Integer> temp = new ArrayList();
public int[] reverseBookList(ListNode head) {
recur(head);
int[] res = new int[temp.size()];
for(int i = 0; i < res.length; i ++) {
res[i] = temp.get(i);
}
return res;
}
void recur(ListNode head) {
if(head == null) return;
recur(head.next);
temp.add(head.val);
}
}
思路三:遍历反转法O(n)
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
ArrayList<Integer> list = new ArrayList();
public int[] reverseBookList(ListNode head) {
int length = 0;
ListNode dummy = head;
while(dummy != null) {
list.add(dummy.val);
dummy = dummy.next;
length ++;
}
Collections.reverse(list);
int[] q = new int[list.size()];
for(int i = 0; i < q.length; i ++) {
q[i] = list.get(i);
}
return q;
}
}
剑指 Offer 07. 重建二叉树
思路一:递归法O(n)
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
int[] preorder;
HashMap<Integer, Integer> hmap = new HashMap();
public TreeNode deduceTree(int[] preorder, int[] inorder) {
this.preorder = preorder;
for(int i = 0; i < inorder.length; i ++ ) {
hmap.put(inorder[i], i);
}
return recur(0, 0, inorder.length - 1);
}
TreeNode recur(int root, int left, int right) {
if(left > right) return null;
TreeNode node = new TreeNode(preorder[root]);
int i = hmap.get(preorder[root]);
node.left = recur(root + 1, left, i - 1);
node.right = recur(root + i - left + 1, i + 1, right);
return node;
}
}
剑指 Offer 08. 二叉树的下一个结点
思路一:中序遍历法O(n)
import java.util.*;
public class Solution {
ArrayList<TreeLinkNode> nodes = new ArrayList<>();
public TreeLinkNode GetNext(TreeLinkNode pNode) {
// 获取根节点
TreeLinkNode root = pNode;
while(root.next != null) root = root.next;
// 中序遍历打造nodes
InOrder(root);
// 进行匹配
int n = nodes.size();
for(int i = 0; i < n - 1; i++) {
TreeLinkNode cur = nodes.get(i);
if(pNode == cur) {
return nodes.get(i+1);
}
}
return null;
}
// 中序遍历
void InOrder(TreeLinkNode root) {
if(root != null) {
InOrder(root.left);
nodes.add(root);
InOrder(root.right);
}
}
}
思路二:分类直接查找法O(n)
import java.util.*;
public class Solution {
public TreeLinkNode GetNext(TreeLinkNode pNode) {
// 情况一
if(pNode.right != null) {
TreeLinkNode rchild = pNode.right;
// 一直找到右子树的最左下的结点为返回值
while(rchild.left != null) rchild = rchild.left;
return rchild;
}
// 情况二
if(pNode.next != null && pNode.next.left == pNode) {
return pNode.next;
}
// 情况三
if(pNode.next != null) {
TreeLinkNode ppar = pNode.next;
// 沿着左上一直爬树,爬到当前结点是其父节点的左自己结点为止
while(ppar.next != null && ppar.next.right == ppar) ppar = ppar.next;
return ppar.next;
}
return null;
}
}
剑指 Offer 09. 用两个栈实现队列
思路:栈模拟O(n)
class CQueue {
Stack<Integer> stack1;
Stack<Integer> stack2;
public CQueue() {
stack1 = new Stack();
stack2 = new Stack();
}
public void appendTail(int value) {
stack1.push(value);
}
public int deleteHead() {
if (stack2.isEmpty()) {
while(!stack1.isEmpty()) {
stack2.push(stack1.pop());
}
}
return stack2.isEmpty() ? -1 : stack2.pop();
}
}
/**
* Your CQueue object will be instantiated and called as such:
* CQueue obj = new CQueue();
* obj.appendTail(value);
* int param_2 = obj.deleteHead();
*/
剑指 Offer 10- I. 斐波那契数列
思路一:暴力递归O(2^n) 超出时间限制
class Solution {
public int fib(int n) {
if(n < 2) return n;
return (fib(n - 1) + fib(n - 2)) % 1000000007;
}
}
**思路二:记忆化递归O(n) **
class Solution {
public int fib(int n) {
if(n < 2) return n;
int[] fib = new int[n + 1];
fib[0] = 0;
fib[1] = 1;
for(int i = 2; i <= n; i ++) {
fib[i] = (fib[i - 1] + fib[i - 2]) % 1000000007;
}
return fib[n];
}
}
**思路三:动态规划O(n) **
class Solution {
public int fib(int n) {
if (n < 2) return n;
int a = 0;
int b = 1;
int sum;
for(int i = 0; i < n; i ++) {
sum = (a + b) % 1000000007;
a = b;
b = sum;
}
// i =0 时, a就是fib[1]的值
return a;
}
}
剑指 Offer 10- II. 青蛙跳台阶问题
**思路一:记忆化递归O(n) **
class Solution {
public int trainWays(int num) {
if(num < 2) return 1;
int[] fib = new int[num + 1];
fib[0] = 1;
fib[1] = 1;
for(int i = 2; i <= num; i++ ){
fib[i] = (fib[i - 1] + fib[i - 2]) % 1000000007;
}
return fib[num];
}
}
思路二:动态规划O(n)
class Solution {
public int trainWays(int num) {
int a = 0;
int b = 1;
int sum;
for(int i = 0; i < num; i ++) {
sum = (a + b) % 1000000007;
a = b;
b = sum;
}
//i= 0时,b就是fib[1]的值,和上一题的区别就是num = 0时,有一种方法
return b;
}
}