Leetcode

-------------------------- 模板 --------------------------

数组里找超过1/2,1/3个数的数字

通解通法:摩尔投票法

一个数组,超过1/2的数字,至多有1个;超过1/3的数字,至多有2个...

超过1/2: 169. 多数元素

超过1/3:229. 求众数 II

疑问:最后留下的一定是超过1/2,或者1/3么?

https://leetcode-cn.com/problems/majority-element-ii/solution/cong-lun-wen-jiao-du-jiang-jie-mo-er-tou-piao-fa-b/

想象着这样一个画面:会议大厅站满了投票代表,每个都有一个牌子上面写着自己所选的候选人的名字。然后选举意见不合的(所选的候选人不同)两个人,会打一架,并且会同时击倒对方。显而易见,如果一个人拥有的选票比其它所有人加起来的选票还要多的话,这个候选人将会赢得这场“战争”,当混乱结束,最后剩下的那个代表(可能会有多个)将会来自多数人所站的阵营。但是如果所有参加候选人的选票都不是大多数(选票都未超过一半),那么最后站在那的代表(一个人)并不能代表所有的选票的大多数。因此,当某人站到最后时,需要统计他所选的候选人的选票是否超过一半(包括倒下的),来判断选票结果是否有效

Q:在leetocde里,为什么n/2众数不需要第二次遍历,n/3众数就需要第二次遍历呢?

A:因为leetcode在1/2题目描述中,确保了肯定存在这个结果。如果没有这个确保, 也需要二次循环。而1/3题目描述中,并没有保证一定有一个/两个,所以要二次循环在统计一次投票数。

动态规划

递归+额外空间保存中间状态(减少重复计算)

模板套路,【必看】:https://www.cnblogs.com/frankcui/p/11674594.html 

位运算

海量数据

  • 不能把所有的数据加载进内存中
  • 需要维护一个固定大小的容器,每进来一个元素进行比较和保留

 例题:

面试题40. 最小的k个数

题目描述的暗示

暗示你用HashMap

“假设数组中没有重复的数字”/ “保证只有一个答案”

暗示你用int[26],int[52],int[256]

当题干提示“只包含大写or小写字母”,那么使用int[26];

如果大小写均有,使用int[52];

如果是任意字符,则是int[256] ; 例如:https://leetcode-cn.com/explore/featured/card/bytedance/242/string/1012/

暗示你用数组解题

“在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。”

正序会覆盖,试试倒序

有时题目会要求在O(k) (或者比正常思路小)的空间复杂度里操作,可能需要重复利用空间。

如果从0往后使用,极有可能会overlap。这时可以倒序处理。

119. Pascal's Triangle II

class Solution {
    public List<Integer> getRow(int rowIndex) {
    	List<Integer> result = new ArrayList<>();
    	int[] array = new int[rowIndex+1];

    	if(rowIndex < 0){
    		return result;
    	}

    	for(int i=0; i<=rowIndex; i++){
    		for(int j = i; j >= 0; j--){
    			if(j == 0 || j == i){
    				array[j] = 1;
    				continue;
    			}
    			array[j] = array[j] + array[j-1];

    		}
    	}
    	for(int q = 0; q < array.length; q++){
    		result.add(array[q]);
    	}
    	return result;

    }
}

  

二叉树

当递归函数需要记录两种信息

例题:判断一个树是否为平衡二叉树 https://leetcode-cn.com/problems/ping-heng-er-cha-shu-lcof/

又要记录树的高度int,又要记录树是否是平衡的boolean,可以用-1来代替boolean(false),>0的int代替boolean(true)

二叉搜索树

模板

把一个二叉搜索树“中序遍历”,会是一个递增的序列。例如下图,中序遍历后是1,5,6,7,8

注意

不能单纯的只递归左节点<中间节点 && 右节点>中间节点,不然下面的case无法通过。(4虽然小于7, 但是因为4在6的右边,也应该大于6才行)

Sliding Window 滑动窗口

模板

整体思路:左右两个指针。右指针在for循环里面控制,左指针在for内部的while循环控制。当窗口内部的某条件不符合题目要求时,会在while循环里移动左指针。

public int slidingWindowTemplate(String[] a, ...) {
    // 输入参数有效性判断
    if (...) {
        ...
    }

    // 申请一个散列,用于记录窗口中具体元素的个数情况
    // 这里用数组的形式呈现,也可以考虑其他数据结构
    int[] hash = new int[...];

    // 预处理(可省), 一般情况是改变 hash
    ...

    // l 表示左指针
    // count 记录当前的条件,具体根据题目要求来定义
    // result 用来存放结果
    int l = 0, count = ..., result = ...;

    //*important* for循环只控制右指针,左指针通过内部的while语句控制
    for (int r = 0; r < A.length; ++r) {
        // 更新新元素在散列中的数量
        hash[A[r]]--;

        // 根据窗口的变更结果来改变条件值
        if (hash[A[r]] == ...) {
            count++;
        }

        // 如果当前条件不满足,移动左指针直至条件满足为止
        while (count > K || ...) {
            ...
            if (...) {
                count--;
            }
            hash[A[l]]++;
            l++;
        }

        // 更新结果
        results = ...
    }

    return results;
}

  

注意事项

1- 有时可以存储item本身,也可以存储index下标

2- 使用HashSet或者HashMap,让查询的时间复杂度降到O(1)

3-当题干提示只包含大写or小写字母,那么使用int[26]; 如果大小写均有,使用int[52];如果是任意字符,则是int[128]

4-当for循环的index变量, 在for循环外依旧要使用。注意该index已经超过了for循环设置的limit,例如:

当跑出for循环外,i的值变成了11,而不是10。如果想使用for的最后一个index值(10),需要将i (11)减去1后再使用。

int i = 0;
for(; i <= 10; i++){
     //do something...
}
System.out.println("i value = " + i); //i = 11,not 10  

5-for循环的执行顺序

第一次进入for循环:1--> 2

其余次进入for循环:3 --> 2

因此,该循环最后一次i会变成11,从而造成>10,跳出循环。

-------------------------- 易错点 --------------------------

快速排序

相等的情况怎么处理,特别容易出错

1-在普通情况下,在while循环当中,两个while循环都要包含==情况

 易错点

    • benchmark采用left坐标
    • benchmark和left, right相等的情况,继续move
    • 如果需要自定义大小关系,不要用boolean函数,而是返回1,0,-1来代表大于,等于,小于。这样可以完美融合原来的逻辑。

2-在需要自定义“大小排序”函数时,不要声明boolean返回值的判断函数,一定要是int返回型得函数 (0代表相等,>0代表n1>n2, <0代表n1<n2)。方便单独处理等于情况。

大数

1-当题目要求输出max/min时,中途会有max/min操作。此时切记不要中途做mod操作,因为原本A>B,但是如果因为A超过了规定的模值,提前mod的话,会导致A<B。正确的做法应该先全都保存原始数据在BigInteger中,最最后返回时再mod。例子:面试题14- II. 剪绳子 II

    public int cuttingRope(int n) {
        BigInteger[] cache = new BigInteger[n+1];
        cache[0]= new BigInteger("1");
        cache[1]= new BigInteger("1");
        BigInteger e= new BigInteger("1000000007");
        for(int i =2; i <= n; i++){
            BigInteger temp_max = new BigInteger("0");
            BigInteger max = new BigInteger("0");

            for(int j = 1; j <= i-1; j++){
                //如果在这里提前mod...会在下一步max()出错
                BigInteger t1 = (BigInteger.valueOf(j).multiply(cache[i-j]));
                BigInteger t2 = (BigInteger.valueOf(j).multiply(BigInteger.valueOf(i-j)));

                //这里牵扯到max比较,因为必须全都保存原始数据
                temp_max = t1.max(t2);
                max = max.max(temp_max);
            }
            //这里也不能mod,要全都保存原始数据
            cache[i] = max;
        }

        //最最后返回时,再取mod
        return cache[n].mod(e).intValue();
    }

  

-------------------------- 常用类 --------------------------

Arrays

import java.util.Arrays;

复制数组 Arrays.copyOfRange(int[] nums, int start, int end)

返回一个新的复制的数组, 范围依然是左闭右开 [start, end)

快排排序 Arrays.sort(int[]/long[]/short[]/char[]/byte[]/float[]/double[]) 

int[] test = new int[]{0,3,4,2,1};
Arrays.sort(test); // test change to {0,1,2,3,4}

快速排序 - 指定坐标区间 Arrays.sort(int[] a, int fromIndex, int toIndex) 

[fromIndex, toIndex)

快速排序 - 自定义比较器

Comparator<T>中的T,代表着temp里每个item的类型

//Comparator<T>中的T,代表着temp里每个item的类型        
        int[][] temp = new int[][]{{1,2},{1,2}};
        Arrays.sort(temp, new Comparator<int[]>(){
            @Override
            public int compare(int[] a, int[]b){
                return a[0] - b[0];
            }
        });

例子:

class Solution {
     //看这里!!!
    private class LargerNumberComparator implements Comparator<String> {
        @Override
        public int compare(String a, String b) {
            String order1 = a + b;
            String order2 = b + a;
           return order2.compareTo(order1);
        }
    }

    public String largestNumber(int[] nums) {
        // Get input integers as strings.
        String[] asStrs = new String[nums.length];
        for (int i = 0; i < nums.length; i++) {
            asStrs[i] = String.valueOf(nums[i]);
        }

        //看这里!!!
        // Sort strings according to custom comparator.
        Arrays.sort(asStrs, new LargerNumberComparator());

        // If, after being sorted, the largest number is `0`, the entire number
        // is zero.
        if (asStrs[0].equals("0")) {
            return "0";
        }

        // Build largest number from sorted array.
        String largestNumberStr = new String();
        for (String numAsStr : asStrs) {
            largestNumberStr += numAsStr;
        }

        return largestNumberStr;
    }
}
    

  

类型转换

List<String> -- String[]

/*
* String[] --> List<String>
*/

String[] strs = new String[] {"aaa", "bbb", "ccc"};
List<String> list = Arrays.asList(strs);
//二步合为一步
//List<String> list = Arrays.asList("aaa", "bbb", "ccc");

/*
* List<String> --> String[]
*/
//传入初始化长度的数组对象,返回该对象数组
String[] array = list.toArray(new String[list.size()]);
//没有参数,就会返回Object[]
//Object[] array = list.toArray(); 

https://blog.csdn.net/meism5/article/details/89874236

List<int[]> <> int[][]

List<int[]> list = new ArrayList<>();

int[][] result = list.toArray(new int[list.size][]); 

注意:此方法不适合一维数组, 因为如果一维数组调用“泛型”那个api,返回的应该是Integer[],而不是int[].

// from java.util.List interface

Object[] toArray();        // 非泛型
<T> T[] toArray(T[] a); //泛型

  

  

int > char

因为是高精度>低精度的去转换,可能出现丢失精度的情况,需要强制转换。

  • char c = 'a' + 10;
  • char c = (char) ('a' + 10); //correct

char, char[], int, long, float, double, boolean > String

 String.valueOf();

String <> char[]

char[] = String.toCharArray();

String = new String(char[]);

String = String.valueOf(char[]);

int <> BigInteger

int = BigInteger.intValue();

BigInteger = BigInteger.valueOf();

>>>THIS IS WRONG: BigInteger = new BigInteger(int);

能直接用new方法构造BigInteger的, 有 new BigInteger(String / long),  new BigInteger(int[] / byte[])

int[][]

初始化

只定义有多少行即可,每一行的具体细节可以之后再赋值

//只初始化有多少行即可       
int size = 5; int[][] result_arr = new int[size][]; //再单独处理每一行的细节 for(int i = 0; i < size; i++){ result_arr[i] = listToArray(result.get(i)); //具体逻辑 }

  

Character

判断是否是数字/字母

boolean Character.isLetter(int/char); //字母

boolean Character.isDigit(int/char); //数字

boolean Character.isLetterOrDigit(int/char); //字母或数字

String

直接比较数字大小 - String.compareTo()

可以直接调用String.compareTo()函数

int result = "123".compareTo("987") // result < 0
int result = "987".compareTo("123") // result > 0
int result = "123".compareTo("123") // result = 0

  

截取substring

注意函数名字的大小写,

  • 是String.substring(int i, int j) // [i,j)
  • 不是String.subString()

split()

以下的“$”代表一个空格“ ”

"hello$world".split(" ") --> {“hello”, "world"}

"hello$$world".split(" ") --> {“hello”, "", "world"}  // Not {“hello”, "$", "world"} 

"hello$$$world".split(" ") --> {“hello”, "", "", "world"} // Not {“hello”, "$", "$", "world"} 

trim()

以下的“$”代表一个空格“ ”

"$$$hello$$$$world$$$$$$".trim() --> "hello$$$$world"

LinkedList<E>

通用!!! 可以用来替代ArrayList, Stack, Queue

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable

  

代替ArrayList

LinkedList<E>.get(int index); // inherit AbstractList<E>

LinkedList<E>.set(int index, E); // inherit AbstractList<E>

LinkedList<E>.add(E); // inherit AbstractList<E>

代替Stack

LinkedList本质上是一个Queue,只不过因其实现了Deque,所以可以双向操作。

Stack的push() : LinkedList<E>.add(E); // inherit AbstractList<E>, "function" equals addLast() -- inherit from Deque<E>

Stack的pop() : LinkedList<E>.pollLast(); // inherit from Deque<E>

Stack的peek() : LinkedList<E>.peekLast(); // inherit from Deque<E>

代替Queue

Queue的add() : LinkedList<E>.add(E); // inherit AbstractList<E>, "function" equals addLast() -- inherit from Deque<E>

Queue的poll() : LinkedList<E>.poll(); // inherit from Deque<E>

Queue的peek() : LinkedList<E>.peekLast(); // inherit from Deque<E>

深度拷贝 Deep Clone

list2 is the deep clone of list1.

 LinkedList<E> list1 = new  LinkedList<E>();

 LinkedList<E> list2 = new  LinkedList<E> (list1);

    /**
     * Constructs a list containing the elements of the specified
     * collection, in the order they are returned by the collection's
     * iterator.
     *
     * @param  c the collection whose elements are to be placed into this list
     * @throws NullPointerException if the specified collection is null
     */
    public LinkedList(Collection<? extends E> c) {
        this();
        addAll(c);
    }

  

下标存储/获取

T var = LinkedList<T>.get(int index) 

int index = LinkedList<T>.indexOf(Object obj);

ArrayList<E>

复制新ArrayList容器

所有继承自Collections接口的容器(例如LinkedList, 详见 https://www.cnblogs.com/frankcui/p/13599021.html),都有该方法。很方便快速复制一个新容器。

//应用
ArrayList<Integer> newArrayList = new ArrayList<>(oldArrayList);

//API
/**
     * Constructs a list containing the elements of the specified
     * collection, in the order they are returned by the collection's
     * iterator.
     *
     * @param c the collection whose elements are to be placed into this list
     * @throws NullPointerException if the specified collection is null
     */
    public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

ArrayList<Integer>.remove(int) 删除的是(Integer)int元素,还是int坐标下的Integer?

答案:

ArrayList.remove(int)删除的是int坐标下的integer,但是如果调用ArrayList.add(int) -- 本身没有这个API,但会被自动装箱为ArrayList.add(Integer),是在尾部加入这个Integer

ArrayList.remove(Object)才是直接删除Integer

不能用[]下标访问

ArrayList<Integer> list = new ArrayList<>();

>>>THIS IS WRONG: Integer i = list[0];

>>>THIS IS RIGHT: Integer i = list.get(0);  

初始化后,不能直接调用set(int index, E element)

ArrayList<Integer> list = new ArrayList<>();

list.set(int index, E element); // Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 0, Size: 0

因为ArrayList初始化后的size是0,紧接着做任何的set都会报错。必须先使用add()操作扩容size才可。详细请查阅ArrayList<E>的size扩容文章。

HashMap<K,V>

HashMap常用方法

//get all map's key list
map.keySet();   

//get all map's value list
map.values();    

//get all map.Entry list
Set<Map.Entry<Integer, Integer>> entries = map.entrySet(); 
for(Map.Entry<Integer, Integer> entry : entries){
       System.out.println("key: " + entry.getKey() + " value: " + entry.getValue());
}

HashMap遍历

        Iterator<Entry<K,V>> i = entrySet().iterator();
	while (i.hasNext()) {
                Entry<K,V> e = i.next();
                if (e.getKey()==null)
                    return true;
		if (e.getValue()==null)
                    return true;
    }    

  

方法二(推荐):

HashMap<Integer, Integer> map = new HashMap<>();

for(Map.Entry<Integer, Integer> entry : map.entrySet()){
System.out.println("key: " + entry.getKey() + " value: " + entry.getValue());
}

HashMap没有contains()方法, 只有containsKey()和containsValue()方法

public boolean containsKey(Object key) {
        Iterator<Map.Entry<K,V>> i = entrySet().iterator();
        if (key==null) {
            while (i.hasNext()) {
                Entry<K,V> e = i.next();
                if (e.getKey()==null)
                    return true;
            }
        } else {
            while (i.hasNext()) {
                Entry<K,V> e = i.next();
                if (key.equals(e.getKey()))
                    return true;
            }
        }
        return false;
    }


public boolean containsValue(Object value) {
        Iterator<Entry<K,V>> i = entrySet().iterator();
        if (value==null) {
            while (i.hasNext()) {
                Entry<K,V> e = i.next();
                if (e.getValue()==null)
                    return true;
            }
        } else {
            while (i.hasNext()) {
                Entry<K,V> e = i.next();
                if (value.equals(e.getValue()))
                    return true;
            }
        }
        return false;
    }

  

HashSet<E>

HashSet的遍历

        HashSet<String> set = new HashSet<>();
        Iterator<String> iter = set.iterator();
        while(iter.hasNext()){
            System.out.println(iter.next());
        }    

  

HashSet的add()

如果所加的元素,之前不存在,会返回true。如果已经存在,则返回false。

PriorityQueue 优先队列

https://www.cnblogs.com/yongh/p/9945539.html

最大堆 & 最小堆 初始化

PriorityQueue(优先队列),一个基于优先级堆的无界优先级队列。实际上是一个堆(不指定Comparator时默认为最小堆),通过传入自定义的Comparator函数可以实现大顶堆。

PriorityQueue<Integer> minHeap = new PriorityQueue<Integer>(); //小顶堆,默认容量为11
PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(11,new Comparator<Integer>(){ //大顶堆,容量11
    @Override
    public int compare(Integer i1,Integer i2){
        return i2-i1;//大顶堆
     //return i1-i2;//小顶堆
} });

  

常用方法

PriorityQueue的常用方法有:poll(),offer(Object),size(),peek()等。

  • 插入方法 offer()、add() 时间复杂度为O(log(n)) --> n 是堆的大小;
  • 删除方法 remove(Object) 时间复杂度为O(n)
  • 包含方法 contains(Object) 时间复杂度为O(n);
  • 检索方法(peek、element 和 size),弹出栈顶元素poll() ,时间复杂度为O(1)。

初始化capacity和size不一样

PriorityQueue<Integer> queue = new PriorityQueue<>(6, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2-o1;
            }
        }); //capacity = 6

int size = queue.size(); //size = 0

  

-------------- Leetcode Tips ----------------

二维数组“上下左右”的移动

构建下面的数组,然后遍历它即可实现上下左右的移动。

        int[][] direction = new int[4][];
        direction[0] = new int[]{-1, 0};
        direction[1] = new int[]{1, 0};
        direction[2] = new int[]{0, -1};
        direction[3] = new int[]{0, 1};   

奇数/偶数总个数,找Median中间值

对于奇数的情况,直接找到最中间的数即可; 偶数的话需要求最中间两个数的平均值。

若整个序列的长度为n,那无论n是奇数、偶数,都可以用下面的公式来找中间数的“索引”:

(n+1)/2 + (n+2)/2 

__________________

             2           

n = 7 时,[ 8/2+9/2  ] / 2 = [4 + 4] / 2 = 4 (第四个,index为3,起始从0开始)-- 奇数的情况,直接找到最中间的数即可

n = 8 时,[9/2+10/2 ] / 2 = [4 + 5] / 2 -- 偶数的话需要求最中间两个数的平均值

int相加防止溢出

如果两个int n1和int n2,都接近了Integer.MAX_VALUE.然而又想求其平均值。

直接相加就会溢出。所以可以转换为:

n1+(n2-n1)/2

  • = (2*n1)/2 + (n2-n1)/2 = (n1+n2)/2

数组打印格式 (int[]打印,ArrayList<T>打印)

在牛客网经常能看到以下格式的数组格式输出:

//一维数组
[2,3,4,1,2]
//二维数组 [[
3,8,9],[0,5,2,3],[3,6,7,8],[1,4,3]]

ArrayList(Integer).toString() -- 能用于输出一维/多维数组

ArrayList<T> --> AbstractList<T> --> Collection<T>

在Collection<T>中,重写了toString(),可以输出一维/多维数组

        ArrayList<ArrayList<Integer>> res = new ArrayList<>();

        ArrayList<Integer> res1 = new ArrayList<>();
        res1.add(1);
        res1.add(1);
        res1.add(1);
        ArrayList<Integer> res2 = new ArrayList<>();
        res2.add(2);
        res2.add(2);
        res2.add(2);
        res2.add(2);
        ArrayList<Integer> res3 = new ArrayList<>();
        res3.add(3);
        res3.add(3);

        res.add(res1);
        res.add(res2);
        res.add(res3);

        System.out.println(res.toString());
        //[[1, 1, 1], [2, 2, 2, 2], [3, 3]]

Arrays.toString(int[]/double[]/char[]/...) -- 只能用于输出一维数组

只能用于输出一维数组 int[], double[], char[],...

Arrays.toString(new int[]{5,1,2,3});
//输出[5,1,2,3]

Arrays.toString(new double[]{5.0,1.0,2.0,3.0});
//输出[5.0,1.0,2.0,3.0]

原文地址:https://www.cnblogs.com/frankcui/p/12125210.html