剑指offer所有的题目总结

目录

 

基本都是参考别的博客和书本的代码,仅作为自己笔记用!!

零、小结:

一、位运算

1、二进制中1的个数

2、判断二进制中0的个数

3.二进制高位连续0的个数

二、二叉树

1、二叉搜索树第k个结点

2.0 从上往下打印二叉树

2.1二叉树打印成多行

2.2按之字形顺序打印二叉树

题目描述

3.数据流中位数

4.二叉树中和为某一值的路径

5.重建二叉树

6.树的子结构

7.二叉树的镜像

8、二叉搜素树的后序遍历序列 

9、二叉搜索树与双向链表

10、二叉树的深度

11、平衡二叉树

12、二叉树的下一个节点

 13、对称的二叉树

14、序列化二叉树

三、字符串

1.正则表达式的匹配

2.表示数值的字符串

3.0第一个只出现一次的字符

3.1字符流中第一个不重复的字符

4.翻转字符串

5.左旋转字符串

5.把字符串转换为整数

6、字符串的排列 

四、数组

1.数组中重复的数字

2.构建乘积数组

3.二维数组的查找

4.数组中只出现一次的数字

5、和为S的两个数

6.和为S的连续正数序列

7、调整数组顺序使奇数位于偶数前面 

8、数组中出现次数超过一半的数字

9、连续子数组的最大和

10、把数组排成最小的数 

11、数组中的逆序对

12、数字在排序数组中出现的次数

五、其他

1.求1+2+3+...+n

2.不用加减乘除做加法

3、旋转数组的最小数字

 

六、其他

1.整数中1出现的次数(从1到n中的整数中1出现的次数)

2.扑克牌顺子

3.孩子们的游戏(圆圈中剩下的数)

4、替换空格

5、斐波那契数列 

6、跳台阶

 7、变态跳台阶

8、矩形覆盖

 9、数值的整数次方

10、顺时针打印矩阵

11、最小的k个数

12、丑数

七、栈和队列

1、滑动窗口的最大值

2、用 两个栈实现队列

 3、包含min函数的栈

4、栈的压入、弹出序列

 

八、回溯法

1、矩阵中的路径

2、机器人运动范围

九、链表

1、从尾到头打印链表

2、链表中倒数第k个结点

 3、反转链表

4、合并两个排序的链表

5、复杂链表的复制

6、两个链表的第一个公共结点

7、链表中环的入口节点

8、删除链表中重复的节点

9、链表回文结构

十、非剑指offer

1、 左神的


基本都是参考别的博客和书本的代码,仅作为自己笔记用!!

零、小结:

1、<<      :     左移运算符,num << 1,相当于num乘以2

     >>      :     右移运算符,num >> 1,相当于num除以2

    >>>    :     无符号右移,忽略符号位,空位都以0补齐

2、//与1位与得1就是奇数,1只有最后一位是1

一、位运算

1、二进制中1的个数

输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。

思路:最简单的思路,整数n每次进行无符号右移一位,同1做&运算,可以判断最后以为是否为1。

通常的剑指offer的思路,n&(n-1) 操作相当于把二进制表示中最右边的1变成0。所以只需要看看进行了多少次这样的操作即可。看看什么时候n为0.


   
   
   
   
  1. public class erjinzhi {
  2. public int NumberOf1(int n) {
  3. /*int count = 0; //自己的思路,主要就是n与2 4 8 16分别与,来判断
  4. long temp = 1;
  5. for(int i = 1; i <= 32;i++){
  6. if((n&temp) > 0)
  7. count++;
  8. temp = temp * 2;
  9. }
  10. return count;*/
  11. /* //简单的思路
  12. int res = 0;
  13. while (n!=0) {
  14. res = res + n&1;
  15. n>>>=1;
  16. }
  17. return res;*/
  18. int count = 0;
  19. while(n!= 0)
  20. {
  21. n = n&(n- 1);
  22. count++;
  23. }
  24. return count;
  25. }
  26. }
  • 2、判断二进制中0的个数

思路:每次右移一位,判断是否是0即可。暂时没有找到别的好思路。


   
   
   
   
  1. public static int findZero(int n) {
  2. int count = 0;
  3. while(n != 0) {
  4. if((n& 1)!= 1)
  5. count++;
  6. n>>>= 1;
  7. }
  8. return count;
  9. }
  • 3.二进制高位连续0的个数

思路:每次与最高位为1的二进制进行&操作。0x80000000的二进制是1000 0000 0000 0000 ...共32位,最高位为1.

参考https://blog.csdn.net/u013190513/article/details/70216730

https://www.cnblogs.com/hongten/p/hongten_java_integer_toBinaryString.html

https://blog.csdn.net/lpjishu/article/details/51323722


   
   
   
   
  1. public static int numberOfLeadingZeros0(int i){
  2. if(i == 0)
  3. return 32;
  4. int n = 0;
  5. int mask = 0x80000000;
  6. int j = i & mask;
  7. while(j == 0){
  8. n++;
  9. i <<= 1;
  10. j = i & mask;
  11. }
  12. return n;
  13. }

JDK中源码解决思路.


   
   
   
   
  1. public static int numberOfLeadingZeros(int i) {
  2. // HD, Figure 5-6
  3. if (i == 0)
  4. return 32;
  5. int n = 1;
  6. if (i >>> 16 == 0) { n += 16; i <<= 16; }
  7. if (i >>> 24 == 0) { n += 8; i <<= 8; }
  8. if (i >>> 28 == 0) { n += 4; i <<= 4; }
  9. if (i >>> 30 == 0) { n += 2; i <<= 2; }
  10. n -= i >>> 31;
  11. return n;
  12. }

二、二叉树

  • 1、二叉搜索树第k个结点

给定一颗二叉搜索树,请找出其中的第k小的结点。例如, 5 / \ 3 7 /\ /\ 2 4 6 8 中,按结点数值大小顺序第三个结点的值为4。

思路:递归的方式:二叉搜索树的中序遍历就是排序的,所以用中序遍历,每一次中间的时候判断是否等于k即可。

         非递归的方式:运用栈进行操作。相当于用栈实现了中序遍历,在中间进行了个数的判断


   
   
   
   
  1. int count = 0;
  2. TreeNode KthNode(TreeNode pRoot, int k)
  3. {
  4. if(pRoot != null) {
  5. TreeNode leftNode = KthNode(pRoot.left, k);
  6. if(leftNode != null)
  7. return leftNode;
  8. count++;
  9. if(count == k)
  10. return pRoot;
  11. TreeNode rightNode = KthNode(pRoot.right, k);
  12. if(rightNode != null)
  13. return rightNode;
  14. }
  15. return null;
  16. }

//栈的方式


   
   
   
   
  1. TreeNode KthNode(TreeNode pRoot, int k)
  2. {
  3. Stack stack = new Stack();
  4. if(pRoot== null||k== 0) return null;
  5. int t= 0;
  6. while(pRoot!= null ||stack.size()> 0){
  7. while(pRoot!= null){
  8. stack.push(pRoot);
  9. pRoot = pRoot.left;
  10. }
  11. if(stack.size()> 0){
  12. pRoot= stack.pop();
  13. t++;
  14. if(t==k) return pRoot;
  15. pRoot= pRoot.right;
  16. }
  17. }
  18. return null;
  19. }
  • 2.0 从上往下打印二叉树

从上往下打印出二叉树的每个节点,同层节点从左至右打印。

思路:


   
   
   
   
  1. import java.util.ArrayList;
  2. import java.util.LinkedList;
  3. /**
  4. public class TreeNode {
  5. int val = 0;
  6. TreeNode left = null;
  7. TreeNode right = null;
  8. public TreeNode(int val) {
  9. this.val = val;
  10. }
  11. }
  12. */
  13. public class Solution {
  14. /**
  15. * 算法思路,(剑指offer图片)
  16. * 1.根节点放到队列里面,队列不空,就打印队列头,打印这个节点,马上把这个节点的左右子节点放到队列中。
  17. * 2.再要访问一个节点,把这个节点的左右放入,此时队头是同层的,对位是打印出来的左右。依次先入先出就可以得到结果。
  18. * @param root
  19. * @return
  20. */
  21. public ArrayList PrintFromTopToBottom(TreeNode root) {
  22. ArrayList layerList = new ArrayList();
  23. if (root == null)
  24. return layerList;
  25. LinkedList queue = new LinkedList();
  26. queue.add(root);
  27. while (!queue.isEmpty()) {
  28. TreeNode node = queue.poll();
  29. layerList.add(node.val);
  30. if (node.left != null)
  31. queue.addLast(node.left);
  32. if (node.right != null)
  33. queue.addLast(node.right);
  34. }
  35. return layerList;
  36. }
  37. }
  • 2.1二叉树打印成多行

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

思路:主要采用左程云的思路,


   
   
   
   
  1. static ArrayList > Print(TreeNode pRoot) {
  2. ArrayList> res = new ArrayList<>();
  3. if(pRoot == null)
  4. return res;
  5. Queue queue = new LinkedList();
  6. TreeNode last = pRoot;
  7. TreeNode nlast = null;
  8. queue.offer(pRoot);
  9. ArrayList tmp = new ArrayList<>();
  10. while (!queue.isEmpty()) {
  11. pRoot = queue.poll();
  12. tmp.add(pRoot.val); //出队列,就把他左右孩子入队列,
  13. //此时,下一层的最右要跟着更新
  14. if (pRoot.left!= null) {
  15. queue.offer(pRoot.left);
  16. nlast = pRoot.left;
  17. }
  18. if (pRoot.right!= null) {
  19. queue.offer(pRoot.right);
  20. nlast = pRoot.right;
  21. }
  22. //如果到了本层的最右,就把这一层结果放入。注意最后一层时,isempty不成立,
  23. //最后一层的结果要单独放入。
  24. if (pRoot == last && !queue.isEmpty()) {
  25. res.add( new ArrayList<>(tmp));
  26. last = nlast;
  27. tmp.clear();
  28. }
  29. }
  30. res.add( new ArrayList<>(tmp));
  31. return res;
  32. }

2.2按之字形顺序打印二叉树

题目描述

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

利用两个栈的辅助空间分别存储奇数偶数层的节点,然后打印输出。或使用链表的辅助空间来实现,利用链表的反向迭实现逆序输出。


   
   
   
   
  1. import java.util.ArrayList;
  2. import java.util.Stack;
  3. /*
  4. public class TreeNode {
  5. int val = 0;
  6. TreeNode left = null;
  7. TreeNode right = null;
  8. public TreeNode(int val) {
  9. this.val = val;
  10. }
  11. }
  12. */
  13. public class Solution {
  14. //利用两个栈的辅助空间分别存储奇数偶数层的节点,然后打印输出。或使用链表的辅助空间来实现,利用链表的反向迭实现逆序输出。
  15. public ArrayList > Print(TreeNode pRoot) {
  16. ArrayList> res = new ArrayList<>();
  17. if(pRoot == null)
  18. return res;
  19. Stack s1 = new Stack<>();
  20. Stack s2 = new Stack<>();
  21. s1.push(pRoot);
  22. int level = 1;
  23. while (!s1.empty()||!s2.empty()) {
  24. if (level % 2 != 0) {
  25. ArrayList list = new ArrayList<>();
  26. while (!s1.empty()) {
  27. TreeNode node = s1.pop();
  28. if (node!= null) {
  29. list.add(node.val);
  30. s2.push(node.left); //因为偶数层,先右后左,所以要先放左子树,栈
  31. s2.push(node.right);
  32. }
  33. }
  34. if (!list.isEmpty()) {
  35. res.add(list);
  36. level++;
  37. }
  38. }
  39. else {
  40. ArrayList list = new ArrayList<>();
  41. while (!s2.empty()) {
  42. TreeNode node = s2.pop();
  43. if (node!= null) {
  44. list.add(node.val);
  45. s1.push(node.right);
  46. s1.push(node.left);
  47. }
  48. }
  49. if (!list.isEmpty()) {
  50. res.add(list);
  51. level++;
  52. }
  53. }
  54. }
  55. return res;
  56. }
  57. }

 

  • 3.数据流中位数

如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。

思路:主要是博客的代码,参考了左的部分分析。

创建优先级队列维护大顶堆和小顶堆两个堆,并且小顶堆的值都大于大顶堆的值。比如6,1,3,0,9,8,7,2则较小的部分大根堆是0,1,2,3 较大的部分小根堆是6,7,8,9.

具体思路:

1.本代码为了保证两个堆的尺寸差距最大为1,采用奇数个时候插到大根堆,偶数个插到小根堆。

2.当数据总数为偶数时,新加入的元素,应当进入小根堆(注意不是直接进入小根堆,而是经大根堆筛选后取大根堆中最大元素进入小根堆),要保证小根堆里面所有数都比大根堆的大。

3.当数据为奇数个时,按照相应的调整进入大根堆。

4.如果个数位奇数个,则大根堆堆顶为中位数,否则就是两个堆顶除以2.比如新加入三个,那么第一个在大,第二个在小,第三个可能在大。所以就是大根堆的堆顶。

* 插入有两种思路: 左采用第一种,本代码采用第二种

* 1:直接插入大堆中,之后若两堆尺寸之差大于1(也就是2),则从大堆中弹出堆顶元素并插入到小堆中

* 若两队之差不大于1,则直接插入大堆中即可。

* 2:奇数个数插入到大堆中,偶数个数插入到小堆中,

* 但是 可能会出现当前待插入的数比小堆堆顶元素大,此时需要将元素先插入到小堆,然后将小堆堆顶元素弹出并插入到大堆中

* 对于偶数时插入小堆的情况,一样的道理。why?

* 因为要保证最大堆的元素要比最小堆的元素都要小。


   
   
   
   
  1. import java.util.Comparator;
  2. import java.util.PriorityQueue;
  3. public class Shujuliumedian {
  4. int count = 0;
  5. PriorityQueue minheap = new PriorityQueue<>();
  6. PriorityQueue maxheap = new PriorityQueue<>( 11, new Comparator() {
  7. @Override
  8. public int compare(Integer o1, Integer o2) {
  9. // TODO Auto-generated method stub
  10. return o2.compareTo(o1); //o2大于o1返回1 ,否则返回-1
  11. }
  12. });
  13. public void Insert(Integer num) {
  14. count++;
  15. if (count % 2 == 0) { //偶数进入小根堆,这个其实无所谓,定了一个平均分配的规则
  16. //保证进入小根堆的元素要比大根堆最大的大,所以如果小调整
  17. if (!maxheap.isEmpty() && num < maxheap.peek()) {
  18. maxheap.offer(num);
  19. num = maxheap.poll();
  20. }
  21. minheap.offer(num);
  22. }
  23. else { //奇数进入大根堆
  24. if (!minheap.isEmpty() && num > minheap.peek()) {
  25. minheap.offer(num);
  26. num = minheap.poll();
  27. }
  28. maxheap.offer(num);
  29. }
  30. }
  31. public Double GetMedian() {
  32. double median = 0;
  33. if (count % 2 == 1) {
  34. median = maxheap.peek();
  35. }
  36. else
  37. median = (minheap.peek()+maxheap.peek())/ 2.0;
  38. return median;
  39. }
  40. }
  • 4.二叉树中和为某一值的路径

输入一颗二叉树的跟节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的list中,数组长度大的数组靠前)

思路:     * 剑指offer思路 博客代码
     * 代码步骤:一个链表记录路径,一个存放这个链表的链表记录最终的结果。
     * 1.首先将根节点放入链表,target减去这个根节点
     * 2.判断是否target同时是叶子节点,如果是就将当前的链表放在结果连表里
     * 3.如果不是,就递归去访问左右子节点。

     * 4.无论是找到没有,都要回退一步、


   
   
   
   
  1. import java.util.ArrayList;
  2. /**
  3. public class TreeNode {
  4. int val = 0;
  5. TreeNode left = null;
  6. TreeNode right = null;
  7. public TreeNode(int val) {
  8. this.val = val;
  9. }
  10. }
  11. */
  12. public class Solution {
  13. private ArrayList> listAll = new ArrayList>();
  14. private ArrayList list = new ArrayList();
  15. private ArrayList> resultList = new ArrayList>();
  16. /**
  17. * 剑指offer思路
  18. * 代码步骤:一个链表记录路径,一个存放这个链表的链表记录最终的结果。前序遍历去访问。先访问根,在递归在左右子树找。注意回退
  19. * 1.首先将根节点放入链表,target减去这个根节点
  20. * 2.判断是否target同时是叶子节点,如果是就将当前的链表放在结果连表里
  21. * 3.如果不是,就递归去访问左右子节点。
  22. * 4.无论是找到没有,都要回退一步、
  23. * @param root
  24. * @param target
  25. * @return
  26. */
  27. public ArrayList> FindPath(TreeNode root, int target) {
  28. if(root == null)
  29. return resultList;
  30. list.add(root.val);
  31. target = target - root.val;
  32. if(target == 0 && root.left == null && root.right == null){
  33. resultList.add( new ArrayList(list));
  34. }
  35. else {
  36. FindPath(root.left, target);
  37. FindPath(root.right, target);
  38. }
  39. // 在返回父节点之前,在路径上删除该结点
  40. list.remove(list.size()- 1);
  41. return resultList;
  42. }
  43. }
  • 5.重建二叉树

输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。

思路:剑指


   
   
   
   
  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. import java.util.Arrays;
  11. public class Solution {
  12. public TreeNode reConstructBinaryTree(int[] pre, int[] in) {
  13. if (pre == null || in == null) {
  14. return null;
  15. }
  16. if (pre.length == 0 || in.length == 0) {
  17. return null;
  18. }
  19. if (pre.length != in.length) {
  20. return null;
  21. }
  22. TreeNode root = new TreeNode(pre[ 0]); //第一个
  23. for ( int i = 0; i < in.length; i++) {
  24. if (pre[ 0] == in[i]) {
  25. //pre的0往后数i个是左子树的,copyofrange包含前面的下标,不包含后面的下标
  26. //in的i往前数i个是左子树的。
  27. root.left = reConstructBinaryTree(Arrays.copyOfRange(pre, 1, i + 1), Arrays.copyOfRange(in, 0, i));
  28. //注意in是从i+1开始,因为i是现在的根,i+1开始才是右子树
  29. root.right = reConstructBinaryTree(Arrays.copyOfRange(pre, i + 1, pre.length),
  30. Arrays.copyOfRange(in, i + 1, in.length));
  31. }
  32. }
  33. return root;
  34. }
  35. }
  • 6.树的子结构

输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)

思路:   //先从根开始再把左作为根,再把右作为根由本函数决定。把一个为根的时候的具体比对过程是第二个函数决定。
    //从根可以认为是一颗树,从左子树开始又可以认为是另外一颗树,从右子树开始又是另外一棵树。
    //本函数就是判断这一整颗树包不包含树2,如果从根开始的不包含,就从左子树作为根节点开始判断,
    //再不包含从右子树作为根节点开始判断。
    //是整体算法递归流程控制。



   
   
   
   
  1. /**
  2. public class TreeNode {
  3. int val = 0;
  4. TreeNode left = null;
  5. TreeNode right = null;
  6. public TreeNode(int val) {
  7. this.val = val;
  8. }
  9. }
  10. */
  11. public class Solution {
  12. //先从根开始再把左作为根,再把右作为根由本函数决定。把一个为根的时候的具体比对过程是第二个函数决定。
  13. //从根可以认为是一颗树,从左子树开始又可以认为是另外一颗树,从右子树开始又是另外一棵树。
  14. //本函数就是判断这一整颗树包不包含树2,如果从根开始的不包含,就从左子树作为根节点开始判断,
  15. //再不包含从右子树作为根节点开始判断。
  16. //是整体算法递归流程控制。
  17. public boolean HasSubtree(TreeNode root1,TreeNode root2) {
  18. boolean res = false;
  19. if (root1 != null && root2 != null) {
  20. if(root1.val == root2.val){
  21. res = doesTree1haveTree2(root1,root2);
  22. }
  23. if(!res)
  24. {
  25. res = HasSubtree(root1.left, root2);
  26. }
  27. if(!res)
  28. {
  29. res = HasSubtree(root1.right, root2);
  30. }
  31. }
  32. return res;
  33. }
  34. //本函数,判断从当前的节点 ,开始两个树能不能对应上,是具体的比对过程
  35. public boolean doesTree1haveTree2(TreeNode root1,TreeNode root2) {
  36. if(root2 == null)
  37. return true;
  38. if(root1 == null)
  39. return false;
  40. if(root1.val != root2.val){
  41. return false;
  42. }
  43. //如果根节点可以对应上,那么就去分别比对左子树和右子树是否对应上
  44. return doesTree1haveTree2(root1.left, root2.left) && doesTree1haveTree2(root1.right, root2.right);
  45. }
  46. }
  • 7.二叉树的镜像

操作给定的二叉树,将其变换为源二叉树的镜像。


   
   
   
   
  1. 二叉树的镜像定义:源二叉树
  2. 8
  3. / \
  4. 6 10
  5. / \ / \
  6. 5 7 9 11
  7. 镜像二叉树
  8. 8
  9. / \
  10. 10 6
  11. / \ / \
  12. 11 9 7 5

   
   
   
   
  1. /**
  2. public class TreeNode {
  3. int val = 0;
  4. TreeNode left = null;
  5. TreeNode right = null;
  6. public TreeNode(int val) {
  7. this.val = val;
  8. }
  9. }
  10. */
  11. import java.util.Stack;
  12. public class Solution {
  13. /**
  14. * 算法步骤
  15. * 1.节点为空直接返回
  16. * 2.如果这个节点的左右子树不为空,就交换。
  17. * 3.递归对这个节点的左子树进行求镜像。对这个节点的右子树求镜像。
  18. * @param root
  19. */
  20. public void Mirror(TreeNode root){
  21. if (root == null) {
  22. return;
  23. }
  24. if(root.left != null || root.right != null) {
  25. TreeNode temp = root.left;
  26. root.left = root.right;
  27. root.right = temp;
  28. Mirror(root.left);
  29. Mirror(root.right);
  30. }
  31. }
  32. /*public void Mirror(TreeNode root) {
  33. if (root == null) {
  34. return;
  35. }
  36. Stack stack = new Stack();
  37. stack.push(root);
  38. while (!stack.isEmpty()) {
  39. TreeNode node = stack.pop();
  40. if (node.left != null || node.right != null) {
  41. TreeNode temp = node.left;
  42. node.left = node.right;
  43. node.right = temp;
  44. }
  45. if(node.left != null)
  46. stack.push(node.left);
  47. if(node.right != null)
  48. stack.push(node.right);
  49. }
  50. }*/
  51. }

8、二叉搜素树的后序遍历序列 

输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。


   
   
   
   
  1. public class Solution {
  2. /**二叉搜索树的性质:
  3. * 所有左子树的节点小于根节点,所有右子树的节点值大于根节点的值。
  4. * 算法步骤:
  5. * 1.后序遍历的最后一个值为root,在前面的数组中找到第一个大于root值的位置。
  6. * 2.这个位置的前面是root的左子树,右边是右子树。然后左右子树分别进行这个递归操作。
  7. * 3.其中,如果右边子树中有比root值小的直接返回false
  8. * @param sequence
  9. * @return
  10. */
  11. public boolean VerifySquenceOfBST(int [] sequence) {
  12. if (sequence == null || sequence.length == 0)
  13. return false;
  14. return IsBST(sequence, 0, sequence.length - 1);
  15. }
  16. public boolean IsBST(int [] sequence, int start, int end) {
  17. if(start >= end) //注意这个条件的添加// 如果对应要处理的数据只有一个或者已经没
  18. //有数据要处理(start>end)就返回true
  19. return true;
  20. int index = start;
  21. for (; index < end; index++) { //寻找大于root的第一个节点,然后再分左右两部分
  22. if(sequence[index] > sequence[end])
  23. break;
  24. }
  25. for ( int i = index; i < end; i++) { //若右子树有小于根节点的值,直接返回false
  26. if (sequence[i] < sequence[end]) {
  27. return false;
  28. }
  29. }
  30. return IsBST(sequence, start, index- 1) && IsBST(sequence, index, end- 1);
  31. }
  32. } /*当案例为{4,6,7,5}的时候就可以看到:
  33. (此时start为0,end为3)
  34. 一开始index处的值为1,左边4的是start为0,index-1为0,下一次递归的start和end是一样的,true!
  35. 右边,start为1,end-1为2,是{6,7}元素,下一轮递归是:
  36. ————7为root,index的值指向7,
  37. ————所以左边为6,start和index-1都指向6,返回true。
  38. ————右边index指向7,end-1指向6,这时候end > start!如果这部分还不返回true,下面的数组肯定超了 */

9、二叉搜索树与双向链表

题目描述

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。


   
   
   
   
  1. /**
  2. public class TreeNode {
  3. int val = 0;
  4. TreeNode left = null;
  5. TreeNode right = null;
  6. public TreeNode(int val) {
  7. this.val = val;
  8. }
  9. }
  10. */
  11. public class Solution {
  12. /**二叉搜索树的中序遍历就是递增的排序,所以就运用中序遍历方法来做。
  13. * 算法思想:
  14. * 中序遍历的步骤,只不过在递归的中间部分不是输出节点值,而是调整指针指向。
  15. * 10
  16. * /\
  17. * 5 12
  18. * /\
  19. * 4 7
  20. * 步骤记住就行,第一次执行,到4的时候,head和resulthead都指向这个
  21. * 指针调整的其中一步:4是head 5是pRootOfTree 然后调整head右指向5,5左指向4,然后5变成head就行了。
  22. * @param pRootOfTree
  23. * @return
  24. */
  25. TreeNode head = null;
  26. TreeNode resultHead = null; //保存生成链表的头结点,便于程序返回
  27. public TreeNode Convert(TreeNode pRootOfTree) {
  28. ConvertSub(pRootOfTree);
  29. return resultHead;
  30. }
  31. public void ConvertSub(TreeNode pRootOfTree) {
  32. if(pRootOfTree == null)
  33. return;
  34. ConvertSub(pRootOfTree.left);
  35. if(head == null){
  36. head = pRootOfTree;
  37. resultHead = pRootOfTree;
  38. }
  39. else {
  40. head.right = pRootOfTree;
  41. pRootOfTree.left = head;
  42. head = pRootOfTree;
  43. }
  44. ConvertSub(pRootOfTree.right);
  45. }
  46. }

10、二叉树的深度

题目描述

输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。


   
   
   
   
  1. /**
  2. public class TreeNode {
  3. int val = 0;
  4. TreeNode left = null;
  5. TreeNode right = null;
  6. public TreeNode(int val) {
  7. this.val = val;
  8. }
  9. }
  10. */
  11. public class Solution {
  12. // 注意最后加1,因为左右子树的深度大的+根节点的深度1
  13. public int TreeDepth(TreeNode root) {
  14. if(root == null)
  15. return 0;
  16. int left = TreeDepth(root.left);
  17. int right = TreeDepth(root.right);
  18. return left > right? left + 1:right+ 1;
  19. }
  20. }

11、平衡二叉树

输入一棵二叉树,判断该二叉树是否是平衡二叉树

描述:如果某二叉树中任意节点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树。


   
   
   
   
  1. public class Solution {
  2. // 注意使用全局变量
  3. boolean isBalance = true;
  4. public boolean IsBalanced_Solution(TreeNode root) {
  5. lengthOfTree(root);
  6. return isBalance;
  7. }
  8. private int lengthOfTree(TreeNode root) {
  9. if (root == null)
  10. return 0;
  11. int left = lengthOfTree(root.left);
  12. int right = lengthOfTree(root.right);
  13. if (Math.abs(left - right) > 1)
  14. isBalance = false;
  15. return Math.max(left, right) + 1;
  16. }
  17. }

第二种Better思路:从底向上判断,这样可以记录下一层的深度


   
   
   
   
  1. public class Solution {
  2. public boolean IsBalanced(TreeNode root) {
  3. int depth = 0;
  4. return IsBalanced(root, depth);
  5. }
  6. public boolean IsBalanced(TreeNode root, int depth) {
  7. if (root == null) {
  8. depth = 0;
  9. return true;
  10. }
  11. int left = 0, right = 0;
  12. if (IsBalanced(root.left, left) && IsBalanced(root.right, right)) {
  13. int diff = left - right;
  14. if (diff <= 1 && diff >= - 1) {
  15. depth = 1 + (left > right ? left : right);
  16. return true;
  17. }
  18. }
  19. return false;
  20. }
  21. }

12、二叉树的下一个节点

题目描述

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


   
   
   
   
  1. /*
  2. public class TreeLinkNode {
  3. int val;
  4. TreeLinkNode left = null;
  5. TreeLinkNode right = null;
  6. TreeLinkNode next = null;
  7. TreeLinkNode(int val) {
  8. this.val = val;
  9. }
  10. }
  11. */
  12. public class Solution {
  13. /**参考左程云和有详解博客的思路,
  14. * 主要分三种:
  15. * 1.如果有右孩子,后继节点就是最左边的
  16. * 2.如果没有右孩子,判断是否是父节点的左孩子,是的话,返回,不是继续网上找
  17. * 3.找不到就是null
  18. * @param pNode
  19. * @return
  20. */
  21. public TreeLinkNode GetNext(TreeLinkNode pNode)
  22. {
  23. if(pNode == null)
  24. return null;
  25. // 如果有右子树,则找右子树的最左节点
  26. if (pNode.right != null) {
  27. pNode = pNode.right;
  28. // 如果此时pNode没有左子树,那么它就是下一个结点 ,就是最左边的了
  29. //如果有左子树,那就在左子树找最左边的
  30. while(pNode.left != null)
  31. pNode = pNode.left;
  32. return pNode;
  33. }
  34. //// 非跟结点,并且没有右子树
  35. while(pNode.next != null) {
  36. // 找到一个结点是该其父亲的左孩子 ,找到就是返回父节点作为后记
  37. if (pNode.next.left == pNode)
  38. return pNode.next;
  39. //找不到这个左孩子的,就继续往上,next其实是parent
  40. pNode = pNode.next;
  41. }
  42. return null;
  43. }
  44. }

 13、对称的二叉树

请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。


   
   
   
   
  1. /*
  2. public class TreeNode {
  3. int val = 0;
  4. TreeNode left = null;
  5. TreeNode right = null;
  6. public TreeNode(int val) {
  7. this.val = val;
  8. }
  9. }
  10. */
  11. public class Solution {
  12. //利用递归进行判断,
  13. //若左子树的左孩子等于右子树的右孩子且左子树的右孩子等于右子树的左孩子,
  14. //并且左右子树节点的值相等,则是对称的。
  15. boolean isSymmetrical(TreeNode pRoot)
  16. {
  17. if (pRoot == null)
  18. return true;
  19. return isCommon(pRoot.left,pRoot.right);
  20. }
  21. public boolean isCommon(TreeNode leftNode, TreeNode rightNode) {
  22. if (leftNode == null && rightNode == null)
  23. return true;
  24. if (leftNode != null && rightNode != null) {
  25. return leftNode.val == rightNode.val &&
  26. isCommon(leftNode.left, rightNode.right) &&
  27. isCommon(leftNode.right, rightNode.left);
  28. }
  29. return false;
  30. }
  31. }

14、序列化二叉树

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

 

这段代码是按照先序遍历的方法来做的: 


   
   
   
   
  1. /*
  2. public class TreeNode {
  3. int val = 0;
  4. TreeNode left = null;
  5. TreeNode right = null;
  6. public TreeNode(int val) {
  7. this.val = val;
  8. }
  9. }
  10. */
  11. import java.util.LinkedList;
  12. import java.util.Queue;
  13. public class Solution {
  14. //主要运用左程云的编程思想的方式来实现
  15. String Serialize(TreeNode root) {
  16. if(root == null)
  17. return "#!";
  18. String res = root.val+ "!";
  19. res = res + Serialize(root.left);
  20. res = res + Serialize(root.right);
  21. return res;
  22. }
  23. TreeNode Deserialize(String str) {
  24. String [] values = str.split( "!");
  25. Queue queue = new LinkedList();
  26. for ( int i = 0; i < values.length; i++) {
  27. queue.offer(values[i]);
  28. }
  29. return reconPre(queue);
  30. }
  31. TreeNode reconPre(Queue queue) {
  32. String value = queue.poll();
  33. if(value.equals( "#"))
  34. return null;
  35. TreeNode head = new TreeNode(Integer.valueOf(value));
  36. head.left = reconPre(queue);
  37. head.right = reconPre(queue);
  38. return head;
  39. }
  40. }

 

 

 

 

 

 

 

 

三、字符串

  • 1.正则表达式的匹配

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

思路:参考网上的思路,主要就是分程序中的两种情况来讨论。第二位是不是*


   
   
   
   
  1. /*
  2. 当模式中的第二个字符不是“*”时:
  3. 1、如果字符串第一个字符和模式中的第一个字符相匹配,
  4. 那么字符串和模式都后移一个字符,然后匹配剩余的。
  5. 2、如果字符串第一个字符和模式中的第一个字符相不匹配,直接返回false。
  6. 而当模式中的第二个字符是“*”时:
  7. 如果字符串第一个字符跟模式第一个字符不匹配,则模式后移2个字符,继续匹配。
  8. 如果字符串第一个字符跟模式第一个字符匹配,可以有3种匹配方式:
  9. 1、模式后移2字符,相当于x*被忽略;
  10. 2、字符串后移1字符,模式后移2字符; 相当于x*算一次
  11. 3、字符串后移1字符,模式不变,即继续匹配字符下一位,因为*可以匹配多位,相当于算多次
  12. 这里需要注意的是:Java里,要时刻检验数组是否越界。*/
  13. public class Zhengze {
  14. public boolean match(char[] str, char[] pattern) {
  15. if (str == null || pattern == null) {
  16. return false;
  17. }
  18. int strIndex = 0;
  19. int patternIndex = 0;
  20. return matchCore(str, strIndex, pattern, patternIndex);
  21. }
  22. public boolean matchCore(char[] str, int strIndex, char[] pattern, int patternIndex) {
  23. // 有效性检验:str到尾,pattern到尾,匹配成功
  24. if (strIndex == str.length && patternIndex == pattern.length)
  25. return true;
  26. // pattern先到尾,匹配失败
  27. if (strIndex != str.length && patternIndex == pattern.length)
  28. return false;
  29. // 模式第2个是*,且字符串第1个跟模式第1个匹配,分3种匹配模式;如不匹配,模式后移2位
  30. if (patternIndex + 1 < pattern.length && pattern[patternIndex + 1] == '*') {
  31. if ((strIndex != str.length && pattern[patternIndex] == str[strIndex])
  32. || (pattern[patternIndex] == '.' && strIndex != str.length)) {
  33. return // 模式后移2,视为x*匹配0个字符
  34. matchCore(str, strIndex, pattern, patternIndex + 2)
  35. // 视为模式匹配1个字符
  36. || matchCore(str, strIndex + 1, pattern, patternIndex + 2)
  37. // *匹配1个,再匹配str中的下一个
  38. || matchCore(str, strIndex + 1, pattern, patternIndex);
  39. } else {
  40. return matchCore(str, strIndex, pattern, patternIndex + 2);
  41. }
  42. } // 模式第2个不是*,且字符串第1个跟模式第1个匹配,则都后移1位,否则直接返回false
  43. if ((strIndex != str.length && pattern[patternIndex] == str[strIndex])
  44. || (pattern[patternIndex] == '.' && strIndex != str.length)) {
  45. return matchCore(str, strIndex + 1, pattern, patternIndex + 1);
  46. }
  47. return false;
  48. }
  49. }
  • 2.表示数值的字符串

请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100","5e2","-123","3.1416"和"-1E-16"都表示数值。 但是"12e","1a3.14","1.2.3","+-5"和"12e+4.3"都不是。

思路:按照一定的规则,如果第一位是+或-,就后移一位。

如果是数字,索引后移,数字表示1.

如果是点,要判断至此点的数量和e的数量是否已经有了,因为java 中e要求后面为整数,如果有了肯定false。索引后移,dotnum增加。

如果是e,判断是否重复e,或者前面没有数字返回false。enum++, 索引++,此时还要判断最后一位是不是e或者+或者-,如果是false。


   
   
   
   
  1. public class StrexpressNum {
  2. /*例子:
  3. * 110 1a1 1.1.1 2.2 12e
  4. *
  5. * */
  6. public static boolean isNumeric(char[] str) {
  7. if(str == null)
  8. return false;
  9. int length = str.length;
  10. int dotNum = 0; //记录点的数量
  11. int index = 0; //索引
  12. int eNum = 0; //记录e的数量
  13. int num = 0; //记录数字的数量
  14. if (str[ 0] == '+' || str[ 0] == '-') {
  15. index++;
  16. }
  17. while (index < length) {
  18. if(str[index]>= '0' && str[index]<= '9') {
  19. index++;
  20. num = 1;
  21. // .前面可以没有数字,所以不需要判断num是否为0
  22. } else if(str[index]== '.') {
  23. // e后面不能有.,e的个数不能大于1.java科学计数要求aeb,b为整数
  24. if(dotNum > 0 || eNum > 0)
  25. return false;
  26. dotNum++;
  27. index++;
  28. } else if(str[index] == 'e' || str[index] == 'E') {
  29. // 重复e或者e前面没有数字
  30. if(eNum > 0 || num == 0)
  31. return false;
  32. eNum++;
  33. index++;
  34. // 符号不能在最后一位
  35. if(index < length &&(str[index]== '+'||str[index]== '-'))
  36. index++;
  37. // 表示e或者符号在最后一位
  38. if(index == length)
  39. return false;
  40. } else {
  41. return false;
  42. }
  43. }
  44. return true;
  45. }
  46. public static void main(String[] args) {
  47. char [] str = { '1', '2', 'e'};
  48. System.out.println(isNumeric(str));
  49. }
  50. }

或者用正则表达式来匹配:

[+-]? 表示+或者-出现0次或1次。[0-9]{0,}表示0到9出现0次或者更多次。()表示这个分组作为一个整体。 \\.?表示.出现0次或1次。[0-9]{1,}表示0到9出现1次或者多次。()表示一个分组。如果把两个分组去掉进行判断是不准确的。100匹配到[0-9]{1,}出错。


   
   
   
   
  1. public boolean isNumeric(char[] str) {
  2. String res = String.valueOf(str);
  3. return res.matches( "[+-]?[0-9]{0,}(\\.?[0-9]{1,})?([Ee][+-]?[0-9]{1,})?");
  4. }
  • 3.0第一个只出现一次的字符

题目描述

在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写).


   
   
   
   
  1. import java.util.LinkedHashMap;
  2. public class Solution {
  3. public int FirstNotRepeatingChar(String str) {
  4. //这个hashmap有序,所以用这个
  5. LinkedHashMap map= new LinkedHashMap<>();
  6. //遍历字符串,第一次设为1 否则就加
  7. for ( int i = 0; i < str.length(); i++) {
  8. if (!map.containsKey(str.charAt(i))) {
  9. map.put(str.charAt(i), 1);
  10. }
  11. else
  12. map.put(str.charAt(i),map.get(str.charAt(i))+ 1);
  13. }
  14. //找出现次数为1的
  15. for ( int i = 0; i < str.length(); i++) {
  16. if (map.get(str.charAt(i)) == 1) {
  17. <

你可能感兴趣的:(剑指offer所有的题目总结)