赞
踩
目录
1.1 最长公共子序列(牛客版, leetcode 1143)
4.2 删除排序链表中的重复元素(牛客版,leetcode 82)
1. 给定一个 0-4随机数生成器 如何生成0-6随机数(leetcode 470)
动态规划三要素:初始化状态,状态转移方程,边界
对于最长序列系列的问题(公共,上升,回文)的解决方案就是动态规划,采取填表的方式,定义dp[i][j]。
公共对应两个字符串str1和str2,i 和 j 分别对应str1的str2的位置,用于比较。
对于单个字符串dp[i][j],指的是该字符串中第 i 位到第 j 位的运算。
题目:给定两个字符串str1和str2,输出连个字符串的最长公共子序列。如过最长公共子序列为空,则输出-1。
输入: "1A2C3D4B56","B1D23CA45B6A"
输出:"123456"
思路:设置二维数组dp[i][j],其中 i 和 j 表示从str1的第 i 位到str2的第 j 位中的最长公共子序列
- import java.util.*;
-
- public class Solution {
- /**
- * longest common subsequence
- * @param s1 string字符串 the string
- * @param s2 string字符串 the string
- * @return string字符串
- */
- public String LCS (String s1, String s2) {
- // write code here
- int n1 = s1.length(), n2 = s2.length();
- //默认赋值,[0][?],[?][0]默认两侧皆0,类似公式中0的场景
- int[][] dp = new int[n1 + 1][n2 + 1];
-
- for(int i = 1; i <= n1; i++)
- for(int j = 1; j <= n2; j++){
- if(s1.charAt(i - 1) == s2.charAt(j - 1)){
- dp[i][j] = dp[i - 1][j - 1] + 1;
- }else{
- dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
- }
- }
-
- //反推结果
- int i = n1, j = n2;
- StringBuffer sb = new StringBuffer();
- while(i > 0 && j > 0){
- //反推公式中不相等的场景
- //根据之前的公式,知道两条字符串的下标都前进一位
- if(s1.charAt(i - 1) == s2.charAt(j - 1)){
- sb.append(s1.charAt(i - 1));
- i--;
- j--;
- }else {
- //对应公式中不相等的反推场景
- if(dp[i][j - 1] > dp[i - 1][j]){
- //找大的那个方向,此处是左边大于上面,则该处的结果是来自左边
- j--;
- j--;
- }else if(dp[i][j - 1] < dp[i - 1][j]){
- i--;
- }else if(dp[i][j - 1] == dp[i - 1][j]){
- j--;
- }
- }
- }
-
- //由于是从后往前加入字符的,需要反转才能得到正确结果
- return sb.reverse().toString();
- }
- }
题目:给定数组arr,设长度为n,输出arr的最长上升子序列的长度
输入:[2,1,5,3,6,4,8,9,7]
输出:5
思路:设置并计算dp[i],i 表示从 0 到 i 中当前最长上升数值的个数
- class Solution {
- public int lengthOfLIS(int[] nums) {
- if(nums == null || nums.length == 0)
- return 0;
-
- int[] dp = new int[nums.length];
- dp[0] = 1;
- int res = 1;
- for(int i = 1; i < dp.length; i++){
- int maxval = 0;
- for(int j = 0; j < i; j++){
- if(nums[i] > nums[j]){
- maxval = Math.max(maxval, dp[j]);
- }
- }
- dp[i] = maxval + 1;
- res = Math.max(dp[i], res);
- }
-
- return res;
- }
- }
题目:给定字符串A以及它的长度n,请返回最长回文子串的长度。
输入:"abc1234321ab",12
输出:7
思路:设置二维数组dp[i][j], 表示第 i 个字符到第 j 个字符是否是回文串
- import java.util.*;
-
- public class Palindrome {
- public int getLongestPalindrome(String A, int n) {
- // write code here
- if(A == null || n <= 0) return 0;
-
- // 第 i 个字符到第 j 个字符是否是回文串
- boolean[][] dp = new boolean[n][n];
- int maxLen = 0;
-
- // 字符串收尾字母长度差 len = j - i
- for(int len = 0; len < n; len++){
- // 字符串起始位置 i
- for(int i = 0; i + len < n; i++){
- // 字符串终止位置 j
- int j = i + len;
- if(len == 0){
- dp[i][j] = true;
- }else if(len == 1){
- dp[i][j] = A.charAt(i) == A.charAt(j);
- }else{
- dp[i][j] = (A.charAt(i) == A.charAt(j) && dp[i + 1][j - 1]);
- }
-
- if(dp[i][j] && j - i + 1 > maxLen){
- maxLen = j - i + 1;
- }
- }
- }
-
- return maxLen;
- }
- }
对于每一列来说,他能存的雨水量是他 左边最高墙和右边最高墙中较低的那堵墙的高度减去自身墙的高度 。所以可以用数组记录每列左右最高墙的高度,然后计算每一列可以存的雨水量。
- class Solution {
- public int trap(int[] height) {
- int sum = 0;
- int[] max_left = new int[height.length];
- int[] max_right = new int[height.length];
-
- for(int i = 1; i < height.length - 1; i++){
- max_left[i] = Math.max(max_left[i - 1], height[i - 1]);
- }
-
- for(int i = height.length - 2; i >= 0; i--){
- max_right[i] = Math.max(max_right[i + 1], height[i + 1]);
- }
-
- for(int i = 1; i < height.length - 1; i++){
- int min = Math.min(max_left[i], max_right[i]);
- if(min > height[i]){
- sum += min - height[i];
- }
- }
-
- return sum;
- }
- }
题目:给出一组数字,返回该组数字的所有排列
示例:
[1,2,3]的所有排列如下
[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2], [3,2,1].
回溯核心思想:采用递归,在递归调用之前「做选择」,在递归调用之后「撤销选择」
1、路径:已经做出的选择
2、选择列表:当前可以做的选择
3、结束条件:到达决策树底层,无法再做选择的条件
- import java.util.ArrayList;
- import java.util.Arrays;
- public class Solution {
- ArrayList<ArrayList<Integer>> res;
-
- public ArrayList<ArrayList<Integer>> permute(int[] num) {
- res = new ArrayList<ArrayList<Integer>>();
- if(num == null || num.length == 0) return res;
- //对数组元素进行从小到大排序
- Arrays.sort(num);
- ArrayList<Integer> list = new ArrayList<Integer>();
-
- solve(list, num);
- return res;
- }
-
- private void solve(ArrayList<Integer> list, int[] num){
- if(list.size() == num.length){
- res.add(new ArrayList<Integer>(list));
- return;
- }
- for(int i = 0; i < num.length; i++){
- if(!list.contains(num[i])){
- list.add(num[i]);
- solve(list, num);
- list.remove(list.size() - 1);
- }
- }
- }
- }
- import java.util.*;
-
- /*
- * public class TreeNode {
- * int val = 0;
- * TreeNode left = null;
- * TreeNode right = null;
- * }
- */
-
- public class Solution {
- /**
- *
- * @param root TreeNode类 the root
- * @return bool布尔型一维数组
- */
- public boolean[] judgeIt (TreeNode root) {
- // write code here
- // 两个情况分别判断:
- // 二叉搜索树:每个节点左边节点小于右边节点,左子树的最大值一定小于根节点,小于右子树的最大值;通过中序遍历,严格递增
- // 完全二叉树:层序遍历,除了最后的一层,每层都是满的
- if(root == null) return new boolean[]{false, false};
- boolean b1 = isBST(root);
- boolean b2 = isCBT(root);
- return new boolean[]{b1, b2};
- }
-
- private boolean isBST(TreeNode root){
- List<Integer> list = new ArrayList<>();
- midSearch(root, list);
- for(int i = 0; i < list.size() - 1; i++){
- if(list.get(i) > list.get(i + 1))
- return false;
- }
-
- return true;
- }
-
- private void midSearch(TreeNode root, List<Integer> list){
- if(root == null) return;
- midSearch(root.left, list);
- list.add(root.val);
- midSearch(root.right, list);
- }
-
- private boolean isCBT(TreeNode root){
- // 判断一棵树是否为完全二叉树,层序遍历,一旦出现null,则队列中剩余的节点必须为叶子节点
- Queue<TreeNode> queue = new LinkedList<>();
- queue.add(root);
- while(!queue.isEmpty()){
- TreeNode cur = queue.remove();
- if(cur.left == null){
- if(cur.right != null) return false;
- while(!queue.isEmpty()){
- TreeNode node = queue.remove();
- if(node.left != null ||node.right != null) return false;
- }
- }else{
- queue.add(cur.left);
- }
-
- if(cur.right == null){
- while(!queue.isEmpty()){
- TreeNode node = queue.remove();
- if(node.left != null ||node.right != null) return false;
- }
- }else{
- queue.add(cur.right);
- }
- }
-
- return true;
- }
-
- }
- class Solution {
- public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
- if(root == null) return null;
- if(root == p || root == q) return root;
- TreeNode left = lowestCommonAncestor(root.left,p,q);
- TreeNode right = lowestCommonAncestor(root.right,p,q);
- if(left == null) return right;
- if(right == null) return left;
- if(right != null && left != null) return root;
- return null;
- }
- }
- public class Solution {
- TreeNode res;
- int k;
- TreeNode KthNode(TreeNode pRoot, int k)
- {
- if(pRoot == null || k <= 0) return null;
- this.k = k;
- postOrder(pRoot);
- return res;
- }
-
- private void postOrder(TreeNode pRoot){
- if(pRoot == null) return;
-
- postOrder(pRoot.left);
- if(k == 0) return;
- if(--k == 0) res = pRoot;
- postOrder(pRoot.right);
- }
-
- }
题目:输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。
- public class Solution
- {
- public TreeNode Convert(TreeNode root)
- {
-
- if(root==null)return null;
- if(root.left==null&&root.right==null)return root;
- TreeNode left=Convert(root.left);
- TreeNode p=left;
- while(p!=null&&p.right!=null)
- {
- p=p.right;
- }
- if(left!=null)
- {
- p.right=root;
- root.left=p;
- }
- TreeNode right=Convert(root.right);
- if(right!=null)
- {
- root.right=right;
- right.left=root;
- }
-
- return left!=null?left:root;
- }
- }
题目:输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
- class Solution {
- public TreeNode buildTree(int[] preorder, int[] inorder) {
- if (preorder == null || preorder.length == 0) {
- return null;
- }
- Map<Integer, Integer> indexMap = new HashMap<Integer, Integer>();
- int length = preorder.length;
- for (int i = 0; i < length; i++) {
- indexMap.put(inorder[i], i);
- }
- TreeNode root = buildTree(preorder, 0, length - 1, inorder, 0, length - 1, indexMap);
- return root;
- }
-
- public TreeNode buildTree(int[] preorder, int preorderStart, int preorderEnd, int[] inorder, int inorderStart, int inorderEnd, Map<Integer, Integer> indexMap) {
- if (preorderStart > preorderEnd) {
- return null;
- }
- int rootVal = preorder[preorderStart];
- TreeNode root = new TreeNode(rootVal);
- if (preorderStart == preorderEnd) {
- return root;
- } else {
- int rootIndex = indexMap.get(rootVal);
- int leftNodes = rootIndex - inorderStart, rightNodes = inorderEnd - rootIndex;
- TreeNode leftSubtree = buildTree(preorder, preorderStart + 1, preorderStart + leftNodes, inorder, inorderStart, rootIndex - 1, indexMap);
- TreeNode rightSubtree = buildTree(preorder, preorderEnd - rightNodes + 1, preorderEnd, inorder, rootIndex + 1, inorderEnd, indexMap);
- root.left = leftSubtree;
- root.right = rightSubtree;
- return root;
- }
- }
- }
题:给定无序数组arr,返回其中最长的连续序列的长度(要求值连续,位置可以不连续,例如 3,4,5,6为连续的自然数)
例:输入:[100,4,200,1,3,2] 输出:4
- import java.util.*;
-
- public class Solution {
- public int MLS (int[] arr) {
- // write code here
- if(arr == null || arr.length == 0) return 0;
- HashSet<Integer> set = new HashSet<>();
- int maxLen = 0;
-
- for(int value : arr){
- set.add(value);
- }
-
- for(int num : set){
- if(!set.contains(num - 1)){
- int currentNum = num;
- int currentLen = 1;
- while(set.contains(currentNum + 1)){
- currentNum += 1;
- currentLen++;
- }
-
- maxLen = Math.max(maxLen, currentLen);
- }
- }
-
- return maxLen;
- }
- }
给定一个整数数组a,同时给定它的大小n和要找的K(K在1到n之间),请返回第K大的数
输入:[1,3,5,2,2],5,3
输出:2
思路:快速排序 + 递归(注意第 k 大是从后往前数,第 k 小是从小往大)
- import java.util.*;
-
- public class Finder {
- public int findKth(int[] a, int n, int K) {
- // write code here
- return findK(a, 0, n - 1, K);
- }
-
- private int findK(int[] arr, int left, int right, int k){
- if(left <= right){
- int temp = quickSort(arr, left, right);
-
- if(temp == arr.length - k){
- return arr[temp];
- }else if(temp > arr.length - k){
- return findK(arr, left, temp - 1, k);
- }else{
- return findK(arr, temp + 1, right, k);
- }
- }
-
- return -1;
- }
-
- private int quickSort(int[] a, int left, int right){
- int key = a[left];
- while(left < right){
- while(left < right && a[right] >= key){
- right--;
- }
-
- a[left] = a[right];
-
- while(left < right && a[left] <= key){
- left++;
- }
-
- a[right] = a[left];
- }
-
- a[left] = key;
-
- return left;
- }
- }
题目:输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。
例子:数组[3,4,5,1,2]为[1,2,3,4,5]的一个旋转,该数组的最小值为1。建议使用O(logn)时间复杂度
思想:二分查找的变种,注意旋转后的边界
- import java.util.ArrayList;
- public class Solution {
- public int minNumberInRotateArray(int [] array) {
- if(array.length == 0 || array == null)
- return 0;
-
- int left = 0;
- int right = array.length - 1;
- int mid = left;
-
- while(array[left] >= array[right]){
- // 二分查找的边界
- if(right - left == 1){
- mid = right;
- break;
- }
-
- mid = (left + right) / 2;
- if(array[mid] >= array[left])
- left = mid;
- else if(array[mid] <= array[right])
- right = mid;
- }
-
- return array[mid];
- }
- }
题目:给定一个m x n大小的矩阵(m行,n列),按螺旋的顺序返回矩阵中的所有元素。
例如:
- [
- [ 1, 2, 3 ],
- [ 4, 5, 6 ],
- [ 7, 8, 9 ]
- ]
返回:[1,2,3,6,9,8,7,4,5]
- import java.util.ArrayList;
- public class Solution {
- public ArrayList<Integer> spiralOrder(int[][] matrix) {
- ArrayList<Integer> res = new ArrayList<Integer>();
-
- if(matrix.length == 0) return res;
-
- int rowBegin = 0;
- int rowEnd = matrix.length - 1;
- int colBegin = 0;
- int colEnd = matrix[0].length - 1;
-
- while(rowBegin <= rowEnd && colBegin <= colEnd){
- for(int j = colBegin; j <= colEnd; j++){
- res.add(matrix[rowBegin][j]);
- }
-
- rowBegin++;
- for(int i = rowBegin; i <= rowEnd; i++){
- res.add(matrix[i][colEnd]);
- }
-
- colEnd--;
-
- if(rowBegin <= rowEnd){
- for(int j = colEnd; j >= colBegin; j--){
- res.add(matrix[rowEnd][j]);
- }
- }
-
- rowEnd--;
-
- if(colBegin <= colEnd){
- for(int i = rowEnd; i >= rowBegin; i--){
- res.add(matrix[i][colBegin]);
- }
- }
- colBegin++;
- }
- return res;
- }
- }
题目:给定一个数组arr,返回arr的最长无的重复子串的长度(无重复指的是所有数字都不相同)。
解法:使用hashmap来保存重复字符的位置。
- import java.util.*;
-
-
- public class Solution {
- public int maxLength (int[] arr) {
- // write code here
- HashMap<Integer, Integer> map = new HashMap<>();
- int max = 1;
- for(int start = 0, end = 0; end < arr.length; end++){
- if(map.containsKey(arr[end])){
- start = Math.max(start, map.get(arr[end]) + 1);
- }
- max = Math.max(max, end - start + 1);
- map.put(arr[end], end);
- }
-
- return max;
- }
- }
- public class Solution {
- /**
- *
- * @param head ListNode类
- * @param k int整型
- * @return ListNode类
- */
- public ListNode reverseKGroup (ListNode head, int k) {
- // write code here
-
- if(head == null || head.next == null || k < 2)
- return head;
-
- ListNode res = new ListNode(-1);
- res.next = head;
- ListNode pre = res;
- ListNode end = res;
-
- while(end != null){
- for(int i = 0; i < k && end != null; i++){
- end = end.next;
- }
-
- if(end == null) break;
-
- ListNode start = pre.next;
- ListNode next = end.next;
- end.next = null;
- pre.next = reverseList(start);
- start.next = next;
- pre = start;
- end = pre;
- }
-
- return res.next;
- }
-
- private ListNode reverseList(ListNode head){
- ListNode preNode = null, pNode = head;
- while(pNode != null){
- ListNode pNext = pNode.next;
- pNode.next = preNode;
- preNode = pNode;
- pNode = pNext;
- }
-
- return preNode;
- }
- }
题目:给定一个排序链表,删除所有含有重复数字的节点,只保留原始链表中 没有重复出现 的数字。
给出的链表为1→2→3→3→4→4→5, 返回 1→2→5.
给出的链表为1→1→1→2→3, 返回 2→3.
- import java.util.*;
-
- public class Solution {
- public ListNode deleteDuplicates (ListNode head) {
- // write code here
- if(head == null || head.next == null)
- return head;
-
- ListNode res = new ListNode(-1);
- res.next = head;
- ListNode preNode = res;
- ListNode pNode = head;
-
- while(pNode != null && pNode.next != null){
- if(preNode.next.val != pNode.next.val){
- preNode = preNode.next;
- pNode = pNode.next;
- }else{
- while(pNode != null && pNode.next != null && preNode.next.val == pNode.next.val){
- pNode = pNode.next;
- }
-
- preNode.next = pNode.next;
- pNode = pNode.next;
- }
- }
-
- return res.next;
-
- }
- }
给定两个这种链表,请生成代表两个整数相加值的结果链表。
例如:链表 1 为 9->3->7,链表 2 为 6->3,最后生成新的结果链表为 1->0->0->0。
思路:先反转,再相加,注意进位以及最后一次相加(String类型的大数加法思路一致)
- import java.util.*;
-
-
- public class Solution {
- public ListNode addInList (ListNode head1, ListNode head2) {
- // write code here
- if(head1 == null) return head2;
- if(head2 == null) return head1;
-
- ListNode l1 = reverseList(head1);
- ListNode l2 = reverseList(head2);
- int carry = 0;
- ListNode res = new ListNode(-1);
- ListNode curNode = res;
-
- while(l1 != null || l2 != null){
- int num1 = l1 == null ? 0 : l1.val;
- int num2 = l2 == null ? 0 : l2.val;
-
- int val = (num1 + num2 + carry) % 10;
- carry = (num1 + num2 + carry) / 10;
-
- curNode.next = new ListNode(val);
- if(l1 != null)
- l1 = l1.next;
- if(l2 != null)
- l2 = l2.next;
-
- curNode = curNode.next;
- }
-
- return reverseList(res.next);
- }
-
- public ListNode reverseList(ListNode head){
- ListNode preNode = null;
- ListNode curNode = head;
-
- while(curNode != null){
- ListNode nextNode = curNode.next;
- curNode.next = preNode;
- preNode = curNode;
- curNode = nextNode;
- }
-
- return preNode;
- }
- }
题目:合并 k 个已排序的链表并将其作为一个已排序的链表返回。
思路:归并排序法 + 两个链表合并法
- class Solution {
- public ListNode mergeKLists(ListNode[] lists) {
- if(lists == null || lists.length == 0) return null;
-
- return merge(lists, 0, lists.length - 1);
- }
-
- private ListNode merge(ListNode[] lists, int left, int right){
- if(left == right) return lists[left];
-
- int mid = left + (right - left) / 2;
- ListNode l1 = merge(lists, left, mid);
- ListNode l2 = merge(lists, mid + 1, right);
- return mergeTwoLists(l1, l2);
- }
-
- private ListNode mergeTwoLists(ListNode l1, ListNode l2){
- ListNode res = new ListNode(-1);
- ListNode pNode = res;
-
- while(l1 != null && l2 != null){
- if(l1.val <= l2.val){
- pNode.next = l1;
- l1 = l1.next;
- }else{
- pNode.next = l2;
- l2 = l2.next;
- }
- pNode = pNode.next;
- }
-
- pNode.next = l1 == null ? l2 : l1;
- return res.next;
- }
- }
- class LRUCache {
- class Node {
- public int key, val;
- public Node prev, next;
- public Node(int key, int val){
- this.key = key;
- this.val = val;
- }
- }
-
- class DoubleList{
- private Node head, tail;
- private int size;
-
- public DoubleList(){
- head = new Node(0, 0);
- tail = new Node(0, 0);
- head.next = tail;
- tail.prev = head;
- size = 0;
- }
-
- public void addFirst(Node x){
- x.next = head.next;
- x.prev = head;
- head.next.prev = x;
- head.next = x;
- size++;
- }
-
- public void remove(Node x){
- x.prev.next = x.next;
- x.next.prev = x.prev;
- size--;
- }
-
- public Node removeLast(){
- if(tail.prev == head){
- return null;
- }
- Node last = tail.prev;
- remove(last);
- return last;
- }
-
- public int size(){
- return this.size;
- }
-
- }
-
- public HashMap<Integer, Node> map;
- private DoubleList cache;
- private int cap;
-
- public LRUCache(int capacity) {
- map = new HashMap<>();
- cache = new DoubleList();
- this.cap = capacity;
- }
-
- public int get(int key) {
- if(!map.containsKey(key)){
- return -1;
- }
-
- int val = map.get(key).val;
- put(key, val);
-
- return val;
- }
-
- public void put(int key, int value) {
- Node x = new Node(key, value);
-
- if(map.containsKey(key)){
- cache.remove(map.get(key));
- cache.addFirst(x);
- map.put(key, x);
- }else{
- if(cache.size == cap){
- Node last = cache.removeLast();
- map.remove(last.key);
- }
-
- cache.addFirst(x);
- map.put(key, x);
- }
- }
- }
定理:已知 rand_N() 可以等概率的生成[1, N]范围的随机数。那么:
(rand_X() - 1) × Y + rand_Y() ==> 可以等概率的生成[1, X * Y]范围的随机数,即实现了 rand_XY()
例:(rand9() - 1) × 7 + rand7() = rand63();
- class Solution extends SolBase {
- public int rand10() {
- while(true) {
- int a = rand7();
- int b = rand7();
- int num = (a-1)*7 + b; // rand 49
- if(num <= 40) return num % 10 + 1; // 拒绝采样
-
- a = num - 40; // rand 9
- b = rand7();
- num = (a-1)*7 + b; // rand 63
- if(num <= 60) return num % 10 + 1;
-
- a = num - 60; // rand 3
- b = rand7();
- num = (a-1)*7 + b; // rand 21
- if(num <= 20) return num % 10 + 1;
- }
- }
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。