显示标签为“总结”的博文。显示所有博文
显示标签为“总结”的博文。显示所有博文

2015年8月31日星期一

acknowledgement

preorder:先序遍历:根结点-》左节点-》右节点
inorder:中序遍历:左节点-》根结点-》右节点
postorder:后续遍历:左节点-》右节点-》根结点

stack 后进先出 stack.push() stack.pop() stack.isEmpty()
queue 先进先出 queue.offer() queue.poll() queue.isEmpty() 
Queue<> queue= new LinkedList<>()

Collection.reverse(List)翻转list

Arrays.sort(array) 给列表排序

hashmap.containsKey() 哈希表是否存在某个key值
hashmap.get(key)
hashset.add()
hashset.contains()
hashset.clear()

String.split(x) 以x字符分割字符串 生成一个sring[]
String.trim() 去掉字符串首尾的空格
String.replace(" "."") 去掉所有的空格
String.toCharArray() 把字符串变成Char[] (可以Arrays.sort(char[]) 给字符串排序) 只有string 有 stringbuilder没有
String.equals()


ArrayList : add remove addAll

StringBuilder append() toString() reverse()只有sb有 deleteCharAt(i)
insert(int index, string/ int/char.... boolean x)

Integer.valueOf(string) 把string转化成integer
Integer.parseInt(string)把string转化成integer
string.charAt(i) - '0' 把string的i位编程int

row 行数
column 列数

Int[] = {1, 2, ....}


ListNode 在findmid 中 要fast.next != null && fast.next.next != null 


Tree 的traversal (!stack.isEmpty() || node != null)

2015年7月25日星期六

链表总结

链表的基本形式是:1 -> 2 -> 3 -> null,反转需要变为 3 -> 2 -> 1 -> null。

  • 访问某个节点 curt.next 时,要检验 curt 是否为 null。 
  • 要把反转后的最后一个节点(即反转前的第一个节点)指向 null。

public ListNode reverse(ListNode head) {
    ListNode prev = null;
    while (head != null) {
        ListNode next = head.next;
        head.next = prev;
        prev = head;
        head = next;
    }
    return prev;
}




1 -> 2 -> 3 -> 4 -> 5 -> 6 -> null变为 1 -> 5 -> 4 -> 3 -> 2 -> 6 -> null
翻转m--n, 不仅要把m-n转好, preMnode. next 要指向n, m.next 要指向postNnode

public class Solution {
    public ListNode reverseBetween(ListNode head, int m, int n) {
        if (m > n || head == null){
            return head;
        }
        ListNode dummy = new ListNode(0);
        dummy.next = head;
        head = dummy;
        for (int i = 1; i < m; i++){
            head = head.next;
        }
        ListNode preM = head;
        ListNode mNode = head.next;
        ListNode nNode = mNode;
        ListNode postN = mNode.next;
        for (int i = m; i < n; i++){
            ListNode tem = postN.next;
            postN.next = nNode;
            nNode = postN;
            postN = tem;
        }
        preM.next = nNode;
        mNode.next = postN;
        return dummy.next;
    }
}

删除链表中的某个节点 

删除链表中的某个节点一定需要知道这个点的前继节点,所以需要一直有指针指向前继节点。
然后只需要把 prev -> next = prev -> next -> next 即可。但是由于链表表头可能在这个过程中产生变化,导致我们需要一些特别的技巧去处理这种情况。就是下面提到的 Dummy Node。

找中点

        ListNode fast = head;
        ListNode slow = head;
        while (fast!= null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
        }


链表指针的鲁棒性

综合上面讨论的两种基本操作,链表操作时的鲁棒性问题主要包含两个情况:
  • 当访问链表中某个节点 curt.next 时,一定要先判断 curt 是否为 null。
  • 全部操作结束后,判断是否有环;若有环,则置其中一端为 null。

Dummy Node


Dummy node 是一个虚拟节点,也可以认为是标杆节点。Dummy node 就是在链表表头 head 前加一个节点指向 head,即 dummy -> head。Dummy node 的使用多针对单链表没有前向指针的问题,保证链表的 head 不会在删除操作中丢失。除此之外,还有一种用法比较少见,就是使用 dummy node 来进行head的删除操作,比如 Remove Duplicates From Sorted List II,一般的方法current = current.next 是无法删除 head 元素的,所以这个时候如果有一个dummy node在head的前面。
所以,当链表的 head 有可能变化(被修改或者被删除)时,使用 dummy node 可以很好的简化代码,最终返回 dummy.next 即新的链表。

快慢指针

快慢指针也是一个可以用于很多问题的技巧。所谓快慢指针中的快慢指的是指针向前移动的步长,每次移动的步长较大即为快,步长较小即为慢,常用的快慢指针一般是在单链表中让快指针每次向前移动2,慢指针则每次向前移动1。快慢两个指针都从链表头开始遍历,于是快指针到达链表末尾的时候慢指针刚好到达中间位置,于是可以得到中间元素的值。快慢指针在链表相关问题中主要有两个应用:
  • 快速找出未知长度单链表的中间节点 设置两个指针 *fast*slow 都指向单链表的头节点,其中*fast的移动速度是*slow的2倍,当*fast指向末尾节点的时候,slow正好就在中间了。
  • 判断单链表是否有环 利用快慢指针的原理,同样设置两个指针 *fast*slow 都指向单链表的头节点,其中 *fast的移动速度是*slow的2倍。如果 *fast = NULL,说明该单链表 以 NULL结尾,不是循环链表;如果 *fast = *slow,则快指针追上慢指针,说明该链表是循环链表。


Remove Duplicates from Sorted List

Remove Duplicates from Sorted ListII



Reorder List

Merge k Sorted Lists

Remove Nth Node From End of List

List Cycle

Linked List Cycle II


Reverse Nodes in k-Group

Rotate List

Insertion Sort List

2015年7月23日星期四

binary search 总结

二分法比较简单, 时间复杂度为O(log n)
mid = right + (left - right) / 2 防止left right 都大时候溢出
两种二分法
1. start <= end 每次start = mid + 1 或者 end = mid - 1
2. start + 1 < end 每次 start = mid 或者 end = mid

正常情况下用1的方法, 但是如果mid+1 或者mid-1 可能会错过target的话(mid 为target) 例如Find Minimum in Rotated Sorted Array 用方法2

Search for a Range

Search Insert Position

Sqrt(x)

Search in Rotated Sorted Array

前边的题只需要mid 跟target比较 而这两道题则还需要跟左右边界比较 所以要注意跟边界相等的情况下不仅会出现 > < 还有>= <=


Find Minimum in Rotated Sorted Array
与之前不同的是如果这道题每次 Amid < A[right] -->mid - 1 = right 的话那么 可能会出现如果此时mid是最小值 但是右边界确实最大值 为了防止这种情况 每次left right 都取 mid 而不是mid +-1 但是这么取得话就不能用left <= right 了 否则会无限循环 所以这里用left + 1 < right
Find Minimum in Rotated Sorted Array II

Search a 2D Matrix

2015年7月16日星期四

动态规划总结

When to use DP?


  • Input cannot sort
  • Find minimum/maximum result
  • Check the feasibility 找可行性
  • Count all possible solutions 列出所有解


(1) 最优化原理:如果问题的最优解所包含的子问题的解也是最优的,就称该问题具有最优子结构,即满足最优化原理。
(2) 无后效性:即某阶段状态一旦确定,就不受这个状态以后决策的影响。也就是说,某状态以后的过程不会影响以前的状态,只与当前状态有关。
(3)有重叠子问题:即子问题之间是不独立的,一个子问题在下一阶段决策中可能被多次使用到。(该性质并不是动态规划适用的必要条件,但是如果没有这条性质,动态规划算法同其他算法相比就不具备优势

4 Types of DP

  • 1. Matrix DP (10%)
  • 2. Sequence (40%)
  • 3. Two Sequences DP (40%)*
  • 4. Backpack (10%)

通用解法:

1.  状 态 State

2. 方程 Function
状态之间的联系,怎么通过小的状态,来算大的状态

3. 初始化 Intialization
最极限的小状态是什么, 起点

4. 答案 Answer
最大的那个状态是什么,终点

Matrix DP


  • state: f[x][y] 表示我从起点走到 坐 标x,y……
  • function: 研究走到xy 这个点之前的一步是从哪里走的
  • intialize: 起点
  • answer: 终点

Sequence Dp

  • state: f[i]表示“ 前i”个位置/数字/字母,(以第i个为)...
  • function: f[i] = f[j] … j 是i之前的一个位置
  • intialize: f[0]..
  • answer: f[n-1]..


Two Sequences Dp


  • state: f[i][j]代表了第一个sequence的前i个数字/字符 配上第二个sequence的前j个...
  • function: f[i][j] = 研究第i个和第j个的匹配关系
  • intialize: f[i][0] 和 f[0][i](二维数组都要初始化第0行和第0列)
  • answer: f[s1.length()][s2.length()]

1. sequences
Climbing Stairs

Decode Ways

Unique Binary Search Trees

Maximum Subarray


Word Break

Palindrome Partitioning II



2. Matrix
Triangle

Unique Paths I

Unique Paths II

Minimum Path Sum

3. two sequences

Edit Distance

Distinct Subsequences

Interleaving String

Scramble String(3 sequences)

2015年7月13日星期一

backtracking类型题目总结


subsets 这道题属于backtracking 类的模板


subsets II 与i不同的是有重复的数字, 例如[1,2(1)] [1,2(2)]情况相同, 递归过程中每次新选取的可以是和递归数组相同的,(比如nums是[1,2,2,2] 当前递归栈是[1,2(1)] 新加入的[1,2(1), 2(2)]是合法的) 但是后面的元素就不能相同了([1, 2(1), 2(3)]就不合法 因为重复了)。
if ( i != pos && num[i] == num[i - 1]) {
                continue;
            }    
用这个方法防止重复。


permutations
与subset不同的是递归传入参数每次从0开始, 为了避免重复 需要构建一个boolean[] 以记录哪个点访问过


permutations II与I 不同的是出现了重复的问题, 所以如果当前元素与之前元素相同 而之前的元素没有访问过 说明相似的情况已经出现过 要避免再出现 所以用
if(i > 0 && num[i] == num[i-1] && visit[i-1] == false ){
                continue;
            }





Combinations
combination sum 递归条件是 target = 0 加入结果 〈0就return
Combination Sum II


Generate Parentheses  返回条件是left 和right都为0
递归条件有两个: 1. left › 0时候可以选择增加left 2. left ‹ right 时 可以增加right

Letter Combinations of a Phone Number

Restore IP Addresses

Palindrome Partitioning


N-Queens I
N-Queens II

Word Search

Word Break II 

Path Sum II
先把给定数组排序 Arrays.sort()

1. 递归返回条件 
在permutation(排列)和combination时候 要当数组里面元素和所给元素数相等时候返回,
combination sum 时候则需要当target=0时候返回。

  2. 递归的时候传入什么样的参数 (0, pos or pos + 1)

2.1(对于在dfs内recursive的使用dfs时候 ) 如果每次是从i开始扫, 则得到的结果是不会重复的。(会出现[1,2]不会出现[2,1])

2.2 但是如果[1,2] 和[2,1]两个重复情况都要的话, 则要每次从0开始扫.并且要加一个boolean[] visit 来记录某些点是否被访问过


  3. 是否重复选取

如果已选取[1,2(1)] 那么[1,2(2)]就是重复选取 为了确保这种情况不发生,
只要确定当前所选取的值num[i]和num[i-1]不重复就可以, 如果重复跳过num[i].
对于每次从i扫的情况 :
if ( i != pos && num[i] == num[i - 1]) {
                continue;
            }    
对于每次从0开始扫的情况:
if (i > 0 && num[i] == num[i - 1] && !visit[i - 1]){
                continue;
                // important: if previous number have same value as (i)th
                // but have never been visited, then skip current number
            }

a. a4
a. 关于递归返回条件满足时, 往往要把结果存入arraylist, 这时arraylist.add(new(tem))。
当arraylist里存的是list这样可以引用的话要new 一下 存string int这样的就可以直接添加
b. 关于继续递归时候往往要把下层的结果存入中间变量 例如 tem.add() 然后递归helper(), 再tem.delete()
当中间变量是string 时候 再下层递归中可以直接把加入的东西带入string中 以免除这个步骤 例如Palindrome Partitioning中第二种解法

2015年7月12日星期日

二叉查找树 总结

Validate Binary Search Tree 利用中序遍历, 比较之前遍历的是否比当前点小, 如果小就返回true 否则false

Recover Binary Search Tree 同样利用中序遍历, 比较之前的点和当前的点, 把逆序的node存储 最后对换

2015年7月7日星期二

tree 总结

1. tree的性质:


Maximum Depth of Binary Tree
Minimum Depth of Binary Tree
Balanced Binary Tree
Same Tree
Symmetric Tree


树的性质判断是树的数据结构比较基本的操作, 争取一遍bug free。
前3道考树的深度问题,可能会考使用非递归做法(用queue)
Maximum Depth of Binary Tree就是遇到空结点返回0, 递归返回左右子树里面最大的那个。
Minimum Depth of Binary Tree稍微复杂一些, 因为当左子树为空时候按照定义深度不为0 而应该是右边子树到底的深度。 所以要对左子树右子树是否为空做一个判断。
Balanced Binary Tree原理也是求深度, 但是他的返回类型是boolean 所以要建立一个helper函数如果不平衡就返回-1。
后两道考树的便利, 递归退出条件是这两道题最难的地方。
Same Tree就是对两棵树同时便利
Symmetric Tree与前一道题基本相同, 但是输入只有一个treenode 所以要建立一个helper函数 让node的左右子树作为两个node比较。

2. 树的遍历


Binary Tree Preorder Traversal 中左右
Binary Tree Inorder Traversal 左中右
Binary Tree Postorder Traversal 左右中




前三道题属于图的深度优先搜索问题
这三种遍历的递归方法 就是递归左右节点直到空为止。中在哪个位置就在哪个位置把root加入最后的结果。 例如:preoder的递归式就是先加入root然后递归左右子树。
对于迭代的解法就是用栈来实现, 前两题用一个栈来保存前驱的分支结点(相当于图的深度搜索的栈), 然后用一个结点来记录当前结点就可以了。 preorder要在root入栈时候就加入res, inorder则递归完左子树再加入res




Binary Tree Level Order Traversal

Binary Tree Level Order Traversal II

Binary Tree Zigzag Level Order Traversal



三道题属于树的层序遍历, 都是广度优先搜索
用queue来实现


3. 树的求和



Path Sum

Path Sum II

Binary Tree Maximum Path Sum


Sum Root to Leaf Numbers



树的题目基本都是用递归或者分治来解决,主要考虑两个问题:
1)如何把问题分治成子问题给左子树和右子树。
2)考虑结束条件是什么。
难点就是考虑递归结束的条件是什么。
path sum II 要保存所有节点 所有要递归的dfs遍历
b

4. 树的构造


Convert Sorted Array to Binary Search Tree leetcode


Convert Sorted List to Binary Search Tree

array本身就是有序的 只需要取中点作为根 然后递归的寻找左右子树
list无序, 如果每次都寻找中点, 复杂度很高。 所以用中序遍历的方法来构造树
Construct Binary Tree from Preorder and Inorder Traversal

这两道题完全相同 分别通过preorder 和 postorder确定root的值 然后在inorder里找到root的位置 然后递归查找。 比较巧妙的是用了hashmap来存贮inorder的root和index 这样可以在o(1)时间里找到root的位置。

Flatten Binary Tree to Linked List 
这道题可以用先序遍历来逐个点递归解 也可以用stack的非递归方法

5. 树的变换

Populating Next Right Pointers in Each Node
Populating Next Right Pointers in Each Node II

2015年6月8日星期一

Bit Manipulation 位运算

位操作符:
OR (|)AND (&)XOR (^)Left Shift (<<)Right Shift (>>)Not (~)
1|0=11&0=01^0=10010<<2=10001100>>2=0011~1=0
1. a ^ a = 0
2. a ^ b = b ^ a

按位或(OR)

按位或处理两个长度相同的二进制数,两个相应的二进位中只要有一个为1,该位的结果值为1。例如
        0101(十进制5)
     OR 0011(十进制3)
      = 0111(十进制7)

按位异或(XOR)

按位异或运算,对等长二进制模式按位或二进制数的每一位执行逻辑异按位或操作。操作的结果是如果某位不同则该位为1,否则该位为0。例如
         0101
     XOR 0011
       = 0110

按位与(AND)[编辑]

按位与处理两个长度相同的二进制数,两个相应的二进位都为1,该位的结果值才为1,否则为0。例如:
         0101
     AND 0011
       = 0001
常用方法:

  • 1. n & (n-1)能够消 除n中最右侧的一个1。
  • 2. 左移 << 右移>>
  • 3. (a >> x) & 1 或者 a & (1 << x) 判断a的x位是否为0
  • 4. num | (1 << i) 把num的第i位置为1

2015年5月22日星期五

String and Array

1. ASCII字符 string.charAt(i) 返回的事int值
String last = "Kennedy";
int key = last.charAt(2);
System.out.println(key);
==>
110 返回ASCII码数值

2. 若果想返回int数值
str1="2345";
int x=str1.charAt(2)-'0';
==> x=4;
3. string.trim() 除去string前后的空格

4. StringBuilder
4.1 append()可以使char和int
4.2 toString()

2015年4月22日星期三

Binary Tree Level Order Traversal leetcode

Given a binary tree, return the level order traversal of its nodes' values. (ie, from left to right, level by level).
For example:
Given binary tree {3,9,20,#,#,15,7},
    3
   / \
  9  20
    /  \
   15   7
return its level order traversal as:
[
  [3],
  [9,20],
  [15,7]
]
这道题是广度优先搜索的模板,BFS用queue来实现
时间O(n) 空间O(n)

要注意:a.有while和for双重循环 
b.每次都要给size付一个新值(如果不赋值queue.size在不停变化)
c.queue add 和 delete 是.offer 和.poll 
d. queue为什么用linkedlist实现???---Queue是接口, LinkedList可以实现此接口。

public class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> res= new ArrayList<List<Integer>>();
        if (root == null) {
            return res;
        }
        Queue<TreeNode> queue = new LinkedList<TreeNode>();
        queue.offer(root);
        while (!queue.isEmpty() ) {
            int size = queue.size();
            List<Integer> tem = new ArrayList<Integer>();
            for (int i = 0; i < size; i++) {
                TreeNode node = queue.poll();
                tem.add(node.val);
                if (node.left != null) {
                    queue.offer(node.left);
                }
                if (node.right != null) {
                    queue.offer(node.right);
                }
            }
            res.add(tem);
        }
        return res;
    }
}

2015年4月12日星期日

Binary Search

For a given sorted array (ascending order) and a target number, find the first index of this number in O(log n) time complexity.
If the target number does not exist in the array, return -1.
Example
If the array is [1, 2, 3, 3, 4, 5, 10], for given target 3, return 2.
这是一个经典的binary serch的模板
 1.start+1 < end
2. mid = start + (end-start/)2
3. nums[mid] <, ==,> target 的三种情况
4. 是return start end 还是-1
class Solution {
    /**
     * @param nums: The integer array.
     * @param target: Target to find.
     * @return: The first position of target. Position starts from 0.
     */
    public int binarySearch(int[] nums, int target) {
        if (nums.length == 0){
            return -1;
        }
        int start = 0;
        int end = nums.length - 1;
        int mid;
        while (start + 1 < end){
            mid = start + (end - start) / 2;
            if (target > nums[mid]){
                start = mid;
            } else if (target < nums[mid]) {
                end = mid;
            } else {
                end = mid;
            }
        }
        if (nums[start] == target){
            return start;
        } else if (nums[end] == target){
            return end;
        } else {
            return -1;
        }
    }
}