zoukankan      html  css  js  c++  java
  • 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]

  • 相关阅读:
    外校培训前三节课知识集合纲要(我才不会告诉你我前两节只是单纯的忘了)
    floyd算法----牛栏
    bfs开始--马的遍历
    (DP 线性DP 递推) leetcode 64. Minimum Path Sum
    (DP 线性DP 递推) leetcode 63. Unique Paths II
    (DP 线性DP 递推) leetcode 62. Unique Paths
    (DP 背包) leetcode 198. House Robber
    (贪心 复习) leetcode 1007. Minimum Domino Rotations For Equal Row
    (贪心) leetcode 452. Minimum Number of Arrows to Burst Balloons
    (字符串 栈) leetcode 921. Minimum Add to Make Parentheses Valid
  • 原文地址:https://www.cnblogs.com/frankcui/p/12125210.html
Copyright © 2011-2022 走看看