leetcode算法知识点总结

  1. 判断重复/多次出现 情况

采用哈希表的HashSet解决。

HashSet是一个不允许有重复元素的集合,允许有Null值,不记录插入顺序。

例子:

  1. 复制数组的某一段

  1. HashMap、HashSet、LinkedHashMap

//HashMap 一般用作字典使用。插入进去的元素相对于插入顺序来说是无序的。
HashMap<Character,Boolean> dic = new HashMap<>();
dic.get('c') // 因为没有 key 是 'c',会返回 Null
dic.getOrDefault('c',false) // 因为没有 key 是 ‘c’,会返回第二个参数 false
//LinkedHashMap 一般用作字典使用。插入进去的元素相对于插入顺序来说是有序的。
Map<Character, Boolean> dic = new LinkedHashMap<>();
//HashSet 一般使用HashSet的特性:不可重复插入,插入成功返回true,插入失败返回false,来完成某些功能
HashSet<Character> set = new HashSet<>();
//HashSet可转化为list集合存储
List list = new ArrayList(set);
  1. 创建小根堆、大根堆

1.1 堆里的方法

heap.add( x ) : 添加元素进堆,自动排序

heap.poll() : 删除堆顶元素,重新排序

heap.size() : 堆有多少元素

heap.isEmpty() : 堆是否为空

heap.clear() : 清空堆

heap.remove( object o ) : 删除堆中某个元素

2. 如何创建大根堆

public static void main(String[] args) {
    //默认就是创建小根堆sheap
    PriorityQueue<Integer> sheap = new PriorityQueue<>();
    //想要创建大根堆,需要写一个比较器,这里写的比较器是 comparator
    PriorityQueue<Integer> bheap = new PriorityQueue<>(new comparator());
}
//int(×) ---> Integer(√):比较器详细内容可以看目录16
public static class comparator implements Comparator<Integer>{
    @Override
    public int compare(Integer o1, Integer o2) {
        return o2-o1;
    }
}
  1. 给一个数据流,用户一直向里面传数据, 边传边要返回当前数据中的中位数

(1)创建 一个大根堆,一个小根堆

(2)把第一个数加入大根堆

(3)接下来,如果当前元素 cur <= 大根堆堆顶元素 ,入大根堆;否则入小根堆

(4)如果 大根堆 与 小根堆 的长度相差=2,则较大长度的根堆的堆顶元素 进入 较小长度的根堆

(5)任何时刻的中位数求法

当前时刻 大根堆长度 == 小根堆长度 :中位数 = (大根堆堆顶元素 + 小根堆堆顶元素)/ 2 ;

当前时刻 大根堆长度 != 小根堆长度 :中位数 = 较大长度的根堆的堆顶元素

  1. List集合的传递

List<Integer> list = new ArrayList<>();
list.add(0); list.add(1);
//1. list2 指向 list , 值与list相同 ,改变 list2 的值,list的值也会改变
List<Integer> list2 = list;
//2. list3 不指向 list , 值与list相同 , 改变 list3 的值,list的值不会改变
List<Integer> list3 = new ArrayList<>(list);
/**
 * List<Integer> duc
 * List<List<Integer>> res
 * duc.add(1);duc.add(2);duc.add(3);
 * res.add(duc) : 这里存的是指向duc集合的指针,一旦duc集合内的元素改变,res也会跟着改变
 * res此时的值为[ [1,2,3] ]
 * duc.remove(duc.size()-1)
 * res此时的值为[ [1,2] ]
 */
List<Integer> duc = new ArrayList<>();
duc.add(1);
duc.add(2);
duc.add(3);
duc.add(4);
List<List<Integer>> res = new ArrayList<>();
res.add(duc);
for (List<Integer> re : res) {
    System.out.println("re = " + re);
}
duc.remove(duc.size()-1);
for (List<Integer> re : res) {
    System.out.println("re = " + re);
}
  1. List集合转数组

ArrayList<int[]> res = new ArrayList<>();
int[] arr1 = new int[]{1,2,3,4};
int[] arr2 = new int[]{4,5,6};
int[] arr3 = new int[]{3};
res.add(arr1);res.add(arr2);res.add(arr3);
int[][] ans = res.toArray(new int[res.size()][]);
//输出ans
for(int i = 0 ; i < ans.length; ++i){
    for(int j = 0; j < ans[i].length ; ++j){
        System.out.print(ans[i][j] + " ");
    }
    System.out.println("");
}
  1. 查找Map<key,value>中是否含有某个value值

// 使用 map.containsValue()方法
List<Map<Integer,Integer>> lm = new ArrayList<>();
Map<Integer,Integer> map = new HashMap<>();
map.put(1,1);
Map<Integer,Integer> map2 = new HashMap<>();
map.put(1,2);
lm.add(map);
lm.add(map2);

for(Map<Integer,Integer> m : lm){
    if(m.containsValue(null)){
        System.out.println(false);
    }
}
  1. n中不同字符的全排列共有多少种可能

f(n) = n * f(n-1), 就是 n!

例:f(1) = 1、f(2) = 2 * f(1) = 2

  1. 遍历HashMap + 删除List集合元素

// 1.遍历map
HashMap<Integer,Integer> map = new HashMap<>();  
   
for(Map.Entry<Integer,Integer> entry : map.entrySet()){
    System.out.println(entry.getKey());
    System.out.println(entry.getValue());
}

// 2. 删除List集合元素
ArrayList<Integer> list = new ArrayList<>();
int target = 10;

Iterator<Integer> iterator = list.iterator();
while(iterator.hasNext()){
    System.out.println(iterator.next());
    int i = iterator.next();
    if( i == target){
        iterator.remove();
        break;
    }
}
  1. 查找字符串A是否包含另一个字符串B

//indexOf返回匹配到的第一个元素的索引值,匹配失败返回-1
String A = "abcde";
String B = "de";
String C = "cf";
int res1 = A.indexOf(B);
int res1 = B.indexOf(C);
  1. 数组排序:Arrays.sort(数组名,fromIndex,toIndex)

    int[] nums1 = new int[]{2,4,1,5,1,2,5,66};
    int[] nums2 = new int[]{2,4,1,5,1,2,5,66};
    Integer[] nums3=new Integer[]{2,4,1,5,1,2,5,66};
//对 nums 数组 所有元素进行排序(从小到大)
    Arrays.sort(nums1);
//对 nums 数组 [0,3) 范围进行排列
    Arrays.sort(nums2,0,3);
//对 nums 数组 所有元素进行排序(从大到小),此种方法只能用 Integer,基本类型数组不可以用
    Arrays.sort(nums3, Collections.reverseOrder());
  1. 有序表TreeMap的使用方法

//以下所有操作,时间复杂度都是O(logN),非常快!!!
TreeMap<Integer,String> orderMap = new TreeMap<>();
    orderMap.put(5,"我是5");
    orderMap.put(10,"我是10");
    orderMap.put(15,"我是15");
    orderMap.put(-5,"我是-5");
    orderMap.put(-15,"我是-15");
    orderMap.put(50,"我是50");
//    1. 求 有序表 中,key的最小值
    Integer firstKey = orderMap.firstKey();
//    2. 求 有序表 中,key的最大值
    Integer lastKey = orderMap.lastKey();
//    3. 求 有序表 中,<= x 并且尽量大的key
//    例如 <= 7 的key有:-15,-5,5 ,尽量大的,则 输出 5
    Integer floorKey1 = orderMap.floorKey(7);
//    例如 <= -17 的key没有,则输出null
    Integer floorKey2 = orderMap.floorKey(-17);
//    4. 求 有序表 中,>= x 并且尽量小的key
//    例如 >= 7 的key有:10,15,50 ,尽量小的,则 输出 10
    Integer ceilingKey = orderMap.ceilingKey(7);
//    5. 求 有序表 中,是否包含某个key值:包含返回true,不包含返回false
    boolean containsKey = orderMap.containsKey(7);
  1. 差分法

  1. 一维差分法

给定数组Target[ 0 , .... , N-1 ] 范围

1.1 适用场景

现在需要整体完成以下所有操作:[ a , b ] 范围元素整体 + 3,[ a1 , b1] 范围整体 -1 ,... ... , [an , bn] 范围元素整体 + 9 ,求完成所有操作后,Target数组任意位置的值。

1.2 解题方法

题目描述:要求[ 1 , 3 ]范围整体-3,[ 2,7 ] 范围整体 + 9 , [ 0 , N-1 ] 范围整体 + x ,返回Target修改后的所有元素

解题:

  1. 定义差分数组diff [ 0 , N+1 ] , 长度为 Target 数组长度 + 1,所有元素初始值=0

  1. [ 1 , 3 ] 整体 -3 ----> 差分数组 1 位置 -3 , 4位置 +3

  1. [ 2 , 7 ] 整体 +9 ----> 差分数组 2 位置 +9 , 8位置 -9

  1. [ 0 , N ] 整体 +21 ----> 差分数组 0 位置 +x , N+1位置 -x

  1. 求 差分数组[ 0 , N ]范围所有位置的前缀和sum[i],Target[ i ] = sum[i] + Target[ i ]

  1. 二维差分法

给定一个矩阵Target , M x N ,

2.1 适用场景

完成以下所有操作:[ a , b ] <--> [ c , d ] 范围元素整体 + 3,[ a1 , b1] <--> [ c1 , d1 ] 范围整体 -1 ,... ... , [an , bn] <--> [ cn , dn ] 范围元素整体 + 9 ,求完成所有操作后,Target矩阵任意位置的值。

注:[ a , b ] <--> [ c , d ] 两点确定唯一 一片区域,起始点为 [ a , b] , 终止点为 [ c , d ]

2.2 解题方法

题目描述:要求[ a , b ] <--> [ c , d] 范围整体+2,[ e , f ] <--> [ M-1 , N-1 ] 范围整体 -3 返回Target修改后的所有元素

解题:

  1. 定义差分矩阵 diff M+1 x N+1 , 所有元素都为0

  1. [ a , b ] <--> [ c , d ] 范围元素整体 + 2 ----> 差分矩阵 [ a , b ] 位置元素 +2 差分矩阵 [ a , d+1 ]、[ c+1 , b ] 位置元素-2 差分矩阵 [ c+1 , d+1 ] 位置元素+2

  1. [ e , f ] <--> [ M-1 , N-1 ] 范围整体 -3 ----> 差分矩阵 [ e , f ] 位置元素 -3 差分矩阵 [ e , N-1 +1] 、[ M-1 +1 , f] 位置元素 +3 差分矩阵 [ M-1 +1 , N-1 +1] 位置元素 -3

  1. 求出差分矩阵diff第0行和第0列的前缀和 sum[0][j] 和 sum[i][0]

  1. 其他位置的前缀和 sum[i][j] = sum[i][j-1] + sum[i-1][j] - sum[i-1][j-1] + diff[i][j] , 即 加左,加上,减左上,加自己

  1. 先根据1)、2)的要求,得到差分矩阵

  1. 求差分矩阵的前缀和sum矩阵:先求第0行和第0列的前缀和,其他位置的值直接套公式

  1. target = target + sum

  1. 位运算

  1. 异或(^)

无进位相加: 1+1 = 0,1+0 = 1,0+0=0

1.1 运算法则

  1. 归零律:a^a = 0

  1. 恒等律:a^0 = a

  1. 交换律:a^b = b^a

  1. 结合律:a^b^c = a^(b^c) = (a^b)^c

1.2 案例

题目:要求不适用额外空间,交换变量a,b的值。

解答:1)a = a ^ b; 2)b = a ^ b; 3)a = a ^ b;

分析:例如 a = 甲,b=乙

1)之后,a = 甲^乙,b = 乙

2)之后,a = 甲^乙,b = 甲^乙^乙 = 甲^0 = 甲

3)之后,a = 甲^乙^甲 = 乙^0 = 乙,b = 甲

即成功交换了变量a,b的值。

题目:已知一数组arr[ 0 , ... , N-1]

1)其中有1个元素出现的次数是奇数,其他元素出现次数全为偶数,求这1个奇数是什么。

2)其中有2个元素出现的次数是奇数,其他元素出现次数全为偶数,求这2个奇数是什么。

要求:空间复杂度O(n),空间复杂度O(1)

解答

1)第一问

//从头 异或 到尾:0^0 = 0 , 0^1 = 1 , 1^1 = 0
int[] arr = new int[]{1,1,2,2,3,3,3,4,4,2,2};
int ans = 0;
for(int i = 0; i < arr.length ; ++i){
    ans = ans ^ arr[i];
}
System.out.println("ans = " + ans);

2)第二问

int[] arr = new int[]{1,1,2,3,3,3,4,4,2,2};
int ans1 = 0;
int ans2 = 0;
//进行完第一个for循环: ans1 = a ^ b
for(int i = 0; i < arr.length ; ++i){
    ans1 = ans1 ^ arr[i];
}
//因为 a != b,一定有 ans1 != 0 , 
//则,ans1 二进制数等于1的i位置上,一定是 a二进制i位置 与 b二进制i位置不同
//则,一定有,在i位置上,a,b一个是1,一个是0
//取出最右侧的1 ---> rightOne
//这个1,一定把arr数组分成了两部分,左边部分元素是i位置=1,右边部分元素是i位置=0
//一定有,a,b一个在左边部分,一个在右边部分
//一定有,左边部分只有一个a或b奇数次出现,其他元素都是偶数次出现
//一定有,右边部分只有一个a或b奇数次出现,其他元素都是偶数次出现
//则,就变成了第一问的解题方法:0 ^ 左边部分得到 a 或 b,假设这里得到 ans2 = a
//则,因为有 ans1 = a ^ b,所以 ans1 ^ ans2 = a ^ b ^ a = b
//这样就得到了两个奇数次出现的元素 
int rightOne = ans1 & (~ans1 + 1); 
for(int i = 0 ; i < arr.length ; ++i){
    if((arr[i] & rightOne) == 0){
        ans2 = ans2 ^ arr[i];
    }
}
ans1 = ans1^ans2;
System.out.println("ans1 = " + ans1);
System.out.println("ans2 = " + ans2);

2. 或(|)

1.1 运算法则

  1. 0 | 0 = 0

  1. 0 | 1 = 1

  1. 1 | 0 = 1

  1. 1 | 1 = 1

1.2 案例

题目:把变量a先乘2再加1

解答:a = 3 , a<<1|1 = 7

分析:a<<1 表示 a*2 , (0000 0011)<<1 ==> (0000 0110) 6

并且因为左移1位,二进制最右侧元素=0,

所以此时 | 1 ,就会把最右侧的0变成1,(0000 0110) | (0000 0001) ==> (0000 0111) 7

就达成了加1的效果

3. 与(&)

1.1 运算法则

  1. 0&0 = 0

  1. 0&1 = 0

  1. 1&0 = 0

  1. 1&1 = 1

1.2 案例

题目:把变量a清零

解答:a & 0 = 0

分析:0&1 = 0,0&0=0

题目:获得变量a的二进制第i位元素

解答:int ans = a & 2^(i-1),ans就是第i位置元素

分析:例如:a = 91( 0101 1011 ),获得 a 的第 4 位元素

则,2^(i-1) = 8 ( 0000 1000 )

0101 1011 &

0000 1000

= 0000 1000

结果 0000 1000 != 0 所以,第4位上的元素就是 1

题目:获得变量a的二进制最右侧的1

解答:int ans = a & ( ~a + 1)

分析:例如:a = 91( 0101 1011 ),获得 a 的最右侧1

则,~a = 1010 0100

则,~a+1 = 1010 0101

则 a & ( ~a + 1 )

= 0101 1011 &

1010 0101

= 0000 0001

4. 右移(>>)

1.1 案例

题目:把 变量a 除于2

解答:a>>1

解析:例如:a = 7 (0000 0000 ... 0000 0111)

a >> 1 ==> 在左侧补,正数补0,负数补1 = 0000 0000 ... 0000 0011

题目:对于[L,R]范围,求中间位置索引

解答:mid = (L+R)/2 (×)

当L+R越界的时候,得到的mid就是错误的

此时可以修改为

mid = L + (R-L)/2 (√)

还可以更快得到结果

mid = L + (R-L)>>1 (√)

题目:求a的二进制第i位的值是0还是1,i 从 0 开始计数

解答:ans = ( a>>i ) & 1 或者 ans = a & ( 1 << i )

解析:1)

a>>i 就把第i位移动到最右侧

1 ==> 0000 ... 0000 0001

2)

1 << i 就是把 0位置的 1 挪到的 i 位置

上面 1)2)方法,都是用了 0&1 = 0 , 1&1 = 1

所以可以得到第i位置的值

5. 左移(<<)

1.1 案例

题目:求 2^a 的值

解答:ans = 1 << a

分析:1(0000 0001) 左移 a 位置,就相当与二进制最后的1,向左移动a位置,即可得到最终结果

题目:求2*a 的值

解答:ans = a << 1

分析:11(0000 1011) << 1 ==> 22(0011 0110), 本来是 8 , 2 ,1 左移 1位,变成了 16 , 4 , 2

  1. 比较器

  1. 规则

1)返回负数,第一个参数排在前面

2)返回正数,第二个参数排在前面

3)返回0,谁在前面无所谓

:有规则 1)、2)、3)可知,

排列,就是 第一个参数 - 第二个参数

排列,就是 第二个参数 - 第一个参数

2. 用法

public static void main(String[] args){
    //1.二维数组排序
    //存在记录n个人的身高person[i][0]、体重person[i][1]的数组,
    int[][] person = new int[][]{{1,2},{3,1},{1,5},{4,6},{5,5}}
    //要求,对person进行排序,先按照身高升序排列,如果身高相同,则按照体重降序排列
    Arrays.sort(person,new personComparator());
    //存在Student实体类,有:姓名、id、年龄
    Student stu1 = new Studeng("gys",1,21);
    Student stu2 = new Studeng("wbj",2,18);
    Student stu3 = new Studeng("sq",3,30);
    Student[] arrStu = new Student[]{stu1,stu2,stu3};
    //要求:按照年龄从小到大升序排列
    Arrays.sort(arrStu,new studentComparator());

    //2.一维数组排序
    int[] nums = new int[]{4,5,8,2};
    //要求:对nums进行从大到小排序
    Arrays.sort(nums,new comparator3());
}
//比较器:对person进行排序,先按照身高升序排列,如果身高相同,则按照体重降序排列
public static class personComparator implements Comparator<int[]>{
    @Override
    public int compare(int[] o1, int[] o2) {
        if(o1[0] == o2[0]){
            return o2[1] - o1[1];
        }
        //返回负数,第一个参数o1排在前面
        //返回正数,第二个参数o2排在前面
        //返回0,谁在前面无所谓
        return o1[0]-o2[0];
    }
}
//比较器:按照年龄从小到大升序排列
public static class studentComparator implements Comparator<Student>{
    @Override
    public int compare(Student o1, Student o2) {
        //返回负数,第一个参数o1排在前面
        //返回正数,第二个参数o2排在前面
        //返回0,谁在前面无所谓
        return o1.age-o2.age;
    }
}
//比较器:对nums进行从大到小排序
public static class comparator3 implements Comparator<Integer>{
    @Override
    public int compare(Integer o1, Integer o2) {
        return o2-o1;
    }
}
  1. Arrays.binarySearch方法

  1. 用法

Arrays.binarySearch(int[] arr, startIndex, endIndex, value)

在数组arr,范围 [ startIndex , endIndex ) 左闭右开中,查找值为value的元素

1)如果找到了,返回 value 在数组中的位置

2)如果找不到,返回 value 在数组中应该插入位置的负数

注意:要保证arr是有序的、不重复的(如果有重复的,插入位置会给的有问题)

int[] num=new int[]{5,8,3,10,1};
//对数组进行排序
Arrays.sort(num);
//排序后的数组为[1,3,5,8,10]
//查询数组num数值为8的下标,有结果,返回下标
int num1=Arrays.binarySearch(num,8);
//查询数组num数值为6的下标,无结果,返回-1或“-”(插入点)。插入点是索引键将要插入数组的那一点,即第一个大于该键的元素的索引。
int num2=Arrays.binarySearch(num,6);
int num3=Arrays.binarySearch(num,-6);
//查询数值num中下标为2到3中数值为8的下标
int num4=Arrays.binarySearch(num,2,3,8);
//查询数值num中下标为1到3中数值为3的下标
int num5=Arrays.binarySearch(num,1,3,3);
System.out.println("查询数组num数值为8的下标,有结果"+num1);
System.out.println("查询数组num数值为6的下标,无结果"+num2);
System.out.println("查询数组num数值为-6的下标,无结果"+num3);
System.out.println("查询数值num中下标为2到3中,数值为8的下标"+num4);
System.out.println("查询数值num中下标为2到3中,数值为3的下标"+num5);

2. 分析

  1. 首先,先对num数组进行了排序,排序后的数组为 [1,3,5,8,10]

  1. 在整个num数组中,查询数值为 8 的下标,能找到,返回下标 3

  1. 在整个num数组中,查询数值为 6 的下标,找不到,返回 -4 , 则插入位置为 -( -4 + 1)= 3

  1. 在整个num数组中,查询数值为 -6 的下标,找不到,返回 -1 , 则插入位置为 -( -1 + 1)= 0

  1. 在 [ 2, 3 )中,查询数值为8的下标,右开区间,找不到,返回 -4 ,则插入位置为 -( -4 + 1)= 3

  1. 在 [ 1 , 3 )中,查询数值为3的下标,左开区间,能找到,返回1

18. StringBuilder

StringBuilder.reverse().toString()

// 1. 指定位置插入元素:
// StringBuilder.insert(int offset , String/char/int/flot/long/boolean等 str)
// 下面使用StringBuilder.insert 实现 "a good example" --> "example good a"
String s = "a good example";
String[] str = s.split(" ");
StringBuilder builder = new StringBuilder();
for(String s1 : str) builder.insert(0,s1);
s = builder.toString();
}

// 2. 在尾部添加元素
// StringBuilder.append(String/char/int/flot/double/long/boolean等 str);
char c = 'a';
StringBuilder builder1 = new StringBuilder();
builder1.append(c);

//3. 反转String  abcdefg --> gfedcba
String s2 = "abcdefg";
char[] c = s2.toCharArray();
for (char c1 : c) {
    builder.append(c1);
}
s2 = builder.reverse().toString();

19. String

  1. intern()

当一个字符串调用Intern()方法时,如果String Pool中已经存在一个字符串与该字符串值相等(内部是使用equals()方法确认的),那么就会返回String Pool中字符串的引用,否则,就会在String Pool中添加一个新的字符串,并返回这个新字符串的引用。

String s1 = "a123";
String s2 = "a123";
System.out.println((s1 == s2));  //true
String s3 = new String("b");
String s4 = new String("b");
System.out.println((s3 == s4));  //false
String s5 = s3.intern();
String s6 = s4.intern();
System.out.println((s5 == s6));  //true

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值