注意: 该题让求一棵二叉树的镜像,而不是判断两棵树是否为镜像对称。分析:
先画出一个二叉树的镜像: 发现与镜像二叉树相比,根节点不变,左孩子成了右孩子,右孩子成了左孩子。 于是,可以考虑通在先序遍历的过程中,加入交换左右孩子节点的操作。
public TreeNode invertTree ( TreeNode root) {
prevOrder ( root) ;
return root;
}
public void prevOrder ( TreeNode root) {
if ( root == null ) {
return ;
}
TreeNode temp = root. left;
root. left = root. right;
root. right = temp;
prevOrder ( root. left) ;
prevOrder ( root. right) ;
}
如果一颗二叉树关于自身对称,则以根节点为中心,左边的等于右边的,右边等于左边的。
① 比较左右子树
除去根节点,其他节点以根节点为轴对称:左孩子等于右孩子,右孩子等于左孩子。 相当于判断二叉树的左右子树是否镜像对称,在进行子树是否相同的基础上,只需要更改判断的对象就可以实现。
public boolean isSymmetric ( TreeNode root) {
if ( root== null ) {
return true ;
}
return isDuichen ( root. left, root. right) ;
}
public boolean isDuichen ( TreeNode node1, TreeNode node2) {
if ( node1 == null && node2 == null ) {
return true ;
}
if ( node1 == null || node2 == null ) {
return false ;
}
return ( node1. val == node2. val) && isDuichen ( node1. left, node2. right) &&
isDuichen ( node1. right, node2. left) ;
}
② 两个自己进行比较
如果一棵二叉树关于自身对称,两个它关于中间成镜像对称。因此,可以在判断是否对称时,直接传入两个自己。 代码如下:
public boolean isSymmetric ( TreeNode root) {
return isDuichen ( root, root) ;
}
③ 迭代实现
利用两个stack,实现对左右子树的节点的存储。 压入左右子树的孩子节点时,按照左孩子/右孩子、右孩子/左孩子的顺序压入。
public boolean isSymmetric ( TreeNode root) {
if ( root == null ) {
return true ;
}
Stack < TreeNode > s1 = new Stack < > ( ) ;
Stack < TreeNode > s2 = new Stack < > ( ) ;
s1. push ( root. left) ;
s2. push ( root. right) ;
while ( ! s1. isEmpty ( ) && ! s2. isEmpty ( ) ) {
TreeNode left = s1. pop ( ) ;
TreeNode right = s2. pop ( ) ;
if ( left == null && right == null ) {
continue ;
}
if ( left == null || right == null ) {
return false ;
}
if ( left. val != right. val) {
return false ;
}
s1. push ( left. left) ;
s2. push ( right. right) ;
s1. push ( left. right) ;
s2. push ( right. left) ;
}
return true ;
}
矩阵每次打印都是由上方的r1
,下方的r2
,左边的c1
,右边的c2
去共同控制的。 注意: 当从右到左、从下到上的打印之前,需要先判断是否可以停止打印。代码如下:
public List < Integer > spiralOrder ( int [ ] [ ] matrix) {
List < Integer > list = new ArrayList < > ( ) ;
if ( matrix. length == 0 || matrix[ 0 ] . length == 0 ) {
return list;
}
int r1 = 0 , r2 = matrix. length - 1 , c1 = 0 , c2 = matrix[ 0 ] . length - 1 ;
while ( r1 <= r2 && c1 <= c2) {
for ( int i = c1; i <= c2; i++ ) {
list. add ( matrix[ r1] [ i] ) ;
}
for ( int i = r1 + 1 ; i <= r2; i++ ) {
list. add ( matrix[ i] [ c2] ) ;
}
if ( r1 != r2) {
for ( int i = c2 - 1 ; i >= c1; i-- ) {
list. add ( matrix[ r2] [ i] ) ;
}
}
if ( c1 != c2) {
for ( int i = r2 - 1 ; i > r1; i-- ) {
list. add ( matrix[ i] [ c1] ) ;
}
}
r1++ ;
r2-- ;
c1++ ;
c2-- ;
}
return list;
}
最大公约数和最小公倍数
以(319,377)
为例,交换二者的位置,让较大数做被除数。 58 = 377 % 319,则有(319,58)
29 = 319 % 58,则有(58,29)
0 = 58 % 29,则最大公约数为29
public int GCD ( int a, int b) {
int max = Math . max ( a, b) ;
int min = Math . min ( a, b) ;
while ( min != 0 ) {
int temp = max % min;
max = min;
min = temp;
}
return max;
}
求到最大公约数后,将两个数的乘积除以最大公约数,就是最小公倍数:a* b/ GCD(a, b)
。 代码如下:
public static int LCM ( int a, int b) {
int max = Math . max ( a, b) ;
int min = Math . min ( a, b) ;
while ( min != 0 ) {
int temp = max % min;
max = min;
min = temp;
}
return a * b / max;
}
要想调用min
函数时,时间复杂度为
O
(
1
)
O(1)
O ( 1 ) ,直接使用一个min
变量存储栈中的最小值。 比如压入3,2,1,最小值为1,如果弹出了1,那最小值应该是栈中的2还是3呢? 这时,如果查找栈中min
,时间复杂度就不是
O
(
1
)
O(1)
O ( 1 ) 了。
创建一个存储当前栈中最小元素的辅助栈,每次获取min
时,直接获取辅助栈栈顶元素即可。 数据栈弹出一个元素,辅助栈也需要弹出对应的元素。 辅助栈压入的元素为:min(数据栈待压入元素,辅助栈的栈顶元素)
。
class MinStack {
private Stack < Integer > data;
private Stack < Integer > helper;
public MinStack ( ) {
data = new Stack < > ( ) ;
helper = new Stack < > ( ) ;
}
public void push ( int x) {
data. push ( x) ;
int min = ( helper. isEmpty ( ) ) ? x : Math . min ( x, helper. peek ( ) ) ;
helper. push ( min) ;
}
public void pop ( ) {
helper. pop ( ) ;
data. pop ( ) ;
}
public int top ( ) {
return data. peek ( ) ;
}
public int getMin ( ) {
return helper. peek ( ) ;
}
}
面试题31:栈的压入、弹出序列
对于压入序列,如果与弹出序列不相同,则直接压入栈;如果待压入的数字是下一个弹出的数字,则不压入数字,直接逃过。 编程思路:
先压入数字,在将匹配的栈顶元素弹出栈。 如果最后栈不为空,说明不是弹出序列;否则,是弹出序列。 无需进行空数组判断 ,如果数组为空不会进行for循环。
public boolean validateStackSequences ( int [ ] pushed, int [ ] popped) {
Stack < Integer > stack = new Stack < > ( ) ;
int cur = 0 ;
for ( int i = 0 ; i < pushed. length; i++ ) {
stack. push ( pushed[ i] ) ;
while ( ! stack. isEmpty ( ) ) {
if ( stack. peek ( ) == popped[ cur] ) {
stack. pop ( ) ;
cur++ ;
} else {
break ;
}
}
}
return stack. isEmpty ( ) ;
}