(还差几题,感觉没太大意义就不写了(
文章目录
- 2020.10.16
- 2020.10.17
- 剑指 Offer 68 - II. 二叉树的最近公共祖先
- 剑指 Offer 27. 二叉树的镜像
- 剑指 Offer 28. 对称的二叉树
- 剑指 Offer 29. 顺时针打印矩阵
- 剑指 Offer 30. 包含min函数的栈
- 剑指 Offer 32 - II. 从上到下打印二叉树 II
- 剑指 Offer 39. 数组中出现次数超过一半的数字
- 剑指 Offer 40. 最小的k个数
- 剑指 Offer 42. 连续子数组的最大和
- 剑指 Offer 50. 第一个只出现一次的字符
- 剑指 Offer 52. 两个链表的第一个公共节点
- 剑指 Offer 53 - I. 在排序数组中查找数字 I
- 剑指 Offer 53 - II. 0~n-1中缺失的数字
- 10.18
- 10.19
- 10.20
- 10.21
- 10.22
- 10.23
- 10.24
- 10.26
- 10.27
- 10.28
- 10.30
- 10.31
- 11.1
- 11.2
- 11.3
- 11.16
2020.10.16
剑指 Offer 68 - I. 二叉搜索树的最近公共祖先
二叉查找树:根节点的值大于其左子树中任意一个节点的值,小于其右节点中任意一节点的值,这一规则适用于二叉查找树中的每一个节点。
附加:二叉查找树要删除的节点既有左节点又有右节点,在这种情况下,我们只需要将找到待删节点的右子树中值最小的节点,将其删除并且获取其值,并用其值替换待删节点的值即可。【该值一定位于该右子树的最左子节点
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root == null){
return root;
}
if(root.val > p.val && root.val > q.val){
return lowestCommonAncestor(root.left,p,q);
}
if(root.val < p.val && root.val < q.val){
return lowestCommonAncestor(root.right,p,q);
}
return root;
}
}
2020.10.17
剑指 Offer 68 - II. 二叉树的最近公共祖先
二叉树不一定有序,不能用通过节点的值来比较。
节点p,q的最近公共祖先的两种情况:
- p,q在root两侧
- p,q在root一侧(要么都在root左子树,要么都在右子树),此时p=root或q=root
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root == null){
return null;
}
if(p == root || q == root){
return root;
}
TreeNode left = lowestCommonAncestor(root.left,p,q);
TreeNode right = lowestCommonAncestor(root.right,p,q);
if(right == null)return left;
if(left == null)return right;
return root;
}
}
剑指 Offer 27. 二叉树的镜像
通过递归来交换每个节点的左右子树。
class Solution {
public TreeNode mirrorTree(TreeNode root) {
if(root == null)return null;
mirrorTree(root.left);
mirrorTree(root.right);
TreeNode tmp = root.left;
root.left = root.right;
root.right = tmp;
return root;
}
}
剑指 Offer 28. 对称的二叉树
对称的二叉树的左子树,二叉树左边的左边要和二叉树右边的右边相同,一左边的右边要和右边的左边相同。(同时满足)
因此递归传递左左和右右
,左右和右左
递归停止条件:左右节点都为空(倒底了都长得一样 ->true)或者只有一个为空(说明此时不相等 ->false)
class Solution {
public boolean isSymmetric(TreeNode root) {
if(root == null)return true;
return test(root.left,root.right);
}
public boolean test(TreeNode a,TreeNode b){
if(a == null && b == null)return true;
if(a == null || b == null)return false;
if(a.val != b.val)return false;
return test(a.left,b.right)&&test(a.right,b.left);
}
}
剑指 Offer 29. 顺时针打印矩阵
只会弱智模拟(…)
class Solution {
public int[] spiralOrder(int[][] matrix) {
int n = matrix.length;
if(n == 0)return new int[0];
int m = matrix[0].length;
int[] result = new int[n*m];
int index = 0;
int left = 0,top = 0,right = m-1,botton = n-1;
while(true){
for(int i=left;i<=right;i++){
result[index++] = matrix[top][i];
}
if(++top > botton){
break;
}
for(int i=top;i<=botton;i++){
result[index++]=matrix[i][right];
}
if(--right < left){
break;
}
for(int i=right;i>=left;i--){
result[index++]=matrix[botton][i];
}
if(--botton<top){
break;
}
for(int i=botton;i>=top;i--){
result[index++]=matrix[i][left];
}
if(++left>right){
break;
}
}
return result;
}
}
剑指 Offer 30. 包含min函数的栈
因为要求调用 min、push 及 pop 的时间复杂度都是 O(1),因此需要辅助栈来保存所有 非严格降序 的元素,则栈 A 中的最小元素始终对应栈 B 的栈顶元素,即 min() 函数只需返回栈 B 的栈顶元素即可。
class MinStack {
Stack<Integer> a,b;
/** initialize your data structure here. */
public MinStack() {
a = new Stack<>();
b = new Stack<>();
}
public void push(int x) {
a.push(x);
if(b.empty()||b.peek()>=x)b.push(x);
}
public void pop() {
if(a.pop().equals(b.peek()))
b.pop();
}
public int top() {
return a.peek();
}
public int min() {
return b.peek();
}
}
剑指 Offer 32 - II. 从上到下打印二叉树 II
方法一:采用递归层序遍历
class Solution {
List<List<Integer>> li = new ArrayList();
public List<List<Integer>> levelOrder(TreeNode root) {
levelOrder(root,0);
return li;
}
public void levelOrder(TreeNode r,int k){
if(r != null){
if(li.size() <= k)li.add(new ArrayList());
li.get(k).add(r.val);
levelOrder(r.left,k+1);
levelOrder(r.right,k+1);
}
}
}
方法二:BFS
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> li = new ArrayList();
Queue<TreeNode> queue = new LinkedList<>();
if(root != null)queue.add(root);
while(!queue.isEmpty()){
List<Integer> tmp= new ArrayList();
for(int i=queue.size();i>0;i--){
TreeNode node = queue.poll();
tmp.add(node.val);
if(node.left != null)queue.add(node.left);
if(node.right != null)queue.add(node.right);
}
li.add(tmp);
}
return li;
}
}
剑指 Offer 39. 数组中出现次数超过一半的数字
啊这,因为出现次数超过一半,所以排序后中间那个数字一定就是答案
class Solution {
public int majorityElement(int[] nums) {
Arrays.sort(nums);
return nums[nums.length/2];
}
}
看见另一个方法,摩尔投票法,我愿称之为同归于尽法,两个数比较,如果不相同就同归于尽,最后留下了的那个数就是答案。
class Solution {
public int majorityElement(int[] nums) {
int res = 0,count = 0;
for(int i=0;i<nums.length;i++){
if(count == 0){
res = nums[i];
count++;
}
else{
if(res == nums[i])count++;
else count--;
}
}
return res;
}
}
剑指 Offer 40. 最小的k个数
啊这,用sort倒是很弱智简单,但复习学习一下堆.
用一个大根堆实时维护数组的前 k小值。首先将前 k 个数插入大根堆中,随后从第 k+1 个数开始遍历,如果当前遍历到的数比大根堆的堆顶的数要小,就把堆顶的数弹出,再插入当前遍历到的数。最后将大根堆里的数存入数组返回.
class Solution {
public int[] getLeastNumbers(int[] arr, int k) {
int[] res = new int[k];
if (k == 0) {
return res;
}
PriorityQueue<Integer> li = new PriorityQueue<Integer>(new Comparator<Integer>(){
public int compare(Integer a,Integer b){
return b-a;
}
});
for(int i=0;i<k;i++){
li.add(arr[i]);
}
for(int i=k;i<arr.length;i++){
if(arr[i] < li.peek()){
li.poll();
li.add(arr[i]);
}
}
for(int i=0;i<k;i++){
res[i] = li.poll();
}
return res;
}
}
剑指 Offer 42. 连续子数组的最大和
动态规划
dp 的最优子结构是什么,进而推出状态定义和转移方程。数组的子结构通常就是子数组,而本题的最优子结构容易想到是 子数组的连续最大和 ,然后为了保证转移方程的有效性,因此肯定是要以 nums[i] 为结尾的 。
class Solution {
public int maxSubArray(int[] nums) {
int[] dp = new int[nums.length];
dp[0] = nums[0];
int maxx = dp[0];
for(int i=1;i<nums.length;i++){
dp[i] = Math.max(dp[i-1]+nums[i],nums[i]);
maxx = Math.max(maxx,dp[i]);
}
return maxx;
}
}
剑指 Offer 50. 第一个只出现一次的字符
class Solution {
public char firstUniqChar(String s) {
char ans =' ';
int[] vis = new int[27];
for(char i : s.toCharArray()){
vis[i-'a']++;
}
for(char i : s.toCharArray()){
if(vis[i-'a'] == 1){
ans = i;
break;
}
}
return ans;
}
}
class Solution {
public char firstUniqChar(String s) {
for(int i=0;i<s.length();i++){
char ch = s.charAt(i);
if(s.indexOf(ch)==i && s.indexOf(ch,i+1)==-1){
return s.charAt(i);
}
}
return ' ';
}
}
剑指 Offer 52. 两个链表的第一个公共节点
神仙题解:使用双指针,使用两个指针 node1,node2 分别指向两个链表 headA,headB 的头结点,然后同时分别逐结点遍历,当 node1 到达链表 headA 的末尾时,重新定位到链表 headB 的头结点;当 node2 到达链表 headB 的末尾时,重新定位到链表 headA 的头结点。
不是c.next
而是c
,巧妙解决不相交的情况。
if(c != null)c=c.next;
else c=b;
if(d != null)d=d.next;
else d=a;
public class Solution {
public ListNode getIntersectionNode(ListNode a, ListNode b) {
ListNode c = a;
ListNode d = b;
while(c != d){
if(c != null)c=c.next;
else c=b;
if(d != null)d=d.next;
else d=a;
}
return c;
}
}
还可以通过集合把第一个链表的节点全部存放到集合set中,然后遍历第二个链表的每一个节点,判断在集合set中是否存在,如果存在就直接返回这个存在的结点。如果遍历完了,在集合set中还没找到,说明他们没有相交,直接返回null即可。
剑指 Offer 53 - I. 在排序数组中查找数字 I
排序数组中的搜索问题,首先想到 二分法 解决。
class Solution {
public int search(int[] nums, int target) {
int left = 0,right = nums.length-1;
int ans = 0;
while(left<right){
int mid = (left+right)/2;
if(nums[mid]>=target){
right = mid;
}
if(nums[mid] < target){
left = mid+1;
}
}
while(left < nums.length && nums[left++]==target){
ans++;
}
return ans;
}
}
剑指 Offer 53 - II. 0~n-1中缺失的数字
弱智行为大赏:(用这种思想不香吗,非要二分(
class Solution {
public int missingNumber(int[] nums) {
int sum = nums.length*(nums.length+1)/2;
for(int i=0;i<nums.length;i++){
sum-=nums[i];
}
return sum;
}
}
class Solution {
public int missingNumber(int[] nums) {
int left = 0,right = nums.length-1;
while(left <= right){
int mid = (left+right)/2;
if(nums[mid] == mid)left = mid+1;
else right = mid-1;
}
return left;
}
}
10.18
剑指 Offer 24. 反转链表
class Solution {
public ListNode reverseList(ListNode head) {
ListNode result = null;
while(head != null){
ListNode tmp = new ListNode(head.val);
tmp.next = result;
result = tmp;
head = head.next;
}
return result;
}
}
剑指 Offer 25. 合并两个排序的链表
递归:
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if(l1 == null)return l2;
if(l2 == null)return l1;
if(l1.val <= l2.val){
l1.next = mergeTwoLists(l1.next,l2);
return l1;
}else{
l2.next = mergeTwoLists(l1,l2.next);
return l2;
}
}
}
迭代
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode res = new ListNode(-1);
ListNode result = res;
while(l1 != null && l2 != null){
if(l1.val <= l2.val){
res.next = l1;
res = res.next;
l1 = l1.next;
}else{
res.next = l2;
res = res.next;
l2 = l2.next;
}
}
if(l1 == null){
res.next = l2;
}else{
res.next = l1;
}
return result.next;
}
}
剑指 Offer 65. 不用加减乘除做加法
位运算
如果是十进制的话,我们是如何完成加法计算的?
15 + 12 = ?
个位数和十位数的数字分别相加先不管进位的问题,
2 + 5 = 7;
1 + 1 = 2;
所以得到结果 27。
计算产生进位的数字 这里有进位吗?没有,那么就是0
把上面两步的结果进行相加:27 + 0 = 27;
99 + 111 = ?
个、十、百位 的数字分别相加先不管进位的问题:
个位:9 + 1 = 0
十位:9 + 1 = 0
百位:0 + 1 = 1
得到临时结果:100
计算进位的数字:
1 + 9 = 10;
10 + 90 = 100;
得到进位结果:110
相加得到结果
100 + 110 = 210如何用二进制完成以上的步骤?
问题1: 二进制的加法利用以上的步骤可以得到正确的结果吗?
12 二进制:1100
15 二进制:1111
各位置上的数字分别相加先不管进位的问题:
1100 + 1111 = 0011
得到临时二进制结果:0011
计算进位的数字:
0100 + 0100 = 1000
1000 + 1000= 10000
得到进位结果:11000
相加得到结果
0011 + 11000 = 11011(十进制:27)
问题2:第一步骤不用加法如何得到相同结果?异或
异或:相同为0,相异为1
1100 ^ 1111 = 0011
问题3:第二步骤不用加法如何得到相同结果?相与,左移一位
如果一个位置上的数字相遇能得到1 ,那么表示,位置上的数字都是1,然后在往左移动一位,就是步骤二 进位得到的结果
(1100 & 1111) << 1 = 11000
class Solution {
public int add(int a, int b) {
while(b != 0){
int c = (a&b)<<1;
a = a ^ b;
b = c;
}
return a;
}
}
剑指 Offer 59 - I. 滑动窗口的最大值
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
if(nums == null || nums.length == 0)return new int[0];
int[] ans = new int[nums.length-k+1];
LinkedList<Integer> list = new LinkedList<>();
int index = 0;
for(int i = 0;i<nums.length;i++){
while(!list.isEmpty() && nums[list.getLast()] < nums[i]){
list.removeLast();
}
list.offerLast(i);
if(i >= k-1){
if(list.getFirst() <= i-k){
list.removeFirst();
}
ans[index++] = nums[list.getFirst()];
}
}
return ans;
}
}
10.19
剑指 Offer 05. 替换空格
自己的弱智写法
class Solution {
public String replaceSpace(String s) {
String ans = "";
for(char i : s.toCharArray()){
if(i != ' '){
ans += i;
}
else{
ans += "%20";
}
}
return ans;
}
}
其他题解法:
class Solution {
public String replaceSpace(String s) {
char[] ch = new char[s.length()*3];
int index = 0;
for(int i=0;i<s.length();i++){
char j = s.charAt(i);
if(j == ' '){
ch[index++] = '%';
ch[index++] = '2';
ch[index++] = '0';
}else{
ch[index++] = j;
}
}
String ans = new String(ch,0,index);
// String ans = String.valueOf(ch,0,index);
//换成这个也行
return ans;
}
}
剑指 Offer 10- I. 斐波那契数列
递归超时了((
class Solution {
public int fib(int n) {
if(n == 0 || n == 1)return n;
int a = 0,b = 1;
for(int i = 2;i<= n;i++){
b = a + b;
a = b - a;
b %= 1000000007;
}
return b;
}
}
题解还有使用动态规划,dp[i]表示第i项的斐波那契数列的值
class Solution {
public int fib(int n) {
if(n == 0 || n == 1)return n;
int[] dp = new int[n+1];
dp[0] = 0;
dp[1] = 1;
for(int i = 2;i<=n;i++){
dp[i] = (dp[i-1] + dp[i-2])%1000000007;
}
return dp[n];
}
}
剑指 Offer 10- II. 青蛙跳台阶问题
有一说一,我恨dp
多少种可能性
的题目一般都有 递推性质
。
class Solution {
public int numWays(int n) {
if(n <= 1)return 1;
int[] dp = new int[n+1];
dp[0] = 1;
dp[1] = 1;
for(int i = 2;i<=n;i++){
dp[i] = (dp[i-1]+dp[i-2])%1000000007;
}
return dp[n];
}
}
剑指 Offer 11. 旋转数组的最小数字
class Solution {
public int minArray(int[] nums) {
int ans = nums[0];
for(int i = nums.length-1;i>=0;i--){
if(nums[i] <= ans){
ans = nums[i];
}
else{
break;
}
}
return ans;
}
}
看题解才发现又忘了可以用二分…
class Solution {
public int minArray(int[] nums) {
int l = 0,r = nums.length-1;
while(l < r){
int mid = ((r-l)>>1) + l;
if(nums[mid] < nums[r]){
r = mid;
}
else if(nums[mid] > nums[r]){
l = mid+1;
}
else{
r--;
}
}
return nums[l];
}
}
剑指 Offer 54. 二叉搜索树的第k大节点
中序遍历二叉搜索树是有有序的
class Solution {
ArrayList<Integer> li = new ArrayList<Integer>();
public int kthLargest(TreeNode root, int k) {
insertNode(root);
return li.get(li.size()-k);
}
public void insertNode(TreeNode root){
if(root == null)return;
if(root.left != null)insertNode(root.left);
li.add(root.val);
if(root.right != null)insertNode(root.right);
}
}
10.20
剑指 Offer 32 - I. 从上到下打印二叉树
List list = new ArrayList();
list不能直接转换为int[],因为list.toArray(指定数组)应该传入泛型参数,但泛型必须是引用类型,不能是基本类型。
List转换成int[]数组
class Solution {
public int[] levelOrder(TreeNode root) {
List<Integer> list = new ArrayList<Integer>();
Queue<TreeNode> queue = new LinkedList<>();
if(root != null)queue.add(root);
while(!queue.isEmpty()){
for(int i = queue.size()-1;i>=0;i--){
TreeNode node = queue.poll();
list.add(node.val);
if(node.left != null)queue.add(node.left);
if(node.right != null)queue.add(node.right);
}
}
int[] ans = new int[list.size()];
for(int i = 0;i<list.size();i++){
ans[i] = list.get(i);
}
return ans;
}
}
剑指 Offer 58 - II. 左旋转字符串
遍历拼接
class Solution {
public String reverseLeftWords(String s, int n) {
char[] r = new char[s.length()];
int index = 0;
for(int i = n;i < s.length();i++){
r[index++] = s.charAt(i);
}
for(int i = 0;i<n;i++){
r[index++] = s.charAt(i);
}
return String.valueOf(r);
}
}
求余运算
class Solution {
public String reverseLeftWords(String s, int n) {
StringBuilder ans = new StringBuilder();
for(int i = n;i<s.length()+n;i++){
ans.append(s.charAt(i%s.length()));
}
return ans.toString();
}
}
剑指 Offer 03. 数组中重复的数字
利用set的key唯一
class Solution {
public int findRepeatNumber(int[] nums) {
HashSet<Integer> set = new HashSet<>();
for(int i : nums){
if(set.contains(i))return i;
set.add(i);
}
return -1;
}
}
10.21
剑指 Offer 55 - I. 二叉树的深度
递归
class Solution {
public int maxDepth(TreeNode root) {
if(root == null)return 0;
int ans = serach(root,0);
return ans;
}
public int serach(TreeNode root,int k){
if(root == null)return 0;
int l =serach(root.right,k)+1;
int r = serach(root.left,k)+1;
if(l > r )return l;
else return r;
}
}
BFS,注意int n = queue.size();
, 不要放在循环条件里面,因为会改变!!
class Solution {
public int maxDepth(TreeNode root) {
int ans = 0;
Queue<TreeNode> queue = new LinkedList<>();
if(root != null)queue.add(root);
while(!queue.isEmpty()){
ans++;
int n = queue.size();
for(int i = 0;i<n;i++){
TreeNode node = queue.poll();
if(node.left != null)queue.add(node.left);
if(node.right != null)queue.add(node.right);
}
}
return ans;
}
}
剑指 Offer 55 - II. 平衡二叉树
判断该树是不是平衡二叉树:某二叉树中任意节点的左右子树的深度相差不超过1。
后序遍历 + 剪枝
。
class Solution {
public boolean isBalanced(TreeNode root) {
if(search(root) == -1)return false;
return true;
}
public int search(TreeNode root){
if(root == null)return 0;
int l = search(root.left);
if(l == -1)return-1;
int r = search(root.right);
if(r == -1)return -1;
if(Math.abs(l-r) <= 1)return Math.max(l,r)+1;
else return -1;
}
}
剑指 Offer 56 - I. 数组中数字出现的次数
class Solution {
public int[] singleNumbers(int[] nums) {
HashSet<Integer> set = new HashSet<Integer>();
for(int i : nums){
if(!set.contains(i)){
set.add(i);
}else{
set.remove(i);
}
}
Iterator<Integer> it = set.iterator();
int[] ans = new int[2];
int index = 0;
while(it.hasNext()){
ans[index++] = it.next();
}
return ans;
}
}
如果除了一个数字以外,其他数字都出现了两次,那么如何找到出现一次的数字?
全员进行异或
操作即可。
那么这一方法如何扩展到找出两个出现一次的数字呢?
如果我们可以把所有数字分成两组,使得:
- 两个只出现一次的数字在不同的组中;
- 相同的数字会被分到相同的组中。
那么对两个组分别进行异或操作,即可得到答案的两个数字。
class Solution {
public int[] singleNumbers(int[] nums) {
int ret = 0;
for (int n : nums) {
ret ^= n;
}
int div = 1;
while ((div & ret) == 0) {
div <<= 1;
}
int a = 0, b = 0;
for (int n : nums) {
if ((div & n) != 0) {
a ^= n;
} else {
b ^= n;
}
}
return new int[]{a, b};
}
}
10.22
剑指 Offer 22. 链表中倒数第k个节点
class Solution {
public ListNode getKthFromEnd(ListNode head, int k) {
ListNode frontNode = head,behindNode = head;
while(frontNode != null && k > 0){
frontNode = frontNode.next;
k--;
}
while(frontNode != null){
frontNode = frontNode.next;
behindNode = behindNode.next;
}
return behindNode;
}
}
剑指 Offer 26. 树的子结构
如果B为A的子结构,则B可能为A的任意一个节点
1.遍历A的每个节点
2.判断A中 以 NA为根节点的子树是否包含树 B
class Solution {
//遍历
public boolean isSubStructure(TreeNode A, TreeNode B) {
if(A == null || B == null)return false;
return dfs(A,B) || isSubStructure(A.left,B) || isSubStructure(A.right,B);
}
//判断是否为子结构
public boolean dfs(TreeNode A,TreeNode B){
if(B == null)return true;
if(A == null)return false;
if(A.val != B.val)return false;
boolean flag1 = dfs(A.left,B.left);
if(flag1 == false)return false;
boolean flag2 = dfs(A.right,B.right);
if(flag2 == false)return false;
return true;
}
}
10.23
剑指 Offer 17. 打印从1到最大的n位数
class Solution {
public int[] printNumbers(int n) {
int end = (int)Math.pow(10,n)-1;
int[] ans = new int[end];
int index = 0;
for(int i = 0;i<end;i++){
ans[i] = i+1;
}
return ans;
}
}
字符串模拟加法(大数)
public void printNumbers(int n) {
StringBuilder ans = new StringBuilder("");
/给每个位数赋值0
for(int i = 0;i<n;i++)ans.append("0");
/只要位数不超过N,就一直使用相加方法
while(addString(ans)){
int start = 0;/表示不为0的最高位
for(int i = 0;i< ans.length();i++){
if(ans.charAt(i) != '0'){
start = i;/找到就break
break;
}
}
/打印,从start到n
System.out.print(ans.substring(start,ans.length())+ " ");
}
}
public boolean addString(StringBuilder s){
int n = s.length();/n的长度
int stackover = 0;/某位上是否有来自上一位的进位
boolean flag = true;/表示位数还在N以内,没有超过范围
for(int i = n-1;i >= 0;i--){
/diff表示 此时位数上的那个数字 + 上一位的进位
int diff = s.charAt(i) - '0' + stackover;
if(i == n-1){
diff++;/如果此时的位数为个位,则diff++
}
/如果位数上的数字此时大于9,就需要进位
if(diff >= 10){
if(i == 0){/如果已经为最高位,说明已经到最后一个数字了
flag = false;
}
else{
stackover = 1;
s.setCharAt(i, (char) (diff - 10 +'0'));
}
}
/如果位数上的数字此时小于等于9,不需要进位,直接将此时数字替换成diff
else{
s.setCharAt(i, (char) (diff + '0'));
break;
}
}
return flag;
}
剑指 Offer 18. 删除链表的节点
class Solution {
public ListNode deleteNode(ListNode head, int val) {
ListNode h = head,pre = null;
if(head == null)return null;
if(head.val == val)return head.next;
while(h.val != val){
pre = h;
h = h.next;
}
pre.next = pre.next.next;
return head;
}
}
剑指 Offer 04. 二维数组中的查找
每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。
可以从右上角出发,此时往左(j–)走的数字都比此时的数小,往下(i++)走的数字都比此时的数大。【平衡二叉树】
class Solution {
public boolean findNumberIn2DArray(int[][] matrix, int target) {
if(matrix == null || matrix.length == 0)return false;
int i = 0,j = matrix[0].length -1,m =matrix.length,n = matrix[0].length;
while(i < m && j >= 0){
if(matrix[i][j] == target)return true;
else if(matrix[i][j] > target)j--;
else i++;
}
return false;
}
}
剑指 Offer 06. 从尾到头打印链表
链表遍历存值在list,然后反转list,利用流操作将list转换为int数组
//还可以利用栈
class Solution {
public int[] reversePrint(ListNode head) {
if(head == null)return new int[0];
ArrayList<Integer> list = new ArrayList<Integer>();
while(head != null){
list.add(head.val);
head = head.next;
}
Collections.reverse(list);
int[] ans = list.stream().mapToInt(Integer::valueOf).toArray();
return ans;
}
}
10.24
剑指 Offer 15. 二进制中1的个数
当n&1 = 1时,此时二进制形式中的n的最右位为1
利用这点累加答案,并循环将n右移一位(本题要求把数字 nn 看作无符号数,因此使用 无符号右移
操作, Java 中无符号右移为 “>>>” )
public class Solution {
// you need to treat n as an unsigned value
public int hammingWeight(int n) {
int ans = 0;
while(n != 0){
ans += n&1;
n = n>>> 1;
}
return ans;
}
}
剑指 Offer 16. 数值的整数次方
递归
class Solution {
public double myPow(double x, int n) {
if(n == 0)return 1;
if(n == 1)return x;
if(n == -1)return 1/x;
double half = myPow(x,n/2);
double mod = myPow(x,n%2);
return half * half *mod;
}
}
快速幂
class Solution {
public double myPow(double x, int n) {
if(n == 0)return 1;
if(n == 1)return x;
if(n == -1)return 1/x;
/一定要是long
long count = n;
if(count < 0){
x = 1/x;
count = -1 *count;
}
double ans = 1;
while(count > 0){
/指数为奇数时,将其变成偶数,就是把底数*x
/(3^5) = (3^4) * (3^1)
if(count%2 == 1){/相当于count & 1
ans = ans *x;
}
/相当于count >>= 1
count/= 2;/每一步都把指数分成两半,而相应的底数做平方运算
x = x*x;
}
return ans;
}
}
剑指 Offer 38. 字符串的排列
class Solution {
List<String> res = new ArrayList<>();
Deque<Character> p = new ArrayDeque<>();
public String[] permutation(String s) {
if(s == null || s.length() == 0)return new String[]{};
char[] array = s.toCharArray();
Arrays.sort(array);
boolean[] vis = new boolean[array.length];
dfs(array,0,vis);
String[] ans = new String[res.size()];
int k = 0;
for(String str : res){
ans[k++] = str;
}
return ans;
}
public void dfs(char[] ch,int deep,boolean[] vis){
if(deep == ch.length){
StringBuilder s = new StringBuilder();
for(Character c : p){
s.append(c);
}
res.add(s.toString());
return;
}
for(int i = 0;i<ch.length;i++){
if(!vis[i]){
if(i>0&&ch[i] == ch[i-1]&& !vis[i-1]){
continue;
}
p.addLast(ch[i]);
vis[i] = true;
dfs(ch,deep+1,vis);
p.removeLast();
vis[i] = false;
}
}
}
}
10.26
剑指 Offer 57 - II. 和为s的连续正数序列
只想到暴力枚举
class Solution {
public int[][] findContinuousSequence(int target) {
ArrayList<int[]> res = new ArrayList<>();
int i = 1,j = 1;
int k = target/2 + 1;
int sum = 1;
while(i <= k){
if(sum < target){
j++;
sum+=j;
}else if(sum > target){
sum-=i;
i++;
}else{
int[] nums = new int[j-i+1];
for(int x = i,y = 0;x <= j ;x++){
nums[y++] = x;
}
res.add(nums);
sum-=i;
i++;
}
}
return res.toArray(new int[0][]);
}
}
可以用双指针法
class Solution {
public int[][] findContinuousSequence(int target) {
ArrayList<int[]> res = new ArrayList<>();
int sum = 0;
for(int i = 1,j = 2;i < j ;){
sum = (i + j) * (j - i+1) / 2;
if(sum == target){
int[] nums = new int[j-i+1];
for(int x = i,y = 0 ;x <= j;x++){
nums[y++] = x;
}
res.add(nums);
i++;
}else if(sum > target){
i++;
}else{
j++;
}
}
return res.toArray(new int[0][]);
}
}
剑指 Offer 64. 求1+2+…+n
利用and的短路特性(代替if)和递归(代替for)
class Solution {
public int sumNums(int n) {
boolean flag = n >0 && (n += sumNums(n-1)) > 0;
return n;
}
}
剑指 Offer 35. 复杂链表的复制
class Solution {
public Node copyRandomList(Node head) {
if(head == null)return null;
Node cur = head;
/将拷贝节点放到原节点后面,例如1->2->3这样的链表就变成了这样1->1'->2->2'->3->3'
while(cur != null){
Node copyNode = new Node(cur.val);
copyNode.next = cur.next;
cur.next = copyNode;
cur = cur.next.next;
}
cur = head;
/把拷贝节点的random指针安排上
while(cur != null){
if(cur.random != null){
cur.next.random = cur.random.next;
}
cur = cur.next.next;
}
Node copyHead = head.next;
cur = head;
Node curCopy = head.next;
/分离拷贝节点和原节点,变成1->2->3和1'->2'->3'两个链表,后者就是答案
while(cur != null){
cur.next = cur.next.next;
cur = cur.next;
if(curCopy.next != null){
curCopy.next = curCopy.next.next;
curCopy = curCopy.next;
}
}
return copyHead;
}
}
剑指 Offer 56 - II. 数组中数字出现的次数 II
如果一个数字出现三次,那么它的二进制表示的每一位(0或者1)也出现三次。如果把所有出现三次的数字的二进制表示的每一位都分别加起来,那么每一位的和都能被3整除。如果某一位的和能被3整除,那么那个只出现一次的数字二进制表示中对应的那一位是0;否则就是1;
class Solution {
public int singleNumber(int[] nums) {
if(nums.length == 0)return -1;
int[] bitSum = new int[32];/java int类型有32位,其中首位为符号位
int res = 0;
for(int num : nums){
int mask = 1;
for(int i = 31 ; i >= 0 ;i--){
/这里同样可以通过num的无符号右移>>>来实现
/否则带符号右移(>>)左侧会补符号位,对于负数会出错。
if((mask&num) != 0)bitSum[i]++;
mask <<= 1;/左移没有无符号、带符号的区别,都是在右侧补0
}
}
for(int i = 0 ;i<32; i++){
res <<= 1;
res+=bitSum[i]%3;/这两步顺序不能变,否则最后一步会多左移一次
}
return res;
}
}
剑指 Offer 57. 和为s的两个数字
看见有序第一反应用二分,结果写成双指针…
class Solution {
public int[] twoSum(int[] nums, int target) {
int i = 0,j = nums.length-1;
while(i < j){
int sum = nums[i]+nums[j];
if(sum == target)return new int[]{nums[i],nums[j]};
else if(sum > target)j--;
else i++;
}
return new int[]{};
}
}
二分法:遍历我们每一个数,然后在后面的数中二分查找对应的差
for(int i = 0;i<nums.length;++i){
int left = i + 1,right = nums.length-1,e =target - nums[i];
while(left <= right){
int mid = left + (right - left)/2;
if(nums[mid] == e){
return new int[]{nums[i],nums[mid]};
}else if(nums[mid] > e){
right = mid -1;
}else if(nums[mid] < e){
left = mid + 1;
}
}
}
return new int[]{};
10.27
剑指 Offer 07. 重建二叉树[已知前序和中序建树
建树无论是前后建树,或者中后建树,要点都是传递中的序列下标,因为根结点下标都可以根据中进行推算。
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
int n = preorder.length;
if(n == 0)return null;
int rootVal = preorder[0],rootIndex = 0;
for(int i = 0;i<n;i++){
if(inorder[i] == rootVal){
rootIndex = i;
break;
}
}
TreeNode node = new TreeNode(rootVal);
node.left = buildTree(Arrays.copyOfRange(preorder,1,1+rootIndex),Arrays.copyOfRange(inorder,0,rootIndex));
node.right = buildTree(Arrays.copyOfRange(preorder,1+rootIndex,n),Arrays.copyOfRange(inorder,rootIndex+1,n));
return node;
}
}
106. 从中序与后序遍历序列构造二叉树^1.28
和上面的差不多,只不过换成根结点一定是后续遍历的最后一个节点
class Solution {
public TreeNode buildTree(int[] inorder, int[] postorder) {
int n = inorder.length;
if(n == 0)return null;
int rootVal = postorder[n-1];
int rootIndex = n-1;
for(int i = 0 ; i < n ; i++){
if(inorder[i] == rootVal){
rootIndex = i;
break;
}
}
TreeNode root = new TreeNode(rootVal);
root.left = buildTree(Arrays.copyOfRange(inorder,0,rootIndex),Arrays.copyOfRange(postorder,0,rootIndex));
root.right = buildTree(Arrays.copyOfRange(inorder,rootIndex+1,n),Arrays.copyOfRange(postorder,rootIndex,n-1));
return root;
}
}
剑指 Offer 12. 矩阵中的路径
class Solution {
int[][] dir = new int[][]{{0,1},{0,-1},{1,0},{-1,0}};
public boolean exist(char[][] board, String word) {
char[] sChar = word.toCharArray();
boolean[][] vis = new boolean[board.length][board[0].length];
for(int i = 0;i < board.length ;i++){
for(int j = 0;j<board[0].length;j++){
if(board[i][j] == sChar[0]){
if(bfs(vis,board,sChar,i,j,0)){
return true;
}
}
}
}
return false;
}
public boolean bfs(boolean[][] vis,char[][] board,char[] word,int x,int y,int k){
if(k == word.length){
return true;
}
if(x < 0 || x >= board.length || y < 0 || y >= board[0].length || vis[x][y] || board[x][y] != word[k]){
return false;
}
vis[x][y] = true;
boolean flag = false;
for(int i=0;i<4;i++){
int tx = x + dir[i][0];
int ty = y + dir[i][1];
flag = flag || bfs(vis,board,word,tx,ty,k+1);
}
vis[x][y] = false;
return flag;
}
}
剑指 Offer 13. 机器人的运动范围
class Solution {
int[][] dir = new int[][]{{0,1},{0,-1},{1,0},{-1,0}};
public int movingCount(int n, int m, int k) {
boolean[][] vis = new boolean[n][m];
int sum = bfs(n,m,k,0,0,vis);
return sum;
}
public int bfs(int n,int m,int k,int x,int y,boolean[][] vis){
if(x < 0 ||y < 0 || x >= n || y >= m || vis[x][y] || !add(x,y,k)){
return 0;
}
int flag = 1;
vis[x][y] = true;
for(int i = 0;i<4;i++){
int tx = x + dir[i][0];
int ty = y + dir[i][1];
flag += bfs(n,m,k,tx,ty,vis);
}
return flag;
}
public boolean add(int x,int y,int k){
int sum = 0;
while(x != 0){
sum += x%10;
x /=10;
}
if(sum > k)return false;
while(y != 0){
sum += y%10;
y /=10;
}
if(sum > k)return false;
return true;
}
}
10.28
剑指 Offer 47. 礼物的最大价值
每次只有两种选择:向右或者向下
动态规划:dp[i][j] = max(dp[i-1][j],dp[i][j-1])+value[i][j];
f(i,j)等于 f(i,j-1) f(i-1,j)中的较大值加上当前单元格礼物价值grid(i,j) 。
可以化简为dp[j] = Math.max(dp[j], dp[j - 1]) + grid[i - 1][j - 1];
右边的dp[j]是上一行遍历时候记录下的dp[j]也就是目前遍历到的这个位置的上面的记录,dp[j-1]就是左边的已经更新的记录,其实就是取左边和上面中的最大值,这样一行行遍历下去,对应最后的dp[n]就是遍历到最后一个位置。
class Solution {
public int maxValue(int[][] g) {
int n = g.length,m = g[0].length;
int[] dp = new int[m+1];
for(int i = 1 ;i <= n;i++){
for(int j = 1;j <= m;j++){
dp[j] = Math.max(dp[j],dp[j-1])+g[i-1][j-1];
}
}
return dp[m];
}
}
剑指 Offer 48. 最长不含重复字符的子字符串
维护一个滑动窗口,并利用hashset的特性
class Solution {
public int lengthOfLongestSubstring(String s) {
int res = 0;
Set<Character> set = new HashSet<>();
for(int l = 0,r = 0;r < s.length();r++){
char c = s.charAt(r);
while(set.contains(c)){
set.remove(s.charAt(l));
l++;
}
set.add(c);
res = Math.max(res,r-l+1);
}
return res;
}
}
剑指 Offer 49. 丑数
第一个丑数是1,以后的丑数都是基于前面的小丑数分别乘2,3,5构成的。
我们每次添加进去一个当前计算出来个三个丑数的最小的一个,并且是谁计算的,谁指针就后移一位.
//虽然代码用了dp数组,但是和动规没关系
class Solution {
public int nthUglyNumber(int n) {
if(n <= 0)return -1;
int[] dp = new int[n];
dp[0] = 1;
int k2 = 0,k3 = 0,k5 = 0;
for(int i = 1 ; i < n ;i++){
dp[i] = Math.min(dp[k2]*2,Math.min(dp[k3]*3,dp[k5]*5));
if(dp[k2]*2 == dp[i]){
k2++;
}
if(dp[k3]*3 == dp[i]){
k3++;
}
if(dp[k5]*5 == dp[i]){
k5++;
}
}
return dp[n-1];
}
}
剑指 Offer 63. 股票的最大利润
弱智逻辑写法
class Solution {
public int maxProfit(int[] prices) {
int n = prices.length;
if(prices == null || n == 0)return 0;
int res = 0;
int min = prices[0];
for(int i = 0 ;i <n;i++){
if(prices[i] < min){
min = prices[i];
}else{
res = Math.max(res,prices[i]-min);
}
}
return res;
}
}
动态规划
前i日最大利润=max(前(i−1)日最大利润,第i日价格−前i日最低价格)
.
class Solution {
public int maxProfit(int[] prices) {
int n = prices.length;
if(n == 0)return 0;
int[] dp = new int[n+1];
int minn = prices[0];
for(int i = 1;i < n ;i++){
minn = Math.min(minn,prices[i]);
dp[i] = Math.max(dp[i-1],prices[i]-minn);
}
return dp[n-1];
}
}
10.30
剑指 Offer 45. 把数组排成最小的数
class Solution {
public String minNumber(int[] nums) {
if(nums.length == 0)return null;
List<String> list = new ArrayList<>();
for(int i : nums){
list.add(String.valueOf(i));
}
Collections.sort(list,new Comparator<String>(){
public int compare(String a,String b){
String ab = a+b;
String ba = b+a;
return ab.compareTo(ba);
}
});
StringBuilder sb = new StringBuilder();
for(String s : list){
sb.append(s);
}
return sb.toString();
}
}
剑指 Offer 21. 调整数组顺序使奇数位于偶数前面
快排
class Solution {
public int[] exchange(int[] nums) {
int l = 0;
int r = nums.length-1;
while(l < r){
while(l < r && nums[l]%2 != 0){
l++;
}
while(l < r && nums[r]%2 == 0){
r--;
}
if(l < r){
int t = nums[l];
nums[l] = nums[r];
nums[r] = t;
}
}
return nums;
}
}
剑指 Offer 58 - I. 翻转单词顺序
class Solution {
public String reverseWords(String s) {
String[] str = s.trim().split(" ");
StringBuilder sb = new StringBuilder();
for(int i = str.length-1;i>=0;i--){
if(str[i].equals(" ")||str[i].equals(""))continue;
sb.append(str[i]);
sb.append(" ");
}
return sb.toString().trim();
}
}
class Solution {
public String reverseWords(String s) {
s = s.trim();
StringBuilder sb = new StringBuilder();
int i = s.length()-1;
int j = i;
while(i >= 0){
while(i >= 0 && s.charAt(i) != ' ')i--;
sb.append(s.substring(i+1,j+1)+" ");
while(i >= 0 && s.charAt(i) == ' ')i--;
j = i;
}
return sb.toString().trim();
}
}
剑指 Offer 61. 扑克牌中的顺子
class Solution {
public boolean isStraight(int[] nums) {
Arrays.sort(nums);
int k = 0;
for(int i=0;i<4;i++){
if(nums[i]==0){
k ++;
continue;
}
if(nums[i] == nums[i+1])return false;
}
if(nums[4] == 0)k++;
return nums[4]-nums[k] <= 4;
}
}
剑指 Offer 62. 圆圈中最后剩下的数字
链表模拟
class Solution {
public int lastRemaining(int n, int m) {
if (n == 0 || m == 0)
return -1;
List<Integer> list = new ArrayList<>();
for (int i = 0; i < n; i++)
list.add(i);
int c = (m - 1) % n;
while (list.size() != 1) {
list.remove(c);
c = (c + m - 1) % list.size();
}
return list.get(0);
}
}
或者:
class Solution {
public int lastRemaining(int n, int m) {
int flag = 0; //迭代
for (int i = 2; i <= n; i++) {
flag = (flag + m) % i;
//动态规划的思想,将f(n,m)替换成flag存储
}
return flag;
}
}
10.31
剑指 Offer 60. n个骰子的点数
竟然用动规…这谁顶得住
class Solution {
public double[] twoSum(int n) {
/定义一个数组保存所有状态,一维是第n次时,二维为第n次时所有可能情况
int[][] dp = new int[n+1][6*n+1];
for(int i = 1 ; i <= 6; i++){
dp[1][i] = 1;/我们只能知道第一次的情况,也是边界条件
}
/一层循环,每多一个色子的情况
for(int i = 2 ; i <= n ;i++){
/二层循环是这么多色子情况下的所有可能和
for(int j = i ;j <= 6*i;j++){
for(int k = 1 ; k<=6 &&k<=j;k++){
dp[i][j] += dp[i-1][j-k];
}
}
}
double[] ans = new double[6*n - n +1];
double sum = Math.pow(6,n);
for(int i = n;i<=6*n;i++){
ans[i-n] = dp[n][i]*1.0/sum;
}
return ans;
}
}
剑指 Offer 36. 二叉搜索树与双向链表
中序遍历
class Solution {
Node pre,head;
public Node treeToDoublyList(Node root) {
if(root == null)return null;
dfs(root);
head.left = pre;
pre.right = head;
return head;
}
public void dfs(Node cur){
if(cur == null)return;
dfs(cur.left);
if(pre == null)head = cur;
else pre.right = cur;
cur.left = pre;
pre = cur;
dfs(cur.right);
}
}
剑指 Offer 44. 数字序列中某一位的数字
我讨厌数学问题
class Solution {
public int findNthDigit(int n) {
int digit = 1;
long start = 1;
long count = 9;
while(n > count){
n -= count;
digit++;
start*=10;
count = digit*start*9;
}
long num = start + (n-1)/digit;
return Long.toString(num).charAt((n-1)%digit)-'0';
}
}
11.1
剑指 Offer 09. 用两个栈实现队列
class CQueue {
LinkedList<Integer> stack1;
LinkedList<Integer> stack2;
public CQueue() {
stack1 = new LinkedList<>();
stack2 = new LinkedList<>();
}
public void appendTail(int value) {
stack1.add(value);
}
public int deleteHead() {
if (stack2.isEmpty()) {
if (stack1.isEmpty()) return -1;
while (!stack1.isEmpty()) {
stack2.add(stack1.pop());
}
return stack2.pop();
} else return stack2.pop();
}
}
剑指 Offer 31. 栈的压入、弹出序列
class Solution {
public boolean validateStackSequences(int[] pushed, int[] popped) {
Deque<Integer> stack = new ArrayDeque<>();
int i = 0;
for(int j : pushed){
stack.push(j);
while(i < popped.length && !stack.isEmpty() && stack.peek() == popped[i]){
stack.pop();
i++;
}
}
return i == popped.length;
}
}
剑指 Offer 32 - III. 从上到下打印二叉树 III
class Solution {
List<List<Integer>> list = new ArrayList<>();
public List<List<Integer>> levelOrder(TreeNode root) {
level(root,1);
return list;
}
public void level(TreeNode root,int k){
if(root != null){
if(list.size() < k)list.add(new ArrayList());
if(k%2 == 0){
list.get(k-1).add(0,root.val);
}else{
list.get(k-1).add(root.val);
}
level(root.left,k+1);
level(root.right,k+1);
}
}
}
剑指 Offer 34. 二叉树中和为某一值的路径
class Solution {
List<List<Integer>> list = new ArrayList<>();
public List<List<Integer>> pathSum(TreeNode root, int sum) {
dfs(root,sum,new ArrayList<>());
return list;
}
public void dfs(TreeNode root,int sum,List<Integer> li){
if(root == null)return;
li.add(root.val);
sum -= root.val;
if(sum == 0 && root.left == null && root.right == null){
list.add(new ArrayList<>(li));
}else{
dfs(root.left,sum,li);
dfs(root.right,sum,li);
}
li.remove(li.size()-1);
}
}
11.2
剑指 Offer 66. 构建乘积数组
class Solution {
public int[] constructArr(int[] a) {
int len = a.length;
int[] b = new int[len];
for(int i = 0,x = 1;i < len ;i++){
b[i] = x;
x *= a[i];
}
for(int i = len-1,x=1;i>=0;i--){
b[i] *= x;
x *= a[i];
}
return b;
}
}
剑指 Offer 67. 把字符串转换成整数
class Solution {
public int strToInt(String s) {
s = s.trim();
if(s.length() == 0)return 0;
char[] str = s.toCharArray();
if(!Character.isDigit(str[0]) && str[0]!='+' && str[0]!='-'){
return 0;
}
int i = 0;
boolean negative = false;
if(str[i]=='-'){
negative = true;
i++;
}
else if(str[i] == '+'){
i++;
}
long res = 0;
while(i < str.length && Character.isDigit(str[i])){
res = res*10 + str[i]-'0';
if(negative == false && res > Integer.MAX_VALUE)return Integer.MAX_VALUE;
else if(negative == true && -res < Integer.MIN_VALUE)return Integer.MIN_VALUE;
i++;
}
return negative?-(int)res:(int)res;
}
}
剑指 Offer 14- I. 剪绳子
class Solution {
public int cuttingRope(int n) {
if(n <= 3)return n-1;
int[] dp = new int[n+1];
dp[1] = 1;
dp[2] = 2;
dp[3] = 3;
for(int i = 4 ;i <= n;i++){
for(int j = 1 ;j <= i/2;j++){
dp[i] = Math.max(dp[i],dp[i-j]*dp[j]);
}
}
return dp[n];
}
}
剑指 Offer 33. 二叉搜索树的后序遍历序列
递归
class Solution {
public boolean verifyPostorder(int[] postorder) {
if(postorder.length < 2)return true;
return order(postorder,0,postorder.length-1);
}
public boolean order(int[] postorder,int left,int right){
if(left >= right)return true;
int root = postorder[right];
int k = left;
while(k < right && postorder[k] < root){
k++;
}
for(int i = k;i < right ;i++){
if(postorder[i] < root)return false;
}
if(!order(postorder,left,k-1))return false;
if(!order(postorder,k,right-1))return false;
return true;
}
}
倒着遍历后续遍历得到的数组
class Solution {
public boolean verifyPostorder(int[] postorder) {
if(postorder.length < 2)return true;
Deque<Integer> stack = new LinkedList<>();
int root = Integer.MAX_VALUE;
for(int i = postorder.length-1 ; i>= 0;i--){
if(postorder[i] > root)return false;
while(!stack.isEmpty() && postorder[i] < stack.peek()){
root = stack.pop();
}
stack.push(postorder[i]);
}
return true;
}
}
11.3
剑指 Offer 59 - II. 队列的最大值
class MaxQueue {
private Deque<Integer> queue;
private Deque<Integer> help;
public MaxQueue() {
queue = new ArrayDeque<>();
help = new ArrayDeque<>();
}
public int max_value() {
return queue.isEmpty() ? -1 : help.peek();
}
public void push_back(int value) {
queue.offer(value);
while(!help.isEmpty() && value > help.peekLast()){
help.pollLast();
}
help.offer(value);
}
public int pop_front() {
if(queue.isEmpty())return -1;
int val = queue.pop();
if(help.peek() == val){
help.pop();
}
return val;
}
}
剑指 Offer 41. 数据流中的中位数
11.16
剑指 Offer 37. 序列化二叉树
题目意思是随便你用什么方式,总之把它变成字符串,递归前序遍历也好,层序遍历也随便
public class Codec {
// Encodes a tree to a single string.
public String serialize(TreeNode root) {
if(root == null){
return "null,";
}
String res = root.val + ",";
res += serialize(root.left);
res += serialize(root.right);
return res;
}
// Decodes your encoded data to tree.
public TreeNode deserialize(String data) {
String[] arr = data.split(",");
Queue<String> queue = new LinkedList<String>();
for(int i = 0 ;i <arr.length ; i++){
queue.offer(arr[i]);
}
return dfs(queue);
}
public TreeNode dfs(Queue<String> q){
String val = q.poll();
if(val.equals("null")){
return null;
}
TreeNode r = new TreeNode(Integer.valueOf(val));
r.left = dfs(q);
r.right = dfs(q);
return r;
}
}
剑指 Offer 41. 数据流中的中位数
维持一个大顶堆和小顶堆,确保:
1、大小顶堆元素数量差小于等于1
2、大顶堆中所有元素均小于小顶堆中元素
class MedianFinder {
Queue<Integer> a,b;
/** initialize your data structure here. */
public MedianFinder() {
a = new PriorityQueue<Integer>();// 小顶堆,保存较大的一半
b = new PriorityQueue<Integer>((x,y)->(y-x));
}
public void addNum(int num) {
if(a.size() != b.size()){
a.add(num);
b.add(a.poll());
}
else{
b.add(num);
a.add(b.poll());
}
}
public double findMedian() {
if(a.size() == b.size()){
return (a.peek()+b.peek())*1.0/2.0;
}
else{
return a.peek();
}
}
}