正在更新中。。。。。。。。。

剑指offer --Python版本的实现:

剑指offer(1/3)第一大部分

剑指offer(2/3)第二大部分

剑指offer(3/3)第三大部分

----------------------------------------------------

《剑指offer》Java版本实现:

《剑指offer》Java版本-第一天

《剑指offer》Java版本-第二天

《剑指offer》Java版本-第三天

46.圆圈后最后剩下额数

每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0...m-1报数....这样下去....直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!^_^)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)

如果没有小朋友,请返回-1

1.  使用模仿小朋友玩游戏的过程

我们可以用链表模拟约瑟夫环的过程。当模拟到人数等于1的时候,输出剩下的人的序号即可。

import java.util.LinkedList;
import java.util.List;
public class Solution {public int LastRemaining_Solution(int n, int m) {if (n <= 0 || n < 0) return -1;int index = -1; // 由于是m-1个小朋友出列,所以index要先减去1 List<Integer> array = new LinkedList();for (int i = 0; i < n; i++){array.add(i);}while (array.size() != 1){index = (m + index) % array.size(); //这里array的size会更新array.remove(index);index -= 1;}return array.get(0);}
}

2. 约瑟夫环

该方法的核心在于:只关心最终活着的那个人的序号变化情况,从最终结果反推出规律

例如一共有 n=8 个人,每当数到 m=3 的时候就将该人给删除,我们用 F(n, m) 来表示最后剩下的那个人的序号。整个流程如下图所示:

从上图可以看到,我们将这 8 个人用字母代替,在每一轮中,红色的方格表示当前需要被删除的人,绿色的方格表示最终存活下来的人。N=8 时,将 C 删除,则下一轮(N=7)将会从 D 开始报数,然后往右数 m=3 个数,再将 F 删除,以此类推。

此外,注意观察每一轮中最后活下来的 G 的索引变化情况。当最后只剩下一个人的时候,这个人的编号(索引号)一定是 0。

接下来我们看看如何进行反推,例如如何从 N=7 反推出 N=8 时的情况,如下图所示:

在从 N=7 回到 N=8 的过程中,先将被删除的 C 补充回去,即黄色方格;然后将 N=7 右移 m 个单位,由于多出 3 个人(A、B、C)越界了,因此将它们移到前面;最后得到的结果就和 N=8 一样了。

因此,从 N=7 到 N=8 的递推公式为:f(8,3)=[f(7,3)+3]%8

而将此公式推广到一般情况,则有:f(N,m)=[f(N−1,3)+3]%N

最后整理得到的递推公式:

当n=1时,f(N,m)=0

当n>1时,f(N,m)=[f(N−1,m)+m]%N

public class Solution {public int LastRemaining_Solution(int n, int m) {if (n <= 0 || n < 0) return -1;int remainIndex = 0;for (int i = 2; i<= n; i++){ // 约瑟夫环remainIndex = (remainIndex + m) % i;}return remainIndex;}
}

47.求1,2.。。n的和

求1+2+3+...+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。

使用递归方法:

解题思路:

1.需利用逻辑与的短路特性实现递归终止。

2.当n==0时,(n>0)&&((sum+=Sum_Solution(n-1))>0)只执行前面的判断,为false,然后直接返回0

这里可以实现不使用 if 判断

3.当n>0时,执行sum+=Sum_Solution(n-1),实现递归计算Sum_Solution(n)。

public class Solution {public int Sum_Solution(int n) {int sum = n;boolean ans = (n > 0)&& ((sum += Sum_Solution(n-1))>0);return sum;}
}

48.不用加减乘除做相加

写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。

使用 ^ 和 & 将两个整数相加

  1. 两个数异或:相当于每一位相加,而不考虑进位;
  2. 两个数相与,并左移一位:相当于求得进位;

13+11 = ?;

13 的二进制      1 1 0 1                     -----a        13

11 的二进制      1 0 1 1                     -----b        11

(a&b) <<1  ->   1 0 0 1 0                         -----d         18

a^b  ->     0 1 1 0                   -----e          6

(d&e) <<1  ->   0 0 1 0 0                       ------f         4

d^e  ->  1 0 1 0 0                  -----g        20

(f&g) <<1  ->   0 1 0 0 0                       ------h        8

f^g  ->  1 0 0 0 0                   ------i           16

(h&i) <<1  ->   0 0 0 0 0                       ------h        0       ---- -------- 没有进位了, 则退出循环

h^i  ->  1 1 0 0 0                  ------i           24

public class Solution {public int Add(int num1,int num2) {if (num1 == 0) return num2;if (num2 == 0) return num1;while (num2 != 0){int carry = num1 & num2;num1 = num1 ^ num2; // 异或,两者相加,但是不进位num2 = carry << 1;  // 相与,左移,相当于进位}return num1;}
}

49.将字符串转换成整数

将一个字符串转换成一个整数(实现Integer.valueOf(string)的功能,但是string不符合数字要求时返回0),要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0。

输入描述:

输入一个字符串,包括数字字母符号,可以为空

输出描述:

如果是合法的数值表达则返回该数字,否则返回0

示例1

输入

+2147483647
1a33

输出

2147483647
0
public class Solution {public int StrToInt(String str) {int size = str.length();if (str.equals("+") && size==1) return 0;if (str.equals("-") && size == 1) return 0;if (size == 0) return 0;int sum = 0;for (int i = 0; i < size; i++){if (str.charAt(i) == '-'){continue;}else if (str.charAt(i) == '+'){continue;}else if (str.charAt(i) >= '0' && str.charAt(i) <= '9'){sum = sum * 10 + str.charAt(i) - '0';}else{return 0;}}return str.charAt(0) == '-' ? -sum : sum; // 判断第一个符号是否是 + -}
}

50.数组中重复的数字

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

例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。

1.使用类似哈希表的方法:

public class Solution {// Parameters://    numbers:     an array of integers//    length:      the length of array numbers//    duplication: (Output) the duplicated number in the array number,length of duplication array is 1,so using duplication[0] = ? in implementation;//                  Here duplication like pointor in C/C++, duplication[0] equal *duplication in C/C++//    这里要特别注意~返回任意重复的一个,赋值duplication[0]// Return value:       true if the input is valid, and there are some duplications in the array number//                     otherwise falsepublic boolean duplicate(int numbers[],int length,int [] duplication) {//由于: 一个长度为n的数组里的所有数字都在0到n-1的范围内boolean[] sign = new boolean[length];for (int i = 0; i < length; i++){if (sign[numbers[i]] == true){duplication[0] = numbers[i];return true;}sign[numbers[i]] = true;}return false;}
}

2.使用哈希表

import java.util.HashMap;
public class Solution {// Parameters://    numbers:     an array of integers//    length:      the length of array numbers//    duplication: (Output) the duplicated number in the array number,length of duplication array is 1,so using duplication[0] = ? in implementation;//                  Here duplication like pointor in C/C++, duplication[0] equal *duplication in C/C++//    这里要特别注意~返回任意重复的一个,赋值duplication[0]// Return value:       true if the input is valid, and there are some duplications in the array number//                     otherwise falsepublic boolean duplicate(int numbers[],int length,int [] duplication) {if (length == 0) return false;HashMap<Integer, Integer> map = new HashMap();for (int i = 0; i< length; i++){  // 计算数组中每个元素的数量if (map.containsKey(numbers[i])){map.put(numbers[i], map.get(numbers[i])+1);}else{map.put(numbers[i], 1);}}for (int i= 0; i < length; i++){ // 找出value值大于1 的key值if (map.get(numbers[i]) > 1){duplication[0] = numbers[i];return true;}}return false;}
}

51.构建乘积数组

给定一个数组A[0,1,...,n-1],请构建一个数组B[0,1,...,n-1],其中B中的元素B[i]=A[0]*A[1]*...*A[i-1]*A[i+1]*...*A[n-1]。不能使用除法。

(注意:规定B[0] = A[1] * A[2] * ... * A[n-1],B[n-1] = A[0] * A[1] * ... * A[n-2];)

对于A长度为1的情况,B无意义,故而无法构建,因此该情况不会存在。

剑指的思路:

B[i]的值可以看作下图的矩阵中每行的乘积。

下三角用连乘可以很容求得,上三角,从下向上也是连乘。

因此我们的思路就很清晰了,先算下三角中的连乘,即我们先算出B[i]中的一部分,然后倒过来按上三角中的分布规律,把另一部分也乘进去。

import java.util.ArrayList;
public class Solution {public int[] multiply(int[] A) {int size = A.length;int[] B = new int[size];if (size == 0) return B;// 计算下三角的数值B[0] = 1;for (int i = 1; i< size; i++){B[i] = B[i-1] * A[i-1];}// 计算上三角的数值,与下三角的数值相乘int temp = 1;for (int j = size-2; j >= 0; j--){temp *= A[j+1];B[j] *= temp;}return B;}
}

52.正则表达式匹配

请实现一个函数用来匹配包括'.'和'*'的正则表达式。模式中的字符'.'表示任意一个字符,而'*'表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。

例如,字符串"aaa"与模式"a.a"和"ab*ac*a"匹配,但是与"aa.a"和"ab*a"均不匹配

逻辑判断的流程:

1.  s, pattern都为空

2. s不为空,pattern为空

3. s为空, pattern不为空

3.1 如果pattern[1] == '*'(前提肯定要满足 len(pattern) > 1)

4. s不为空,pattern不为空

4.1 如果 pattern[1] == '*'(前提肯定要满足 len(pattern) > 1)

4.1.1 在pattern[1] == '*'前提下,如果s和pattern第一个字符不相同时

4.1.2 在pattern[1] == '*'前提下, 如果s和pattern第一个字符相同时:

3 种情况------

4.2 否则(如果 pattern[1] != '*'):

4.2.1 如果s和pattern第一个字符相同,True

4.2.2 否则,False

public class Solution {public boolean match(char[] str, char[] pattern){if (str == null || pattern == null) return false;return matchHelper(str, 0, pattern, 0);}private boolean matchHelper(char[] str, int sIndex, char[] pattern, int pIndex){// 如果两者为空,则返回trueif(sIndex == str.length && pIndex == pattern.length) return true;// 如果 str 不为空,p为空,则不能匹配if (sIndex != str.length && pIndex == pattern.length) return false;// 如果str为空,但p不为空if (sIndex == str.length && pIndex != pattern.length){// 此时 * 号刚好抵消,继续递归判断pattern后面的字符if (pIndex + 1 < pattern.length && pattern[pIndex+1] == '*'){return matchHelper(str, sIndex, pattern, pIndex+2);}else{return false;}}// 如果两者都不为空的情况下if (pIndex + 1 < pattern.length && pattern[pIndex+1] == '*'){// 如果第一个字符相同,且pattern第一个字符为 ‘*’if (pattern[pIndex] == str[sIndex] || (pattern[pIndex] == '.' && sIndex != str.length)){return matchHelper(str, sIndex, pattern, pIndex+2)  // s ='bcd', pattern = 'a*bcd', 相当于 * 为0, 不进行匹配|| matchHelper(str, sIndex+1, pattern, pIndex+2) //s = 'abcd'. pattern = 'a*bcd"  匹配成功了 1 位|| matchHelper(str, sIndex+1, pattern, pIndex); // s = 'aaaaabcd'. pattern = 'a*bcd"}else{return matchHelper(str, sIndex, pattern, pIndex+2);}}// 第一个字符相同情况下if (pattern[pIndex] == str[sIndex] || (pattern[pIndex] == '.' && sIndex != str.length)){return matchHelper(str, sIndex+1, pattern, pIndex+1);}return false;}}

53.表示数值的字符串

请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。

例如,字符串"+100","5e2","-123","3.1416"和"-1E-16"都表示数值。

但是"12e","1a3.14","1.2.3","+-5"和"12e+4.3"都不是。

需要注意的是,指数E后面必须跟一个整数,不能没有数,也不能为小数。

public class Solution {public boolean isNumeric(char[] str) {int size = str.length;if (size == 0) return false;boolean hasE = false, hasDot = false, hasSign = false;for (int i = 0; i < size; i++){// 如果当前字符是 e, Eif (str[i] == 'e' || str[i] == 'E'){// 1.首部出现,2.尾部出现,3.重复出现,均不是if (i == 0) return false;if (i == size-1) return false;if (hasE) return false;hasE = true;// 如果当前字符是 +, -}else if (str[i] == '+' || str[i] == '-'){// 1.如果当前不是第一位,且前一位不是E, e// 2.如果已经出现过+-, 且前一位不是 E, e,均为falseif(i != 0 && str[i-1] != 'E' && str[i-1] != 'e') return false;if (hasSign && str[i-1] != 'E' && str[i-1] != 'e') return false;hasSign = true;}else if (str[i] == '.'){// 1.存在多个 . 2. 在 E 后面if (hasDot) return false;if (hasE) return false;hasDot = true;}else if (str[i] < '0' || str[i] > '9'){return false;}}return true;}
}

54.字符流中第一个不重复的元素

请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。

输出描述:

如果当前字符流没有存在出现一次的字符,返回#字符。
public class Solution {//Insert one char from stringstreamString str = "";char[] hash = new char[256];public void Insert(char ch){str += ch;  // 拼接字符串hash[ch] += 1; // 对字符 计数}//return the first appearence once char in current stringstreampublic char FirstAppearingOnce(){for (int i = 0; i < str.length(); i++){char ch = str.charAt(i);if (hash[ch] == 1){return ch;}}return '#';}
}

55.链表中环的入口

给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。

方法1. 使用数组,数组保存已经遍历的node,遍历每次查看节点是否在node数组中,如果在,则存在环

/*public class ListNode {int val;ListNode next = null;ListNode(int val) {this.val = val;}
}
*/
import java.util.ArrayList;
public class Solution {public ListNode EntryNodeOfLoop(ListNode pHead){// 使用数组,数组保存已经遍历的node,遍历每次查看节点是否在node数组中,如果在,则存在环if (pHead == null || pHead.next == null) return null;ArrayList<ListNode> list = new ArrayList();while (pHead != null){if (isInArray(list, pHead)){ // 判断节点是否在数组当中return pHead;}list.add(pHead);pHead = pHead.next;}return null;}private boolean isInArray(ArrayList<ListNode> list, ListNode node){for (ListNode n: list){if (n == node){return true;}}return false;}}

方法二:使用快慢指针

假设x为环前面的路程(黑色路程),a为环入口到相遇点的路程(蓝色路程,假设顺时针走), c为环的长度(蓝色+橙色路程)

当快慢指针相遇的时候:

此时慢指针走的路程为Sslow = x + m * c + a
快指针走的路程为Sfast = x + n * c + a
2 Sslow = Sfast
2 * ( x + m*c + a ) = (x + n *c + a)
从而可以推导出:
x = (n - 2 * m )*c - a
= (n - 2 *m -1 )*c + c - a
即环前面的路程 = 数个环的长度(为可能为0) + c - a
什么是c - a?这是相遇点后,环后面部分的路程。(橙色路程)

所以,我们可以让一个指针从起点A开始走,让一个指针从相遇点B开始继续往后走,
2个指针速度一样,那么,当从原点的指针走到环入口点的时候(此时刚好走了x)
从相遇点开始走的那个指针也一定刚好到达环入口点。
所以2者会相遇,且恰好相遇在环的入口点。

最后,判断是否有环,且找环的算法复杂度为:

时间复杂度:O(n)

空间复杂度:O(1)

设置快慢指针fast和low,fast每次走两步,low每次走一步。假如有环,两者一定会相遇

步骤1:使用快慢指针,找到相遇点,如果找不到则没有环

步骤2:找到环入口,让一个指针从头开始走,让一个指针从相遇点B开始继续往后走

/*public class ListNode {int val;ListNode next = null;ListNode(int val) {this.val = val;}
}
*/
public class Solution {public ListNode EntryNodeOfLoop(ListNode pHead){if (pHead == null || pHead.next == null || pHead.next.next == null) return null;// 设置快慢指针,找到两者的相遇点ListNode low =  pHead.next;ListNode fast = pHead.next.next;while (low != fast){if (low == null || fast == null) return null;low = low.next;fast = fast.next.next;}// 一个从头开始遍历,一个从相遇点low开始遍历ListNode head = pHead;while (low != head){low = low.next;head = head.next;}return low;}
}

56.删除链表中的重复结点

在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。

例如,链表1->2->3->3->4->4->5 处理后为 1->2->5

创建一个辅助结点,方便处理第一个节点是重复的节点的情况

画图理解

删除3 节点的示例图形

/*public class ListNode {int val;ListNode next = null;ListNode(int val) {this.val = val;}
}
*/
public class Solution {public ListNode deleteDuplication(ListNode pHead){if (pHead == null || pHead.next == null) return pHead;ListNode dummy = new ListNode(-1);// 创建辅助节点dummy.next = pHead; //连接链表ListNode last = dummy;while (pHead != null && pHead.next != null){if (pHead.val != pHead.next.val){ // 更新节点pHead = pHead.next;last = last.next;}else{int val = pHead.val;while (pHead != null && val == pHead.val){ //找到重复节点的下一个节点pHead = pHead.next;}last.next = pHead; // 删除重复节点}}return dummy.next;}
}

57.二叉树的下一个结点

给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。

思路:首先知道中序遍历的规则是:左根右,然后作图

结合图,我们可发现分成两大类:

1、有右子树的,那么下个结点就是右子树最左边的点;(eg:D,B,E,A,C,G)

2、没有右子树的,也可以分成两类

a)是父节点左孩子(eg:N,I,L) ,那么父节点就是下一个节点 ;

b)是父节点的右孩子(eg:H,J,K,M)找他的父节点的父节点的父节点...直到当前结点是其父节点的左孩子位置。如果没有eg:M,那么他就是尾节点。

1、有右子树的,那么下个结点就是右子树最左边的点

2、没有右子树,2.1如果有左结点,返回根节点就可以了;

2.2.如果是右叶子结点,则一直往上寻找父节点,然后继续判断其是否有左结点

分析二叉树的下一个节点,一共有以下情况:
1.二叉树为空,则返回空;
2.节点右孩子存在,则设置一个指针从该节点的右孩子出发,一直沿着指向左子结点的指针找到的叶子节点即为下一个节点;
3.节点不是根节点。如果该节点是其父节点的左孩子,则返回父节点;否则继续向上遍历其父节点的父节点,重复之前的判断,返回结果。

其中next结点是指向父亲结点

/*
public class TreeLinkNode {int val;TreeLinkNode left = null;TreeLinkNode right = null;TreeLinkNode next = null;TreeLinkNode(int val) {this.val = val;}
}
*/
public class Solution {public TreeLinkNode GetNext(TreeLinkNode pNode){if(pNode == null) return null;// 如果有右子树的话,一直找右子树的左节点if (pNode.right != null){pNode = pNode.right;while (pNode.left != null){pNode = pNode.left;}return pNode;}// 没有右子树的情况// 1. 如果root左子树不存在,则往上遍历// 2. 如果左子树存在,则returnwhile (pNode.next != null){TreeLinkNode proot = pNode.next;if(proot.left == pNode){return proot;}pNode = pNode.next;}return null;}
}

58.判断是否是对称二叉树

请实现一个函数,用来判断一颗二叉树是不是对称的。

注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。

思路:递归判断

1. 创建一个函数,包含左右节点

2. 如果左右子树均为空,这时对称

3.如果存在一个子树不为空,则不对称

4. 递归判断,判断2个节点值是否相同,左子树的左节点与右子树右节点,左子树右节点与右子树左节点比较

/*
public class TreeNode {int val = 0;TreeNode left = null;TreeNode right = null;public TreeNode(int val) {this.val = val;}}
*/
public class Solution {boolean isSymmetrical(TreeNode pRoot){if(pRoot == null) return true;return symHelper(pRoot.left, pRoot.right);}private boolean symHelper(TreeNode left, TreeNode right){if (left == null && right == null) return true;if (left == null || right == null) return false;return left.val == right.val && symHelper(left.left, right.right) && symHelper(left.right, right.left);}}

59.按之字形打印二叉树

请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。

即使用层次遍历得到每一层分开的多重数组,然后按单双次打印,单次按顺序,双次反向打印。

使用两个栈实现,栈1 按左右子树的顺序存入栈中,栈2 按右 左的顺序存入栈中。

import java.util.ArrayList;
import java.util.LinkedList;/*
public class TreeNode {int val = 0;TreeNode left = null;TreeNode right = null;public TreeNode(int val) {this.val = val;}}
*/
public class Solution {public ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {ArrayList<ArrayList<Integer> > res = new ArrayList();if (pRoot == null) return res;LinkedList<TreeNode> stack1 = new LinkedList();LinkedList<TreeNode> stack2 = new LinkedList();stack1.push(pRoot);while (!stack1.isEmpty() || !stack2.isEmpty()){ArrayList<Integer> array1 = new ArrayList();  //保存奇数行二叉树数据ArrayList<Integer> array2 = new ArrayList();  // 保存偶数行while (!stack1.isEmpty()){TreeNode curNode = stack1.pop();if (curNode.left != null) stack2.push(curNode.left);if (curNode.right != null) stack2.push(curNode.right);array1.add(curNode.val);}if (array1.size() > 0) res.add(array1);while (!stack2.isEmpty()){TreeNode curNode = stack2.pop();if (curNode.right != null) stack1.push(curNode.right); // 先保存右子树if (curNode.left != null) stack1.push(curNode.left);array2.add(curNode.val);}if (array2.size() > 0) res.add(array2);}return res;}}

60.把二叉树打印成多行

从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。

二叉树的层次、广度优先遍历,使用队列来实现

import java.util.ArrayList;
import java.util.LinkedList;/*
public class TreeNode {int val = 0;TreeNode left = null;TreeNode right = null;public TreeNode(int val) {this.val = val;}}
*/
public class Solution {ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {ArrayList<ArrayList<Integer>> res = new ArrayList();LinkedList<TreeNode> queue = new LinkedList();if (pRoot == null) return res;queue.offer(pRoot);while (!queue.isEmpty()){ArrayList<Integer> list = new ArrayList();int size = queue.size(); // 保存队列的长度for (int i = 0; i < size; i++){  // 确保打印二叉树一行的数据TreeNode root = queue.poll();if (root.left != null) queue.offer(root.left);if (root.right != null) queue.offer(root.right);list.add(root.val);}res.add(list);}return res;}}

61.序列二叉树

请实现两个函数,分别用来序列化和反序列化二叉树

二叉树的序列化是指:把一棵二叉树按照某种遍历方式的结果以某种格式保存为字符串,从而使得内存中建立起来的二叉树可以持久保存。序列化可以基于先序、中序、后序、层序的二叉树遍历方式来进行修改,序列化的结果是一个字符串,序列化时通过 某种符号表示空节点(#),以 ! 表示一个结点值的结束(value!)。

二叉树的反序列化是指:根据某种遍历顺序得到的序列化字符串结果str,重构二叉树。

例如,我们可以把一个只有根节点为1的二叉树序列化为"1,",然后通过自己的函数来解析回这个二叉树

/*
public class TreeNode {int val = 0;TreeNode left = null;TreeNode right = null;public TreeNode(int val) {this.val = val;}}
*/
public class Solution {private int index = -1;String Serialize(TreeNode root) { // 序列化二叉树,根左右,前序序列化StringBuffer sb = new StringBuffer();if (root == null){sb.append("#,");return sb.toString();}sb.append(root.val + ",");  // 先保存根节点,sb.append(Serialize(root.left)); // 保存左节点的字符串sb.append(Serialize(root.right));return sb.toString();}TreeNode Deserialize(String str) { // 反序列化二叉树index += 1;int size = str.length();if (index >= size) return null;String[] s = str.split(",");TreeNode node = null;if (!s[index].equals("#")){node = new TreeNode(Integer.valueOf(s[index]));node.left = Deserialize(str);node.right = Deserialize(str);}return node;}
}

62.二叉搜索树的第k 个结点

给定一棵二叉搜索树,请找出其中的第k小的结点。

例如, (5,3,7,2,4,6,8)    中,按结点数值大小顺序第三小结点的值为4。

思路:二叉搜索树按照中序遍历的顺序打印出来正好就是排序好的顺序。

//     所以,按照中序遍历顺序找到第k个结点就是结果。

/*
public class TreeNode {int val = 0;TreeNode left = null;TreeNode right = null;public TreeNode(int val) {this.val = val;}}
*/
import java.util.Stack;
public class Solution {TreeNode KthNode(TreeNode pRoot, int k){// 使用非递归中序遍历二叉树if (pRoot == null || k <= 0) return null;Stack<TreeNode> stack = new Stack();int count = 0;while (pRoot != null || !stack.isEmpty()){while (pRoot != null){stack.push(pRoot); // 将左节点全部放入栈中pRoot = pRoot.left;}pRoot = stack.pop();count += 1;  // 统计节点数量if (count == k) return pRoot;pRoot = pRoot.right; // 找到右节点}return null;}}

63.数据流中的中位数

如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。

思路:

此题的核心是:将获取的最大的数字均放在最小堆,将获取的最小的值均放在最大堆

import java.util.PriorityQueue;
import java.util.Comparator;
public class Solution {private PriorityQueue<Integer> minHeap = new PriorityQueue(); //  最小堆private PriorityQueue<Integer> maxHeap = new PriorityQueue(12, new Comparator<Integer>(){@Overridepublic int compare(Integer o1, Integer  o2){return o2 - o1;}});  // 需要结合comparator 来实现最大堆private int count = 0;public void Insert(Integer num) {if ((count & 1) == 0){ // 如果是偶数的情况下// 将最大值保存在最小堆中maxHeap.offer(num);int maxValue = maxHeap.poll();// 获取堆中的最大值minHeap.offer(maxValue);}else{minHeap.offer(num);int minValue = minHeap.poll(); // 获取堆中的最小值maxHeap.offer(minValue);}count += 1;}public Double GetMedian() {if ((count & 1) == 0){ return new Double((minHeap.peek() + maxHeap.peek()) /2.0);}else{return new Double(minHeap.peek());}}}

64.滑动窗口的最大值

给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。

例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。

使用大顶堆的

import java.util.PriorityQueue;
import java.util.Comparator;
import java.util.ArrayList;public class Solution {public ArrayList<Integer> maxInWindows(int [] num, int size){ArrayList<Integer> res = new ArrayList();int len = num.length;if (len < size) return res;if (size == 0) return res;int gap = len - size;PriorityQueue<Integer> maxHeap = new PriorityQueue(3, new Comparator<Integer>(){public int compare(Integer o1, Integer o2){return o2-o1;}}); // 最大堆for (int i = 0; i <= gap; i++){for (int j = i; j < size+i; j++){maxHeap.offer(num[j]);}int max = maxHeap.peek(); // 获取最大值res.add(max);while(!maxHeap.isEmpty()){maxHeap.poll();}}return res;}
}

使用双端队列,复杂度为O(n)

用一个双端队列,队列第一个位置保存当前窗口的最大值,当窗口滑动一次

1.判断当前最大值是否过期

2.新增加的值从队尾开始比较,把所有比他小的值丢掉

import java.util.ArrayList;
import java.util.LinkedList;public class Solution {public ArrayList<Integer> maxInWindows(int [] num, int size){ArrayList<Integer> res = new ArrayList();int len = num.length;if (len < size || size <= 0 || len == 0) return res;LinkedList<Integer> indexQueue = new LinkedList(); // 用于保存索引值的双端队列,队首保存最大值的索引for (int i = 0; i < len; i++){while (indexQueue.size()!=0 && num[i] > num[indexQueue.peekLast()]){//如果当前值比队首索引值的大小大的话,这删除队首索引indexQueue.pollLast();}indexQueue.addLast(i);// 判断队首元素是否过期,如6,2,5,1,size为3if (indexQueue.size()!=0 && (i-indexQueue.peekFirst())==size){indexQueue.pollFirst();}if (i >= size-1){res.add(num[indexQueue.peekFirst()]);}}return res;
}}

65.矩阵中的路径

请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则之后不能再次进入这个格子。

例如

这样的3 X 4 矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。

解题思路:

采用回溯法(探索与回溯法),它是一种选优搜索法,又称为试探法,按选优条件向前搜索,以达到目标。 但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。

基本思想:

0.根据给定数组,初始化一个标志位数组,初始化为false,表示未走过,true表示已经走过,不能走第二次

1.根据行数和列数,遍历数组,先找到一个与str字符串的第一个元素相匹配的矩阵元素,进入judge

2.根据i和j先确定一维数组的位置,因为给定的matrix是一个一维数组

3.确定递归终止条件:越界,当前找到的矩阵值不等于数组对应位置的值,已经走过的,这三类情况,都直接false,说明这条路不通

4.若pathLength,就是待判定的字符串str的索引已经判断到了最后一位,此时说明是匹配成功的

5.下面就是本题的精髓,递归不断地寻找周围四个格子是否符合条件,只要有一个格子符合条件,就继续再找这个符合条件的格子的四周是否存在符合条件的格子,直到k到达末尾或者不满足递归条件就停止。

6.走到这一步,说明本次是不成功的,我们要还原一下标志位数组index处的标志位,进入下一轮的判断。

public class Solution {public boolean hasPath(char[] matrix, int rows, int cols, char[] str){int size = matrix.length;if (rows * cols != size || size == 0 || str.length==0) return false;// 初始化时,全部元素为 falseboolean[] visited = new boolean[size]; // 如果被访问,则位true,记录matrix元素访问int pathLength = 0; // 记录遍历的索引值for (int i = 0; i < rows; i++){for (int j = 0; j< cols; j++){// 如果有一条路径满足,则为 trueif (hasPathHelper(matrix, rows, cols, str, i, j, visited, pathLength)){return true;}}}return false;}private boolean hasPathHelper(char[] matrix, int rows, int cols, char[] str, int x, int y, boolean[] visited, int pathLength){if (str.length == pathLength){return true;}// 使用递归回溯的方法int index = x * cols + y; // matrix的索引boolean result = false;// 最后2个满足的条件是:没有别访问,且找到指定字符if (0 <= x && x < rows && 0 <= y && y < cols && visited[index] == false && matrix[index]== str[pathLength]){pathLength += 1;visited[index] = true;// 遍历上下左右 四个方位boolean x1 = hasPathHelper(matrix, rows, cols, str, x-1, y, visited, pathLength);boolean x2 = hasPathHelper(matrix, rows, cols, str, x+1, y, visited, pathLength);boolean x3 = hasPathHelper(matrix, rows, cols, str, x, y-1, visited, pathLength);boolean x4 = hasPathHelper(matrix, rows, cols, str, x, y+1, visited, pathLength);result = x1 || x2 || x3|| x4;if (result == false){ //回溯上一层pathLength -= 1;visited[index] = false;}}return result;}}

66.机器人的运动范围

地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。

例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?

public class Solution {public int movingCount(int threshold, int rows, int cols){boolean[][] flag = new boolean[rows][cols]; // 创建二维矩阵,走过则为trueint result = counterHelper(flag, threshold, rows, cols, 0, 0); // 从原点开始遍历return result;}private int counterHelper(boolean[][] flag, int threshold, int rows, int cols, int i, int j){boolean isFit = countNumber(i) + countNumber(j) <= threshold;if (i >= 0 && i < rows && j >= 0 && j < cols && flag[i][j] == false && isFit){flag[i][j] = true;
// 计算上下左右的符合的数量return counterHelper(flag, threshold, rows, cols, i-1, j) +counterHelper(flag, threshold, rows, cols, i+1, j) +counterHelper(flag, threshold, rows, cols, i, j-1) +counterHelper(flag, threshold, rows, cols, i, j+1) + 1;}return 0;}// 计算数字 各百分十分等位数 的 和private int countNumber(int num){int res = 0;while (num != 0){res += num%10;num /= 10;}return res;}}

终于。。。。。。。。。。。。。完结。。。。。。。。。。。。。。。

三天刷完《剑指OFFER编程题》--Java版本实现(第三天)相关推荐

  1. 剑指offer编程题Java实现——面试题3二维数组中的查找

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

  2. 剑指offer编程题(JAVA实现)——第35题:数组中的逆序对

    github https://github.com/JasonZhangCauc/JZOffer 剑指offer编程题(JAVA实现)--第35题:数组中的逆序对 题目描述 在数组中的两个数字,如果前 ...

  3. 剑指offer编程题(JAVA实现)——第38题:二叉树的深度

    github https://github.com/JasonZhangCauc/JZOffer 剑指offer编程题(JAVA实现)--第38题:二叉树的深度 题目描述 输入一棵二叉树,求该树的深度 ...

  4. 剑指Offer 66题 python版本 汇总

    牛客网剑指offer 66题汇总 (python) 有部分参考牛客网答案,部分为自己提交结果 1. 二维数组中的查找 在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每 ...

  5. 剑指Offer——编程题的Java实现(更新完毕……)

    目录 二维数组中的查找 替换空格 从尾到头打印链表 重建二叉树 用两个栈实现队列 用两个队列实现一个栈 旋转数组的最小数字 斐波那契数列 跳台阶 变态跳台阶 矩形覆盖 二进制中1的个数 数值的整数次方 ...

  6. 一只青蛙跳向三个台阶_Java版剑指offer编程题第9题--变态跳台阶

    跟learnjiawa一起每天一道算法编程题,既可以增强对常用API的熟悉能力,也能增强自己的编程能力和解决问题的能力.算法和数据结构,是基础中的基础,更是笔试的重中之重. 不积硅步,无以至千里: 不 ...

  7. 编程 跳台阶_Java版剑指offer编程题第8题--跳台阶

    跟learnjiawa一起每天一道算法编程题,既可以增强对常用API的熟悉能力,也能增强自己的编程能力和解决问题的能力.算法和数据结构,是基础中的基础,更是笔试的重中之重. 不积硅步,无以至千里: 不 ...

  8. 剑指Offer——编程题的Java实现

    声明:我写这个的意图是我在看书的过程中,就我掌握的内容做一个笔记,没有抄袭的意图.再次说明一下,我找工作的过程中并不顺利,没有像那些牛人们拿到一大把的Offer,我只是希望通过我自己的努力,不断提高自 ...

  9. 剑指 offer 编程题 C++ 版总结(中)

    解题思路:从根结点开始向下进行对比,(1) 若两边的结点为空,返回 true.(2) 一个结点为空,而另一个结点不为空,此时二叉树不是对称的,返回 false.(3) 两边结点都不为空,但是结点的值不 ...

最新文章

  1. 鸿蒙2.0开始推送,华为OS首次登陆手机,亮点槽点都在这里
  2. App自动化测试之Adb基础命令使用
  3. python 打包文件
  4. matlab disteclud,机器学习实战ByMatlab(四)二分K-means算法
  5. Windows Mobile的一些网站和资源(由网友搜集 我整理了一下)
  6. 三大电信运营商携号转网数据_携号转网数据出炉!转出移动最多,近五成转入电信...
  7. 网站获取ip代码怎么写_大学生写论文必备技能:怎么免费获取外文文献并下载...
  8. 白鹭引擎和layabox哪个好用,哪个技术更成熟 ?
  9. Affymetrix基因芯片小总结
  10. c# winform 浏览器调用chrome内核
  11. C语言中sprintf()函数的用法
  12. Python 操作 Excel 表格
  13. 使用conda管理python环境
  14. (翻译)锚定效应(Anchoring)
  15. java 时区 夏令时_关于时区:如何显示Java开始夏令时后的时间差异?
  16. webx参数注入、bean创建总结
  17. Mac OS X系统下修改wifi共享的默认网段
  18. 分享150个PHP源码,总有一款适合您
  19. 强化学习-把元学习(Meta Learning)一点一点讲给你听
  20. matlab 符号表达式正负,如何在Matlab上为符号表达式编写`lhs()`或`rhs()`函数

热门文章

  1. 仿pinterest实现瀑布流布局效果
  2. Mybatis中TypeHandler的简单应用
  3. ​法国将于2020年测试自己的数字货币
  4. T3054 高精度练习-文件操作 codevs
  5. 聚划算的赢面不只是刘涛
  6. php 无法加载activex,IE无法加载 Activex 控件的解决办法
  7. MySQL讲义第 35 讲——select 查询之正则表达式
  8. AlarmManager的简单使用
  9. 真三国无双3.9D蓝屏 (魔兽争霸3 蓝屏) 的处理方法
  10. 记录下server的配置