字符串处理 leetcode 2047.句中的有效单词数2047. 句子中的有效单词数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 class Solution {public : bool check (string s) { int k = 0 ; if (s.size () == 0 ) return false ; for (int i = 0 ; i < s.size (); ++ i) { if (s[i] == '!' || s[i] == '.' || s[i] == ',' ) { if (i != s.size () - 1 ) return false ; } if (isdigit (s[i])) return false ; if (s[i] == '-' ) { if (k ++) return false ; if (i == 0 || i == s.size () - 1 || !isalpha (s[i + 1 ])) return false ; } } return true ; } int countValidWords (string sentence) { string tmp = "" ; int cnt = 0 ; for (int i = 0 ; i < sentence.size (); ++ i) { if (sentence[i] == ' ' ) { if (check (tmp)) cnt ++; tmp = "" ; continue ; } tmp += sentence[i]; } if (check (tmp)) cnt ++; return cnt; } };
关于字符串处理部分 获得一个字符串中连续的数字
1 2 3 4 5 6 7 8 9 10 11 12 13 for (int i = 0 ; i < str.size (); ++ i) { char ch = str[i]; if (isdigit (ch)) { int x = 0 , j = i; while (j < str.size () && isdigit (str[j])) { x = x * 10 + (str[j ++] - '0' ); } i = j - 1 ; num.push (x); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 class Solution {public : int myAtoi (string s) { int sign = 1 ; int i = 0 ; for (; i < s.size (); ++ i) { if (s[i] != ' ' ) break ; } if (s[i] == '-' ) sign = -1 ; if (!isdigit (s[i]) && s[i] != '-' && s[i] != '+' ) return 0 ; int ans = 0 ; int start = i; for (int i = start; i < s.size (); ++ i) { if (i > start && !isdigit (s[i])) break ; if (i == start && !isdigit (s[i])) continue ; if ((INT_MAX - (s[i] - '0' )) / 10 < ans) { if (sign == 1 ) return INT_MAX; else return INT_MIN; } ans = ans * 10 + (s[i] - '0' ); } return ans * sign; } };
Java-leetcode-Tips 需要对字符串修改时可以先转化为字符数组进行处理,最后再转换回来。
cs = str.toCharArray();
String res = String.valueOf(cs);
使用StringBuilder
StringBuilder sb = new StringBuilder(str);
sb.setCharAt(i, 'ch');
List转化为二维数组1 2 List<int []> res = new ArrayList <>(); int [][] resArr = res.toArray(new int [res.size()][]);
排序-自定义构造器 java例如:
1 2 3 4 5 6 7 8 9 class Pair { private int index; private int freq; public Pair (int freq, char ch) { this .freq = freq; this .ch = ch; } }
需求:按照Pair
的freq
属性从大到小排序。
1 2 3 4 5 6 7 8 9 List<Pair> arr = new ArrayList <>(); Arrays.sort(arr, (p1, p2) -> p2.freq - p1.freq); Arrays.sort(arr, new Comparator <Pair>() { public int compare (Pair a, Pair b) { return b.freq - a.freq; } });
C++1 2 3 sort (arr.begin (), arr.end (), [](const pair<int , char > & p1, const pair<int , char > & p2) { return p1.f irst > p2.f irst; });
Ps:如何快速对int
数组排序?
例如:剑指 Offer II 074. 合并区间 中需要按照intervals[i][0]
的大小顺序对二维数组intervals[][]
进行排序
Sol1:自己手写快排
1 2 3 4 5 6 7 8 9 10 11 12 13 void quickSort (int [][] q, int l, int r) { if (l >= r) return ; int x = q[l + r >> 1 ][0 ], i = l - 1 , j = r + 1 ; while (i < j) { while (q[++i][0 ] < x); while (q[--j][0 ] > x); if (i < j) { int [] tmp = q[i]; q[i] = q[j]; q[j] = tmp; } } quickSort(q, l, j); quickSort(q, j + 1 , r); }
Sol2:先将 i n t int i n t 数组装箱,然后再写 L a m b d a Lambda L a m b d a 表达式,最后再转移回来
1 nums = Arrays.stream(nums).boxed().sorted((a, b) -> b - a).mapToInt(p -> p).toArray();
C++自定义哈希函数c++ 不能直接将pair
类型的变量放入集合或者哈希表,需要自己提供一个哈希函数
1 2 3 4 5 auto hash_p = [](const pair<int , int > &p) -> size_t { static hash<long long > hash_ll; return hash_ll (p.first + (static_cast <long long >(p.second) << 32 )); }; unordered_set<pair<int , int >, decltype (hash_p)> points (0 , hash_p);
回溯问题学习自代码随想录的力扣题解
代码随想录-回溯法理论
确定递归的返回条件 确定是组合问题还是排列问题 确定每个树层的搜索逻辑 脑子里要有个清晰的递归树,树层,树枝,是在代码的何处进入和出去的。
经典回溯问题
给定一个可包含重复数字的序列 nums
,按任意顺序 返回所有不重复的全排列。
输入:nums=[1,1,2]
输出:[[1,1,2],[1,2,1],[2,1,1]]
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class Solution { public List<List<Integer>> permuteUnique (int [] nums) { Arrays.sort (nums); List<List<Integer>> res = new ArrayList<>(); List<Integer> cur = new ArrayList<>(); boolean[] used = new boolean[nums.length]; dfs (res, cur, nums, used); return res; } void dfs (List<List<Integer>> res, List<Integer> cur, int [] q, boolean[] used) { if (cur.size () == q.length) { res.add (new ArrayList<>(cur)); return ; } for (int i = 0 ; i < q.length; ++i) { if (i > 0 && q[i] == q[i - 1 ] && used[i - 1 ] == false ) continue ; if (used[i] == true ) continue ; cur.add (q[i]); used[i] = true ; dfs (res, cur, q, used); used[i] = false ; cur.remove (cur.size () - 1 ); } } }
二叉树的前/中/后遍历——迭代版本1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Solution {public : vector<int > preorderTraversal (TreeNode* root) { if (root == nullptr ) return {}; stack<TreeNode*> st; vector<int > res; st.push (root); while (!st.empty ()) { TreeNode* p = st.top (); st.pop (); res.push_back (p->val); if (p->right != nullptr ) st.push (p->right); if (p->left != nullptr ) st.push (p->left); } return res; } };
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Solution {public : vector<int > inorderTraversal (TreeNode* root) { if (root == nullptr ) return {}; vector<int > ans; stack<TreeNode*> s; TreeNode* curr = root; while (curr != nullptr || !s.empty ()) { while (curr != nullptr ) { s.push (curr); curr = curr->left; } curr = s.top (); s.pop (); ans.push_back (curr->val); curr = curr->right; } return ans; } };
反转前序遍历c++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Solution {public : vector<int > postorderTraversal (TreeNode* root) { if (!root) return {}; deque<int > ans; stack<TreeNode*> s; s.push (root); while (!s.empty ()) { TreeNode* n = s.top (); s.pop (); ans.push_front (n->val); if (n->left) s.push (n->left); if (n->right) s.push (n->right); } return vector <int >(ans.begin (), ans.end ()); } };
java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Solution { public List<Integer> postorderTraversal (TreeNode root) { List<Integer> res = new ArrayList <>(); if (root == null ) return res; Deque<TreeNode> st = new ArrayDeque <>(); st.push(root); while (!st.isEmpty()) { TreeNode cur = st.peek(); res.add(st.pop().val); if (cur.left != null ) st.push(cur.left); if (cur.right != null ) st.push(cur.right); } Collections.reverse(res); return res; } }
迭代版本c++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 class Solution {public : vector<int > postorderTraversal (TreeNode *root) { if (!root) return {}; vector<int > vec; stack<TreeNode *> stk; TreeNode *prev = nullptr ; auto node = root; while (!stk.empty () || node) { while (node) { stk.push (node); node = node->left; } node = stk.top (); stk.pop (); if (node->right && node->right != prev) { stk.push (node); node = node->right; } else { vec.push_back (node->val); prev = node; node = nullptr ; } } return vec; } };
迭代优化版本java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Solution { public List<Integer> postorderTraversal (TreeNode root) { List<Integer> res = new ArrayList <>(); if (root == null ) return res; Deque<TreeNode> st = new ArrayDeque <>(); Set<TreeNode> v = new HashSet <>(); st.push(root); while (!st.isEmpty()) { TreeNode cur = st.peek(); if ((cur.right == null && cur.left == null ) || v.contains(cur)) { st.pop(); res.add(cur.val); continue ; } if (cur.right != null ) st.push(cur.right); if (cur.left != null ) st.push(cur.left); v.add(cur); } return res; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Solution {public : vector<vector<int >> levelOrder (TreeNode* root) { queue<TreeNode*> q; vector<vector<int >> res; if (root == nullptr ) return res; q.push (root); while (!q.empty ()) { int size = q.size (); vector<int > tmp; while (size --){ TreeNode *cur = q.front (); q.pop (); tmp.push_back (cur->val); if (cur->left != nullptr ) q.push (cur->left); if (cur->right != nullptr ) q.push (cur->right); } res.push_back (tmp); } return res; } };
层序遍历–BFS相关例题从上到下打印出二叉树的每个节点,同一层的节点按照从左到右的顺序打印。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Solution {public : vector<int > levelOrder (TreeNode* root) { vector<int > res; queue<TreeNode*> q; if (root == NULL ) return res; q.push (root); while (!q.empty ()) { int n = q.size (); while (n --) { TreeNode *tmp = q.front (); q.pop (); res.emplace_back (tmp->val); if (tmp->left != NULL ) q.push (tmp->left); if (tmp->right != NULL ) q.push (tmp->right); } } return res; } };
从上到下按层打印二叉树,同一层的节点按从左到右的顺序打印,每一层打印到一行 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Solution {public : vector<vector<int >> levelOrder (TreeNode* root) { vector<vector<int >> res; if (root == NULL ) return res; queue<TreeNode*> q; q.push (root); while (!q.empty ()) { vector<int > temp; for (int i = q.size (); i > 0 ; -- i) { TreeNode *tmp = q.front (); q.pop (); temp.push_back (tmp->val); if (tmp->left != NULL ) q.push (tmp->left); if (tmp->right != NULL ) q.push (tmp->right); } res.push_back (temp); } return res; } };
请实现一个函数按照之字形顺序打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右到左的顺序打印,第三行再按照从左到右的顺序打印,其他行以此类推。
引入双端队列
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 class Solution {public : vector<vector<int >> levelOrder (TreeNode* root) { vector<vector<int >> res; if (root == NULL ) return res; deque<TreeNode*> dq; dq.push_back (root); int flag = 0 ; while (!dq.empty ()) { vector<int > tmpAns; int n = dq.size (); while (n --) { TreeNode *cur; if (flag & 1 ) { cur = dq.back (); dq.pop_back (); tmpAns.push_back (cur->val); if (cur->right != NULL ) dq.push_front (cur->right); if (cur->left != NULL ) dq.push_front (cur->left); } else { cur = dq.front (); dq.pop_front (); tmpAns.push_back (cur->val); if (cur->left != NULL ) dq.push_back (cur->left); if (cur->right != NULL ) dq.push_back (cur->right); } } res.push_back (tmpAns); flag ++; } return res; } };
Morris遍历-空间占用O(1)参考 神级遍历——morris
Morris中序遍历
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public List<Integer> inorderTraversal (TreeNode root) { List<Integer> res = new ArrayList <>(); TreeNode cur = root; TreeNode rightmost = null ; while (cur != null ) { rightmost = cur.left; if (cur.left != null ) { while (rightmost.right != null && rightmost.right != cur) { rightmost = rightmost.right; } if (rightmost.right == null ) { rightmost.right = cur; cur = cur.left; continue ; } else rightmost.right = null ; } res.add(cur.val); cur = cur.right; } return res; }
Morris先序遍历
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public List<Integer> preorderTraversal (TreeNode root) { List<Integer> res = new ArrayList <>(); TreeNode cur = root; TreeNode mostright = null ; while (cur != null ) { mostright = cur.left; if (mostright != null ) { while (mostright.right != null && mostright.right != cur) mostright = mostright.right; if (mostright.right == null ) { mostright.right = cur; res.add(cur.val); cur = cur.left; continue ; } else mostright.right = null ; } else res.add(cur.val); cur = cur.right; } return res; }
二叉树的序列化相关问题 序列化二叉树实现LeetCode上序列化二叉树的方式
例如
1 2 输入:root = [1,2,3,null,null,4,5] 输出:[1,2,3,null,null,4,5]
序列化
层序遍历的思路
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public String serialize (TreeNode root) { if (root == null ) return "[]" ; StringBuffer sb = new StringBuffer ("[" ); Queue<TreeNode> q = new LinkedList <>(); q.offer(root); while (!q.isEmpty()) { TreeNode cur = q.poll(); if (cur != null ) { sb.append(cur.val + "," ); q.offer(cur.left); q.offer(cur.right); } else sb.append("null," ); } sb.deleteCharAt(sb.length() - 1 ); sb.append("]" ); return sb.toString(); }
反序列化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public TreeNode deserialize (String data) { if (data.equals("[]" )) return null ; String[] arr = data.substring(1 , data.length() - 1 ).split("," ); TreeNode root = new TreeNode (Integer.parseInt(arr[0 ])); Queue<TreeNode> q = new LinkedList <>(); q.offer(root); int idx = 1 ; while (!q.isEmpty()) { TreeNode curNode = q.poll(); if (!"null" .equals(arr[idx])) { curNode.left = new TreeNode (Integer.parseInt(arr[idx])); q.offer(curNode.left); } idx++; if (!"null" .equals(arr[idx])) { curNode.right = new TreeNode (Integer.parseInt(arr[idx])); q.offer(curNode.right); } idx++; } return root; }
从中序和前序遍历序列构建二叉树leetcode: 105. 从前序与中序遍历序列构造二叉树
给出前序遍历和中序遍历的序列,构建二叉树
ps:indexMap保存节点值到中序遍历序列中索引的映射,用于快速定位中序遍历序列中根节点的位置
节点值必须无重复
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Solution { public Map<Integer, Integer> indexMap; public TreeNode buildTree (int [] preorder, int [] inorder) { int n = preorder.length; indexMap = new HashMap <>(); for (int i = 0 ; i < inorder.length; ++i) { indexMap.put(inorder[i], i); } return build(preorder, inorder, 0 , n - 1 , 0 , n - 1 ); } public TreeNode build (int [] preorder, int [] inorder, int preLeft, int preRight, int inLeft, int inRight) { if (preLeft > preRight) return null ; TreeNode root = new TreeNode (preorder[preLeft]); int inorderRootIdx = indexMap.get(preorder[preLeft]); int numsOfLeft = inorderRootIdx - inLeft; root.left = build(preorder, inorder, preLeft + 1 , preLeft + numsOfLeft, inLeft, inorderRootIdx - 1 ); root.right = build(preorder, inorder, preLeft + numsOfLeft + 1 , preRight, inorderRootIdx + 1 , inRight); return root; } }
前序遍历:Root node ,[Left subtree的前序],[Right subtree的前序]
中序遍历:[Left subtree的中序],Root node ,[Right subtree的中序]
二叉树的路径问题参见星晴Pro的乐扣题解
输入一棵二叉树的根节点,判断该树是不是平衡二叉树。如果某二叉树中任意节点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树。
1 2 3 4 5 6 7 8 9 10 11 12 13 class Solution {public : bool isBalanced (TreeNode* root) { if (root == NULL ) return true ; int left = depth (root->left); int right = depth (root->right); return abs (right - left) <= 1 && isBalanced (root->left) && isBalanced (root->right); } int depth (TreeNode *root) { if (root == NULL ) return 0 ; return max (depth (root->left), depth (root->right)) + 1 ; } };
图论——建图&搜索 建图邻接表法
为节点A添加一条指向节点B的边
A节点的值为a,B节点同理
1 2 3 4 5 void add (int a, int b) { e[idx] = b; ne[idx] = h[a]; h[a] = idx++; }
DFS-深度优先遍历1 2 3 4 5 6 7 8 9 void dfs (int u) { visited[u] = true ; for (int i = h[u]; i != -1 ; i = ne[i]) { int val = e[i]; if (!visited[val]) { dfs(val); } } }
BFS-广度优先遍历1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 void bfs (int u) { Queue<Integer> q = new LinkedList <>(); q.offer(u); while (!q.isEmpty()) { int size = q.size(); while (size-- > 0 ) { int cur = q.poll(); for (int i = h[cur]; i != -1 ; i = ne[i]) { int val = e[i]; if (!visited[val]) { q.offer(val); visited[val] = true ; } } } } }
几处细节首先,关于各个数组的含义 以下默认节点序号即为节点的值,例如
h[N],数组的索引为:节点序号,数组的值为:该节点的“全局地址idx“
e[M],索引为:全局地址idx,值:节点序号
ne[M],索引为:全局地址idx,值:全局地址idx
visited[N],索引为:节点序号,值:true/false表示该节点是否被访问过
无向图,构建的时候,需要增加 a → b a\to b a → b 的边,也要增加 b → a b\to a b → a 的边,且M = 2 ∗ N M=2*N M = 2 ∗ N
h[N] 存储的是每个邻接表的表头地址,初始化时,均为-1,Arrays.fill(h, -1)
。可以用来判断链表的终止
关于链表的模拟,查看笔记数组模拟单双链表
例题863. 二叉树中所有距离为 K 的结点
先将二叉树转为图,再进行 BFS \text{BFS} BFS
1 2 3 4 5 6 7 8 9 10 11 12 13 public void build (TreeNode root) { if (root == null ) return ; if (root.left != null ) { add(root.val, root.left.val); add(root.left.val, root.val); build(root.left); } if (root.right != null ) { add(root.val, root.right.val); add(root.right.val, root.val); build(root.right); } }
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 class Solution { static final int N = 510 ; static final int M = 2 * N; int idx = 0 ; int [] h = new int [N]; int [] ne = new int [M]; int [] e = new int [M]; boolean [] visited = new boolean [N]; void add (int a, int val) { e[idx] = val; ne[idx] = h[a]; h[a] = idx++; } public List<Integer> distanceK (TreeNode root, TreeNode target, int k) { Arrays.fill(h, -1 ); idx = 0 ; build(root); int tt = target.val; visited[tt] = true ; Queue<Integer> q = new LinkedList <>(); q.offer(tt); List<Integer> res = new ArrayList <>(); while (!q.isEmpty() && k >= 0 ) { int size = q.size(); while (size-- > 0 ) { int cur = q.poll(); if (k == 0 ) { res.add(cur); continue ; } for (int i = h[cur]; i != -1 ; i = ne[i]) { if (!visited[e[i]]) { q.offer(e[i]); visited[e[i]] = true ; } } } k--; } return res; } public void build (TreeNode root) { if (root.left != null ) { add(root.val, root.left.val); add(root.left.val, root.val); build(root.left); } if (root.right != null ) { add(root.val, root.right.val); add(root.right.val, root.val); build(root.right); } } }
哈希表相关见哈希表题目集锦
链表相关1 2 3 4 5 6 7 8 9 10 11 ListNode* reverseList (ListNode* head) { ListNode* prev = nullptr ; ListNode* curr = head; while (curr != nullptr ) { ListNode* nextTemp = curr->next; curr->next = prev; prev = curr; curr = nextTemp; } return prev; }
快慢指针见快慢指针解决环形链表问题
快慢指针可以寻找链表的中间结点
新建一个前置节点dummy
例如,四个节点的情况
dummy–>4->[2]->1->3–>null
返回的midNode=[2]
五个节点的情况
dummy–>-1->5->[3]->4->0–>null
返回的midNode=[3]
1 2 3 4 5 6 7 8 9 ListNode findMid (ListNode head) { ListNode dummy = new ListNode (0 , head); ListNode fast = dummy, slow = dummy; while (fast != null && fast.next != null ) { fast = fast.next.next; slow = slow.next; } return slow; }
综合剑指 Offer II 026. 重排链表
重排链表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 class Solution { public void reorderList (ListNode head) { ListNode fast = head, slow = head, p = head; while (fast != null && fast.next != null ) { fast = fast.next.next; slow = slow.next; } ListNode cur = slow.next; slow.next = null ; ListNode prev = null ; while (cur != null ) { ListNode next = cur.next; cur.next = prev; prev = cur; cur = next; } while (prev != null ) { ListNode next1 = prev.next; ListNode next2 = p.next; prev.next = p.next; p.next = prev; prev = next1; p = next2; } return ; } }
剑指 Offer II 027. 回文链表
注意空间复杂度,要满足 O ( 1 ) \Omicron(1) O ( 1 ) 代码略 25. K 个一组翻转链表
还是翻转加指针定位,过程略微复杂
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 class Solution { public ListNode reverseKGroup (ListNode head, int k) { ListNode dummy = new ListNode (0 , head); ListNode prev = dummy, tail = head; while (head != null ) { tail = prev; int tt = k; while (tt-- > 0 && tail != null ) tail = tail.next; if (tail == null ) return dummy.next; ListNode nextNode = tail.next; ListNode[] temp = myReverse(head, tail); head = temp[0 ]; tail = temp[1 ]; tail.next = nextNode; prev.next = head; prev = tail; head = nextNode; } return dummy.next; } public ListNode[] myReverse(ListNode head, ListNode tail) { ListNode prev = tail.next; ListNode cur = head; while (prev != tail) { ListNode nex = cur.next; cur.next = prev; prev = cur; cur = nex; } return new ListNode []{tail, head}; } }
剑指 Offer II 077. 链表排序
归并的思想
完整代码
由于是链表,不能随机访问,因此无法使用堆排和快排,只能使用归并排序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 class Solution { public ListNode sortList (ListNode head) { return merge(head); } ListNode merge (ListNode list) { if (list == null ) return null ; if (list.next == null ) return list; ListNode midNode = findMid(list); ListNode rightStart = midNode.next; midNode.next = null ; ListNode left = merge(list); ListNode right = merge(rightStart); return mergeListNode(left, right); } ListNode mergeListNode (ListNode x, ListNode y) { ListNode res = new ListNode (0 ); ListNode p = res; while (x != null && y != null ) { if (x.val < y.val) { p.next = x; x = x.next; } else { p.next = y; y = y.next; } p = p.next; } if (x != null ) p.next = x; if (y != null ) p.next = y; return res.next; } ListNode findMid (ListNode head) { ListNode dummy = new ListNode (0 , head); ListNode fast = dummy, slow = dummy; while (fast != null && fast.next != null ) { fast = fast.next.next; slow = slow.next; } return slow; } }
23. 合并K个升序链表
简单归并
java完整代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 class Solution { public ListNode mergeKLists (ListNode[] lists) { return mergeList(lists, 0 , lists.length - 1 ); } ListNode merge (ListNode[] lists, int l, int r) { if (l > r) return null ; if (l == r) return lists[l]; int mid = l + r >> 1 ; return mergerListNode(merge(lists, l, mid), merge(lists, mid + 1 , r)); } ListNode mergerListNode (ListNode l1, ListNode l2) { ListNode dummy = new ListNode (0 ); ListNode p = dummy; while (l1 != null && l2 != null ) { if (l1.val < l2.val) { p.next = l1; l1 = l1.next; } else { p.next = l2; l2 = l2.next; } p = p.next; } if (l1 != null ) p.next = l1; else p.next = l2; return dummy.next; } }
数学计算 1. 整数除法剑指 Offer II 001. 整数除法
题解参考:简单易懂Java/C++ /Python/js/go - 整数除法(剑指)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 class Solution { public int divide (int a, int b) { if (a == Integer.MIN_VALUE && b == -1 ) return Integer.MAX_VALUE; int sign = (a > 0 ) ^ (b > 0 ) ? -1 : 1 ; a = Math.abs(a); b = Math.abs(b); int res = 0 ; for (int i = 31 ; i >= 0 ; i--) { if ((a >>> i) - b >= 0 ) { a -= (b << i); res += (1 << i); } } return sign == 1 ? res : -res; } }
学习位运算 和 边界控制 的方法
总结:
对于 Math.abs()
函数,取绝对值 Math.abs(Integer.MIN_VALUE)
的结果为− 2147483648 -2147483648 − 2 1 4 7 4 8 3 6 4 8
第18行
中原始代码是
但是存在几个问题:
b b b 左移可能会越界 -->改为a a a 右移:右移不会出现越界 的情况。(a >> i) >= b;
但是如果b为− 2147483648 -2147483648 − 2 1 4 7 4 8 3 6 4 8 ,即Integer.MIN_VALUE
最小值,(a >> i) >= b
永远为t r u e true t r u e
改为(a >> i) - b >= 0
,
如果a a a 为− 2147483648 -2147483648 − 2 1 4 7 4 8 3 6 4 8 ,取绝对值还是− 2147483648 -2147483648 − 2 1 4 7 4 8 3 6 4 8 ,右移是负数
改为无符号右移 ,即(a >>> i) - b >= 0