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]

  • 相关阅读:
    【集合遍历-Java】
    【eclipse】使用说明
    【Java IO流】浅谈io,bio,nio,aio
    【Mysql数据库】知识点总结
    【struts2】学习笔记
    【EL&JSTL】学习笔记
    思科交换机-常用命令及配置
    【JDBC-MVC模式】开发实例
    【JDBC】java连接MySQL数据库步骤
    【JDBC】Servlet实例
  • 原文地址:https://www.cnblogs.com/frankcui/p/12125210.html
Copyright © 2011-2022 走看看