您的位置:首页 > 职场人生

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

2016-03-22 21:39 561 查看
容易实现的算不上梦想,轻易放弃的算不上诺言。
要想成功得敢于挑战,有了梦想才有美好的明天!

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

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

1. 二叉树镜像

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

二叉树的镜像定义:源二叉树

8
/  \
610
/ \  / \
5  7 9 11
镜像二叉树
8
/  \
106
/ \  / \
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
;

}


}


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: