面试题2:单例模式
只有一个实例 自我实例化 提供全局访问点
饿汉模式、非线程安全的懒汉模式、线程安全的懒汉模式。
① 暴力法:先排序再查找
public boolean duplicate ( int numbers[ ] , int length, int [ ] duplication) {
if ( length <= 1 ) {
return false ;
}
Arrays. sort ( numbers) ;
for ( int i = 0 ; i < length - 1 ; i++ ) {
if ( numbers[ i] == numbers[ i + 1 ] ) {
duplication[ 0 ] = numbers[ i] ;
return true ;
}
}
return false ;
}
② hash表
public boolean duplicate ( int numbers[ ] , int length, int [ ] duplication) {
if ( length <= 1 ) {
return false ;
}
HashMap< Integer, Integer> map = new HashMap < > ( ) ;
for ( int i = 0 ; i < length; i++ ) {
if ( map. containsKey ( numbers[ i] ) ) {
duplication[ 0 ] = numbers[ i] ;
return true ;
}
map. put ( numbers[ i] , 1 ) ;
}
return false ;
}
③ 元素交换
public boolean duplicate ( int numbers[ ] , int length, int [ ] duplication) {
if ( length <= 1 ) {
return false ;
}
for ( int i = 0 ; i < length; i++ ) {
while ( numbers[ i] != i) {
if ( numbers[ i] == numbers[ numbers[ i] ] ) {
duplication[ 0 ] = numbers[ i] ;
return true ;
}
int temp = numbers[ i] ;
numbers[ i] = numbers[ temp] ;
numbers[ temp] = temp;
}
}
return false ;
}
2. 题目二:不修改数组找出重复的数字
直接使用上面的hash表法,顺序遍历数组的同时,又能判断元素是否重复。
hash表:先遍历数组,求得每个数字出现的次数;再遍历hash表,查找出现次数为1的数字。 位操作(很简单): 因为
a
⊕
a
=
0
,
a
⊕
0
=
a
a ⊕ a=0,a ⊕0=a
a ⊕ a = 0 , a ⊕ 0 = a ,这里所有的元素都出现两次,除了只出现一次的数字,剩余元素做异或后都为0。
leetcode上的 240. 搜索二维矩阵 II 矩阵中的元素按行升序,按例也是升序的,暴力查找效率不高。 从右上角的元素开始进行比较,可以剔除行或者列,达到缩小搜索范围的效果。
public boolean Find ( int target, int [ ] [ ] array) {
if ( array. length == 0 || array[ 0 ] . length == 0 ) {
return false ;
}
int row = 0 , col = array[ 0 ] . length - 1 ;
boolean result = false ;
while ( row < array. length && col >= 0 ) {
if ( array[ row] [ col] == target) {
result = true ;
break ;
} else if ( array[ row] [ col] > target) {
col-- ;
} else {
row++ ;
}
}
return result;
}
① 不考虑原地替换
遍历原来的字符串,遇到非空格字符,直接添加到结果字符串;否则,将%20
添加到结果字符串。 使用了StringBuffer.append()方法
实现字符串的添加,StringBuffer.toString()方法
实现类型的转换。 注意: 无需考虑字符串长度为0的情况。
public String replaceSpace ( StringBuffer str) {
int len = str. length ( ) ;
StringBuffer result = new StringBuffer ( ) ;
for ( int i = 0 ; i < len; i++ ) {
if ( str. charAt ( i) == ' ' ) {
result. append ( "%20" ) ;
} else {
String temp = str. charAt ( i) + "" ;
result. append ( temp) ;
}
}
return result. toString ( ) ;
}
② 直接使用Java的API
使用String.replace()方法
,将字符串中的空格替换为%20
。但是,运行效果貌似并不好。
public String replaceSpace ( StringBuffer str) {
String result= str. toString ( ) ;
return result. replace ( " " , "%20" ) ;
}
③ 使用双指针
两次遍历 ,第一次根据字符串中的空格,对字符串进行扩充:在末尾添加空格;第二次遍历,从后往前,减少字符的移动次数。使用双指针 ,一个指向原字符串,一个用于指向扩充后的字符串。
public String replaceSpace ( StringBuffer str) {
int p = str. length ( ) - 1 ;
for ( int i = 0 ; i <= p; i++ ) {
if ( str. charAt ( i) == ' ' ) {
str. append ( " " ) ;
}
}
int q = str. length ( ) - 1 ;
while ( p >= 0 && q>= p) {
if ( str. charAt ( p) == ' ' ) {
str. setCharAt ( q-- , '0' ) ;
str. setCharAt ( q-- , '2' ) ;
str. setCharAt ( q-- , '%' ) ;
} else {
str. setCharAt ( q-- , str. charAt ( p) ) ;
}
p-- ;
}
return str. toString ( ) ;
}
由于合并后的数组还是存储在nums1中的
,如果结束合并时nums1有剩余
,不用移动
;如结束合并时nums2有剩余,需要移动到nums1中。 这里使用的是原地合并,如果借助ArrayList
进行合并,可以从前往后合并。而且需要考虑nums1有剩余时,将剩余数字移动到list中。 两个有序链表的合并,也是借助新的链表头,也需要分别考虑两个链表不为未到末尾的情况。
public void merge ( int [ ] nums1, int m, int [ ] nums2, int n) {
int p = m + n - 1 , i = m - 1 , j = n - 1 ;
while ( i >= 0 && j >= 0 ) {
if ( nums1[ i] > nums2[ j] ) {
nums1[ p-- ] = nums1[ i-- ] ;
} else {
nums1[ p-- ] = nums2[ j-- ] ;
}
}
while ( j >= 0 ) {
nums1[ p-- ] = nums2[ j-- ] ;
}
}