二维数组的查找
题目描述

思路
二叉搜索树法
如果对二叉搜索树比较熟悉,你就会发现改数组有二叉树的特征,于是就可以采用二叉树找值的算法
应用在本题中,如果目标数比标志数大,就行+1,小则列-1,循环终止条件为不匹配,且要变动的索引抵达边界
代码实现
class Solution {
public boolean findNumberIn2DArray(int[][] matrix, int target) {
if(matrix.length==0){
return false;
}
int n=matrix.length-1,m=matrix[0].length-1;
for(int i=0,j=m;i<=n&&j>=0;){
if(target>matrix[i][j]){
i++;
}else if(target<matrix[i][j]){
j--;
}else{
return true;
}
}
return false;
}
}
class Solution {
public boolean findNumberIn2DArray(int[][] matrix, int target) {
int i = matrix.length - 1, j = 0;
//正是这一步让其不用判定是否为空数组
while(i >= 0 && j < matrix[0].length)
{
if(matrix[i][j] > target) i--;
else if(matrix[i][j] < target) j++;
else return true;
}
return false;
}
}
复杂度分析
时间复杂度
O(M+N),最坏情况行列都走一边,在对角
空间复杂度
O(1)
反思不足
思路
有序想到二分法,想到先行后列,先列后行,二者都来一遍,4次二分
但是对于条件的判断比较模糊,行之后,如何确定列的索引? 答案是直接返回右指针
参考了题解的二叉搜索树思路后,没有考虑到传递一个空数组的情况,于是索引越界了
以后见到有序的二维数组要把往二叉搜索树方向上想
旋转数组的最小数字
题目描述

思路
二分法
具体细节看代码注释吧,记住二分法是种思想,不要执着于判定条件的变化
记住这种旋转数组的特性,左排列一定全部大于等于右排列
代码实现
class Solution {
public int minArray(int[] numbers) {
int left=0,right=numbers.length-1;
int mid=left+(right-left)/2;
while(left!=right){
//前两步很好理解,目的是找到旋转点,也即边界点,mid大于right时,mid肯定在左,小于right,肯定在右,而且可能处于边界点,所以不-1
//不用到left与mid比较是因为有right足够,这两者唯一能确定的是在mid小于left时,mid肯定在右
if(numbers[mid]<numbers[right]){
right=mid;
}else if(numbers[mid]>numbers[right]){
left=mid+1;
//原始处理方案:right-=1
//等于时,如果mid落在左,且right不处于边界,-1可能遇到更小值,于是往right靠,right处于边界,后续也只会让right不断等于mid,最终与left相等,而一旦mid落在左,那么就必然left到mid都是同一个值,所以返回的还是最小值
//如果mid落在右,那right肯定不处于边界,-1更新mid,继续缩小范围,且此时肯定有mid到right相等
}else{
//新方案:
//一大片连续的相等,让二分查找失去了意义,于是遍历更直接
//1.1时肯定要么找到一个更小值,要么找到边界值也是right,1.2时,找不到更小值,返回的仍是最小值,2时同1.1
int r=left;
for(int i=r+1;i<right;i++){
if(numbers[i]<numbers[r]){
r=i;
}
}
return numbers[r];
}
mid=left+(right-left)/2;
}
return numbers[left];
}
}
复杂度分析
时间复杂度
平均O(log2N),极端情况,所有元素都相同时,会变成O(N),此时只会让right一个一个减,减到left,即0
空间复杂度
O(1)
反思不足
思路
最开始的思路是二分法+递归,但是发现不对,单纯是想到了很久很久之前做过的题,把它背出来而已
知道应该往二分法的角度思考,但是不知道应该怎么分,什么时候终止,什么时候左右指针变动
看了题解才知道,原来恒有左大于右的性质,而最小值就是边界点、旋转点
看了题解仍是做不出来,甚至理解其思路都费了老大劲,好在磕磕绊绊理解了
第一个只出现一次的字符
题目描述

思路
哈希表
用哈希表构建字符是否是出现了一次
有序哈希表
用有序哈希表构建字符是否是出现了一次
代码实现
哈希表
class Solution {
HashMap<Character,Boolean> map=new HashMap<Character,Boolean>();
public char firstUniqChar(String s) {
char[] chars= s.toCharArray();
for(char c:chars){
map.put(c,!map.containsKey(c));
}
for(char c:chars){
if(map.get(c)){
return c;
}
}
return ' ';
}
}
有序哈希表
class Solution {
LinkedHashMap<Character,Boolean> map=new LinkedHashMap<Character,Boolean>();
public char firstUniqChar(String s) {
char[] chars= s.toCharArray();
for(char c:chars){
map.put(c,!map.containsKey(c));
}
for(Map.Entry<Character,Boolean> entry:map.entrySet()){
if(entry.getValue()){
return entry.getKey();
}
}
return ' ';
}
}
复杂度分析
时间复杂度
均为O(N),但是后者第二遍遍历次数少,字符串越长后者优势越大
空间复杂度
均为O(N)
反思不足
思路
最开始想的是set集合配合哈希表,添加成功时,就把元素加入哈希表中,反之则删除,的确可以达成目的,但是没必要,直接用哈
希表对应其是不是只出现了一次不就完了嘛
可见模式识别不是那么容易用的,不小心还会思维固化
javase
Set有关
add方法添加元素
泛型不能是基本数据类型,Character对应char
Map有关
HashMap无序,插入顺序不代表输出顺序
LinkedHashMap则是有序的
put添加基本元素
remove根据键删除
values获取所有值,而后toArray可以转化成Object数组
size获取键值对数量
containsKey判断是否存在对应键
entrySet返回所有键值对,用Map.Entry<E,E>接收,getValue,getKey
泛型不能是基本数据类型,Character对应char
泛型建议两边都写上,影响返回值类型