Java刷题总结 —— 数组-二叉树篇

        开个贴记录自己在刷leetcode时部分题目的思路,以及在编写时漏掉的一些代码细节,学习到的一些代码技巧~~

 一、数组

1. 二分法 704

主要用到了二分法进行查找,注意左右边界的选择(我选左闭右开),此外就是在求中间的下标时,定义int middle = left + (right - left) / 2 效果 等同于(left + right)/2,但是用前者可以防止溢出例如left和right都是最大int时,left + right会越界。

2. 有序查找 35

讨论4种情况:

1.目标在数组所有元素之前

2.目标等于数组中的某一个元素

3.目标插入数组中的位置

4.目标在数组所有元素之后

除了能找到target返回middle,此外都返回left right +1

3. 开始位置/结束位置 34

开始:第一个等于target的

结束:第一个大于target-1

没有找到元素:返回[-1,-1]

4. x的平方根 69(未完)

思路不对,留个坑吧~~

双指针法

5. 移除数组元素 27

采用快慢指针法。若使用暴力解法:注意i-- 因为下标i以后的数值都向前移动了一位,所以i也向前移动一位,否则会跳过i后面一位

6. 删除有序数组中的重复项 26

最后返回的时候,记住返回slow+1,因为总长度为slow+1,slow从0开始

7. 移动0 283

还是slow的问题,因为判断完nums[fast] != 0后slow会进行自增++,所以总长为slow,数组下标为slow-1;

8. 有序数组的平方 977

新思路:数组其实是有序的, 只不过负数平方之后可能成为最大数了。

那么数组平方的最大值就在数组的两端,不是最左边就是最右边,不可能是中间。

滑动窗口法

9.  长度最小的子数组 209

窗口的长度不一样长!

  1. 找最小:

满足条件时:

left++,再判断if,满足则left继续右移动,缩小窗口;不满足则right++,一直到right到右边界

不满足条件:

right++,直到满足条件

如果窗口中元素的和大于target,就开始缩小窗口,然后更新最小滑动窗口

    2.其他条件下思路也一样

    3.遗忘

Int(4字节)

Integer.MAX_VALUE表示int数据类型的最大取值数:2 147 483 647
Integer.MIN_VALUE表示int数据类型的最小取值数:-2 147 483 648

10. 无重复字符的最长子串 9

左边界的移动:

如果当前字符包含在 map中,此时有2类情况:

  (1)如:abca,当我们遍历到第二个a,当前有效最长子段是 abc,我们又遍历到a,此时更新 left 为map.get(a)+1=1,

当前有效子段更新为 bca;

(2)如:abba,我们先添加a,b进map,此时left=0,我们再添加b,发现map中包含b, left=map.get(b)+1=2,

此时子段更新为 b,而且map中仍然包含a,且map.get(a)=0;

 随后,我们遍历到a,发现a包含在map中,且map.get(a)=0,如果像(1)一样处理,就会发现 left=map.get(a)+1=1,实际上,left此时应该不变,left始终为2,子段变成 ba才对。因此:

 left=Math.max(left ,map.get(c)+1).

二、链表

1. 删除链表的倒数第 N 个结点 19

1.条件为fast != null,需要走n+1

for(int i = 0; i < n + 1 ; i++){  

            fast = fast.next;

    }

  while( fast != null){}

  2.条件fast.next != null,走n

for(int i = 0; i < n  ; i++){  

      fast = fast.next;

  }

 while( fast.next != null){}

注意:边界条件:i<n,因为是快指针fast.next为空,而不是快指针移到空位置

 2. 链表相交 160

循环遍历链表长度时,使用cur.next报错-空指针异常——>错误原因:没有考虑[ ]空链表的情况

正确写法:

int sizeB = 0;

        while(curA != null){

            sizeA++;

            curA = curA.next;

        }

3. 环形链表II 142

要点:

  1. 如何证明链表有环

               答:快慢指针,重合则有环

     2. 如何找到入环点

               2(x+y)=n(z+y)+y+x

4.有效的字母异位词 142

小技巧:record[s.charAt(i) - 'a']++;

若s.charAt(i)为z,则相减表示z-a之间的差距:25,即record[25];若为a,则record[0]

5. 两数之和 1

    在遍历数组的时候,只需要向map去查询是否有和目前遍历元素匹配的数值,如果有,就找到的匹配对,如果没有,就把目前遍历的元素放进map中

6. 四数相加 II 454

新方法:Map.getOrDefault(key,默认值);

如果 在Map中存在key,则返回key所对应的value。如果 在Map中不存在key,则返回默认值。

省去了if判断

7. 赎金信 383

在本题的情况下,使用map的空间消耗要比数组大一些的,因为map要维护红黑树或者哈希表,而且还要做哈希函数,费时

8. 四数之和 18(未完)

留坑:为什么内层循环不用剪枝?

三、字符串

1. 反转字符串 344

除了使用StringBulider的reverse()方法外,可以使用双指针,^异或运算

// 使用异或交换元素 底层实际是二进制码之间的异或运算

如:a ^ b => 97 ^ 98 = 3

即0110 0001 ^0110 0010 = 00000011=3

2. 反转字符串中的单词 151

具体步骤为:

  1. 移除多余空格
  2. 将整个字符串反转
  3. 将每个单词反转

翻转单个单词:

for(int fast = 1;fast < A.length; fast++)

 if(A [fast] == ' '){      

     reverseAll(A,slow,fast - 1);       

      slow = fast + 1;

  }

  if(fast == A.length - 1){

      reverseAll(A,slow,fast);

       }

} 

条件可改在一起判断,即:

for(int fast = 1;fast <= A.length; fast++){

     if(fast == A.length||A [fast] == ' '){
        ......

     }
}

  必须是A.length在前,否则当fast为长度时,先判断A[fast]会导致下标越界

KMP算法

  • KMP主要应用在字符串匹配上。—— K 快速 M模板 P匹配

  • 主要思想:当出现字符串不匹配时,可以知道一部分之前已经匹配的文本内容,可以利用这些信息避免从头再去做匹配。

        next数组:前缀表是用来回退的,它记录了模式串与主串(文本串)不匹配的时候,模式串应该从哪里开始重新匹配。

3. 找出字符串中第一个匹配项的下标 28

最后返回 return i - needle.length() + 1;

4. 重复的子字符串 459

// 判断是否能整除     

 if(next[]> 0 && N % (N-next[]) == 0){

            return true;

 }

最大前后缀不包含的地方 ——> 公共的

长度能够整除公共 ——>重复

四、栈与队列

1. 删除字符串中的所有相邻重复项 1047

在取栈中的元素时,字符串的拼接可以实现反转

String str = "";

        while (!deque.isEmpty()) {

            str = deque.pop() + str;

}

如第一次pop() a,str=a;第二次pop()h

str=h+a=ha,实现(ah)——>ha,注意str的拼接顺序!

2. 逆波兰表达式求值 150

Integer.valueOf(tokens[i]) 把字符转换成int

for (String s : tokens)等价于

for(int i = 0; i < tokens.length; i++)

token[i]==s

3. 前 K 个高频元素 347

  1. 优先级队列PriorityQueue 默认是小栈堆
PriorityQueue<Integer> heap = new PriorityQueue<>((n1,n2) -> n1-n2);

比较规则就是n1-n2Comparator函数式接口的Lambda表达式写法: () -> {};, ()里面是参数列表,{}里面是方法体,方法体只有一行时{}可以省略

    2. Map.Entry

Map.EntryMap的一个内部接口。此接口为泛型,定义为Entry<K,V>。它表示Map中的一个实体(一个key-value对),它将键值对的关系封装成了一个对象。

Map提供了一些常用方法,

  1. keySet():返回值是Mapkey值的集合;
  2. entrySet():返回值也是返回一个Set集合,此集合的类型为Map.Entry
  • new int[]{a,b} int{a,b}的简化形式

        int a[] =new int {1,2,3} ; 

        int a[] = {1,2,3} ; 

  3. pq.poll()[0]01怎么确定?

五、二叉树

二叉树的遍历方式:两种方式都有前中后序三种遍历顺序

1. 递归遍历

        前:中左右

        中:左中右

        后:左右中

        (1)递归遍历 

                统一模板:(前中后序只需改变顺序即可)

//前序遍历
class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<Integer>();
        preorder(root, result);
        return result;
    }

    public void preorder(TreeNode root, List<Integer> result) {
        if (root == null) {
            return;
        }
        result.add(root.val);         //中
        preorder(root.left, result);  //左
        preorder(root.right, result); //右
    }
}

      2. 迭代遍历

        没有统一模板,中序略有不同,因为前序遍历中访问节点(遍历节点)和处理节点(将元素放进result数组中)可以同步处理,但是中序就无法做到同步!

        中序:

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        if (root == null){
            return result;
        }
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        while (cur != null || !stack.isEmpty()){
           if (cur != null){
               stack.push(cur);
               cur = cur.left;
           }else{
               cur = stack.pop();
               result.add(cur.val);
               cur = cur.right;
           }
        }
        return result;
    }
}

后序:调整一下前序遍历的代码顺序,变成中左右入栈最后在反转result集合——>使用collections接口

 Collections.reverse(result);
        return result;

3. 层序遍历

层序遍历可以使用递归方式,或者借组队列:代码参见

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值