剑指Offer学习笔记(3)——解决面试题的思路

容易实现的算不上梦想,轻易放弃的算不上诺言。
要想成功得敢于挑战,有了梦想才有美好的明天!

前言:面试的时候我们经常遇到难题,画图、举例子和分解能够很好的帮助我们解决问题和细化问题。


 

一、画图使得抽象问题形象化

1. 二叉树镜像

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

    	    8
    	   /  \
    	  6   10
    	 / \  / \
    	5  7 9 11
    	镜像二叉树
    	    8
    	   /  \
    	  10   6
    	 / \  / \
    	11 9 7  5
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
public class TreeNode {
     int val = 0;
     TreeNode left = null;
     TreeNode right = null;
 
     public TreeNode(int val) {
         this.val = val;
 
     }
 
}
*/
public  class  Solution {
public  void  Mirror(TreeNode root) {
         //当树为空或者只有根节点的时候是不需要操作的
         if (root== null ||(root.left== null &&root.right== null )){
             return ;
         }
         
         TreeNode temp=root.left;
         root.left=root.right;
         root.right=temp;
         
         if (root.left!= null ){
             Mirror(root.left);
         }
         if (root.right!= null ){
             Mirror(root.right);
         }
 
     }
}
 

2. 顺时针打印矩阵

输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
import  java.util.ArrayList;
public  class  Solution {
    /**
      * print a matrix
      * @param matrix
      * @return
      */
     public  ArrayList<Integer> printMatrix( int [][] matrix) {
 
         if (matrix== null ){
             return  null ;
         }
         
         ArrayList<Integer> aList =  new  ArrayList<Integer>();
         int  rows=matrix.length;
         int  columns=matrix[ 0 ].length;
         int  start= 0 ;
         while (columns>start* 2 &&rows>start* 2 ){
             printMatrixCircle(matrix,rows,columns,start,aList);
             ++start;
         }
         
         return  aList;
     }
 
     /**
      * print the out circle of the matrix
      * @param matrix
      * @param rows
      * @param columns
      * @param start
      * @param aList
      */
     private  void  printMatrixCircle( int [][] matrix,  int  rows,  int  columns,
             int  start, ArrayList<Integer> aList) {
         int  endX=columns- 1 -start;
         int  endY=rows- 1 -start;
         
         //print from left to right
         for ( int  i=start;i<=endX;i++){
             int  num=matrix[start][i];
             System.out.print(num);
             aList.add(num);
         }
         
         //print from top to bottom
         if (start<endY){
             for ( int  j=start+ 1 ;j<=endY;j++){
                 int  num=matrix[j][endX];
                 System.out.print(num);
                 aList.add(num);
             }
         }
         
         //print from right to left
         if (start<endX&&start<endY){
             for ( int  i=endX- 1 ;i>=start;i--){
                 int  num=matrix[endY][i];
                 System.out.print(num);
                 aList.add(num);
             }
         }
         
         //from bottom to top       
         if (start<endX&&start<endY- 1 ){
             for ( int  i=endY- 1 ;i>=start+ 1 ;i--){
                 int  num=matrix[i][start];
                 System.out.print(num);
                 aList.add(num);
             }
         }
         
     }
}
 
 

二、举例让抽象具体化

 

1. 包含min函数的栈

定义栈的数据结构,请在该类型中实现一个能够得到栈最小元素的min函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import  java.util.Stack;
 
public  class  Solution {
 
     
     Stack<Integer> data =  new  Stack<Integer>();
     Stack<Integer> min =  new  Stack<Integer>();
     Integer temp= null ;
 
     public  void  push( int  node) {
         if (min.size()== 0 ){
             data.push(node);
             min.push(node);
         } else {
             int  t = min.peek();
             if (t<node){
                  data.push(node);
                  min.push(t);
             } else {
                  data.push(node);
                  min.push(node);
             }
         }
             
         
     }
 
     public  void  pop() {
         int  num = data.pop();
         int  num2 = min.pop();
     }
 
     public  int  top() {
         return  data.peek();
     }
 
     public  int  min() {
     
         return  min.peek();
     }
}


  •  
 

2. 栈的压入、弹出序列

输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import  java.util.ArrayList;
import  java.util.Stack;
public  class  Solution {
    /**
      * 栈的压入和弹出序列的判断
      *
      * @param pushA
      * @param popA
      * @return
      */
     public  boolean  IsPopOrder( int [] pushA,  int [] popA) {
         if (pushA.length== 0 ||popA.length== 0 ){
             return  false ;
         }
         
         
         boolean  flag =  true ;
         Stack<Integer> s =  new  Stack<Integer>();
         s.push(- 1 ); //标记开始
         
         for  ( int  i =  0 ; i < popA.length; i++) {
             int  num = popA[i];
             int  index= 0 ;
             
             while  (num != s.peek()) {
                 if  (pushA[index] != - 1 ) {
                     s.push(index +  1 );
                     pushA[index] = - 1 ;
                 }
                 index++;
                 if (index>=pushA.length){
                     break ;
                 }
             }
             int  pop = s.pop();
             if (num!=pop){
                 flag= false ;
                 break ;
             }
 
         }
 
         return  flag;
     }
}
 

3. 从上往下打印二叉树

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import  java.util.ArrayList;
import  java.util.LinkedList;
import  java.util.Queue;
 
/**
public class TreeNode {
     int val = 0;
     TreeNode left = null;
     TreeNode right = null;
 
     public TreeNode(int val) {
         this.val = val;
 
     }
 
}
*/
public  class  Solution {
     /**
      * 按照层遍历二叉树算法的设计
      * @param root
      * @return
      */
     public  ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
         ArrayList<Integer> aList =  new  ArrayList<Integer>();
         if (root== null ){
             return  aList;
         }
         
         Queue<TreeNode> qt =  new  LinkedList<TreeNode>();
         qt.offer(root);
         while (qt.size()> 0 ){
             root=qt.poll();
             aList.add(root.val);
             if (root.left!= null ){
                 qt.offer(root.left);
             }
             if (root.right!= null ){
                 qt.offer(root.right);
             }          
         }
         
         return  aList;
     }
}
 

4. 二叉搜索树的后序遍历序列

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public  class  Solution {
     public  boolean  VerifySquenceOfBST( int [] sequence) {
         boolean  flag =  true ;
         if  (sequence ==  null  || sequence.length <=  0 ) {
             return  false ;
         }
         return  VerifySquenceOfBSTRe(sequence, 0 ,sequence.length);
     }
 
     private  boolean  VerifySquenceOfBSTRe( int [] sequence,  int  begin,  int  length) {
         int  root=sequence[begin+length- 1 ];
         int  i=begin;
         for  (; i < length- 1 ; ++i) {
             if  (sequence[i] > root) {
                 break ;
             }
         }
 
         // In BST right subtree is smaller than root
 
         int  j = i;
         for  (; j < length- 1 ; ++j) {
             if  (sequence[j] < root) { //大于等于才是右子树
                 return  false ;
             }
         }
 
         boolean  left =  true ;
         if (i> 0 ){
             left=VerifySquenceOfBSTRe(sequence, 0 ,i);
         }
 
         
         boolean  right =  true ;
         if (i<length- 1 ){
             right=VerifySquenceOfBSTRe(sequence,i+ 1 ,length-i- 1 );
         }
 
         return  left&&right;
     }
}
 

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

输入一颗二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
import  java.util.ArrayList;
import  java.util.Stack;
/**
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>> FindPath(TreeNode root,  int  target) {
         ArrayList<ArrayList<Integer>> pathList =  new  ArrayList<ArrayList<Integer>>();
         if  (root ==  null )
             return  pathList;
         Stack<Integer> stack =  new  Stack<Integer>();
         
         FindPath(root, target, stack, pathList);
         
         return  pathList;
 
     }
 
     private  void  FindPath(TreeNode root,  int  target, Stack<Integer> path,
             ArrayList<ArrayList<Integer>> pathList) {
         
         if  (root ==  null )
             return ;
         //如果左右子树都为空 也就是到达叶子结点
         if  (root.left ==  null  && root.right ==  null ) {
             if  (root.val == target) {
                 ArrayList<Integer> list =  new  ArrayList<Integer>();
                 for  ( int  i : path) { //stack有iterator接口实现
                     list.add( new  Integer(i));
                 }
                 list.add( new  Integer(root.val));
                 pathList.add(list);
             }
         else  {
             //如果不到叶子结点 递归的调用求的
             path.push( new  Integer(root.val));
             FindPath(root.left, target - root.val, path, pathList);
             FindPath(root.right, target - root.val, path, pathList);
             path.pop();
         }
 
     }
 
}
 
 

三、分解让复杂问题简单

 
 

1. 复杂链表的复制

输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
/*
public class RandomListNode {
     int label;
     RandomListNode next = null;
     RandomListNode random = null;
 
     RandomListNode(int label) {
         this.label = label;
     }
}
*/
public class Solution {
     /**
      * 复杂链表的复制
      * @param pHead
      * @return
      */
     public RandomListNode Clone(RandomListNode pHead)    {
         
         //每一个传入的结点都是pHead
         cloneNodes(pHead);
         connectRandomNodes(pHead);
         return reConnectNode(pHead);
         
     }
 
     /**
      * 分离重建两个链表
      * @param pHead
      * @return
      */
     private RandomListNode reConnectNode(RandomListNode pHead) {
         RandomListNode pNode=pHead;
         RandomListNode pClonedHead=null;//复制链表的头结点的设置
         RandomListNode pClonedNode=null;
         
         //
         if(pNode!=null){
             pClonedHead=pClonedNode=pNode.next;
             pNode.next=pClonedNode.next;
             pNode=pNode.next;//向后遍历
         }
         //后续链表的分离复制
         while(pNode!=null){
             pClonedNode.next=pNode.next;
             pClonedNode=pClonedNode.next;
             pNode. next=pClonedNode.next;
             pNode=pNode.next;
             
         }
         
         return pClonedHead;
     }
 
     /**
      * 链接Random
      * @param pHead
      */
     private void connectRandomNodes(RandomListNode pHead) {
         RandomListNode pNode = pHead;
         while(pNode!=null){
             RandomListNode pCloned = pNode.next;
             if(pNode.random!=null){
                 pCloned.random=pNode.random.next;//比着葫芦画瓢
             }
             
             pNode=pCloned.next;
         }
         
     }
 
     /**
      * 结点复制 在同一个链表中
      * @param pHead
      */
     private  void  cloneNodes(RandomListNode pHead) {
         
         RandomListNode pNode=pHead;
         while (pNode!= null ){
             RandomListNode pCloned =  new  RandomListNode(pNode.label);
             pCloned.next=pNode.next;
             pCloned.random= null ;
             
             pNode.next=pCloned; //复制结点
             
             pNode=pCloned.next;
         }
         
     }
}

2. 二叉搜索树与双向链表

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
/**
public class TreeNode {
     int val = 0;
     TreeNode left = null;
     TreeNode right = null;
 
     public TreeNode(int val) {
         this.val = val;
 
     }
 
}
*/
public  class  Solution {
    /**
      * 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向
      *
      * 思路:中序遍历可以输出有序
      * @param pRootOfTree
      * @return
      */
     
     public  TreeNode Convert(TreeNode pRootOfTree) {
         TreeNode  tnLastNodeInList= null ;
         //转换
         tnLastNodeInList=convertNode(pRootOfTree,tnLastNodeInList);
         
         TreeNode tnFirstNodeInList=tnLastNodeInList;
         
         while (tnFirstNodeInList!= null &&tnFirstNodeInList.left!= null ){
             tnFirstNodeInList=tnFirstNodeInList.left;
         }
         
         return  tnFirstNodeInList;
     }
 
     private  TreeNode convertNode(TreeNode pRootOfTree, TreeNode tnLastNodeInList) {
         
         if (pRootOfTree== null ){
             return  tnLastNodeInList;
         }
         
         TreeNode tnCur = pRootOfTree;
         
         //左侧
         if (tnCur.left!= null ){
             tnLastNodeInList=convertNode(tnCur.left, tnLastNodeInList);
         }
         //中间  当前的根结点的前一个(PRE)结点就是链表尾
         tnCur.left=tnLastNodeInList;
         
         if (tnLastNodeInList!= null ){ //只要是当前的链表尾不是空的那么就可以设置当前链表的下一个(POST)结点
             tnLastNodeInList.right=tnCur;
         }
         //链表尾部结点向后移动
         tnLastNodeInList=tnCur;
         //右边重复上边的操作
         if (tnCur.right!= null ){
             tnLastNodeInList=convertNode(tnCur.right, tnLastNodeInList);
         }
         return  tnLastNodeInList;
     }
 
}

3. 字符串的排列

输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。 结果请按字母顺序输出。 
输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
import  java.util.*;
public  class  Solution {
     public  ArrayList<String> Permutation(String str) {
         ArrayList<String> strList =  new  ArrayList<String>();
         if  (str ==  null  || str.equals( "" )) {
             return  strList;
         }
         strList = arrangeStr(str.toCharArray(),  0 , strList);
         Collections.sort(strList);
 
         return  strList;
     }
 
     /**
      * 采用递归策略实现字符串的全排列: 我们将字符序列看作是两部分,一部分是第一个字符,第二部分是后面的所有字符。
      * Step1:首先求出所有可能出现在第一个位置的字符,也就是将第一个字符同后面所有的字符进行交换
      * Step2:固定第一个字符,求后面所有字符的排列序列。 后面的字符我们仍然分为两个部分,第一个字符和第一个字符后面的字符。
      *
      * @param strCharArray
      * @param begin
      * @param strList
      * @return
      */
     private  ArrayList<String> arrangeStr( char [] strCharArray,  int  begin,
             ArrayList<String> strList) {
         // 一直安排到最后一个字符就终止
         if  (begin == strCharArray.length -  1 ) {
             strList.add(String.valueOf(strCharArray));
         else  {
             // 对第一个字符进行设置交换 从不交换开始
             for  ( int  i = begin; i < strCharArray.length; ++i) {
                 if  (strNothas(strCharArray, i, begin)) {
                     char  temp = strCharArray[begin];
                     strCharArray[begin] = strCharArray[i];
                     strCharArray[i] = temp;
 
                     arrangeStr(strCharArray, begin +  1 , strList);
 
                     temp = strCharArray[begin];
                     strCharArray[begin] = strCharArray[i];
                     strCharArray[i] = temp;
                 }
 
             }
         }
 
         return  strList;
     }
 
     private  boolean  strNothas( char [] strCharArray,  int  i,  int  begin) {
         if  (i == begin) {
             return  true ;
         else  {
             for  ( int  j = begin; j < i; j++) {
                 if  (strCharArray[i] == strCharArray[j]) {
                     // 如果在begin到i之间存在一个重复的就不要进行交换
                     return  false ;
                 }
             }
         }
         return  true ;
     }
 
}
 
 
 
 
 
 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值