1-10
215. 数组中的第K个最大元素
思路:使用堆,建立一个K个元素的小顶堆,当元素比堆顶元素大时,入堆,否则不入堆。最后弹出堆顶元素即为第K个最大的元素。
import java.util.PriorityQueue;
public class Solution {
public int findKthLargest(int[] nums, int k) {
int len = nums.length;
// 使用一个含有 k 个元素的最小堆
PriorityQueue<Integer> minHeap = new PriorityQueue<>(k, (a, b) -> a - b);
for (int i = 0; i < k; i++) {
minHeap.add(nums[i]);
}
for (int i = k; i < len; i++) {
// 看一眼,不拿出,因为有可能没有必要替换
Integer topEle = minHeap.peek();
// 只要当前遍历的元素比堆顶元素大,堆顶弹出,遍历的元素进去
if (nums[i] > topEle) {
minHeap.poll();
minHeap.add(nums[i]);
}
}
return minHeap.peek();
}
}
206. 反转链表(https://www.cnblogs.com/mwl523/p/10749144.html)
思路1: 新建链表,头节点插入
具体操作:新建一个链表dummy,当前指针pCur指向head,每次都头插入新链表
关键:1个头结点,2个指针(包含一个临时保存节点的pNex),4行代码
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
ListNode dummy = new ListNode(-1);
ListNode pCur = head;
ListNode pNext = null;
while(pCur != null) {
pNext = pCur.next;
pCur.next = dummy.next;
dummy.next = pCur;
pCur = pNext;
}
return dummy.next;
}
}
思路二:使用递归
ListNode reverse(ListNode head) {
if (head.next == null) return head;
ListNode last = reverse(head.next);
head.next.next = head;
head.next = null;
return last;
}
3. 无重复字符的最长子串
思路:滑动窗口,一个left和一个right,用一个hashmap保存出现的字符及其下标,每次增加right,判断是否存在重复,从而跟新left,每次计算最大子串长度。
class Solution {
public int lengthOfLongestSubstring(String s) {
if(s==null || s.length() == 0) {
return 0;
}
int len = s.length();
int res = 0;
int left = 0, right = 0;
HashMap<Character,Integer> map = new HashMap<>();
for( ; right<len; right++) {
if(map.containsKey(s.charAt(right))){
left = Math.max(left, map.get(s.charAt(right)) + 1);
}
map.put(s.charAt(right), right);
res = Math.max(res, right - left + 1);
}
return res;
}
}
25. K 个一组翻转链表
思路:把链表结点按照k个一组分组(可使用一个指针head依次指向每组的头结点)对于每个分组,先判断它的长度是否大于等于k,若是,翻转这部分链表,否则不需要翻转。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public static ListNode reverseKGroup(ListNode head, int k) {
if(head == null || head.next == null) {
return head;
}
ListNode dummy = new ListNode(0);
dummy.next = head;
ListNode pre = dummy, end = dummy;
while (end.next != null) {
for(int i=0; i<k&&end != null; i++) {
end = end.next;
}
if(end == null) {
break;
}
ListNode next = end.next;
end.next = null;
ListNode start = pre.next;
pre.next = reverse(start);
start.next = next;
pre = start;
end = start;
}
return dummy.next;
}
public static ListNode reverse(ListNode head) {
if (head == null || head.next == null) {
return head;
}
ListNode pre = null;
ListNode cur = head;
ListNode nextNode = null;
while(cur != null) {
nextNode = cur.next;
cur.next = pre;
pre = cur;
cur = nextNode;
}
return pre;
}
}
15. 三数之和
思路:排序+双指针
首先对数组进行由小到大的排序,然后依次遍历数组(重复元素跳过),选定一个之后,以其右边第一个元素和最右边元素为left和right,双指针法求二数之和问题。其中关键是如何去除重复,对于重复元素跳过。
class Solution {
List<List<Integer>> res = new ArrayList<>();
public List<List<Integer>> threeSum(int[] nums) {
int n = nums.length-1;
if(n<2){
return res;
}
Arrays.sort(nums);
for(int i=0;i<=n;i++){
if(n-i<2){
break;
}
if(i>0 && nums[i] == nums[i-1]){
continue;
}
int l= i+1;
int r = n;
int target = -nums[i];
while(l<r){
if(nums[l]+nums[r] == target){
List<Integer> list = new ArrayList<>();
list.add(nums[i]);
list.add(nums[l]);
list.add(nums[r]);
res.add(list);
l++;
r--;
} else if(nums[l]+nums[r] < target) {
l++;
} else{
r--;
}
}
}
return res;
}
}
146. LRU 缓存机制
最近最少使用算法,有两个接口get和put
思路:使用hashMap和Deque(LinkedList)实现LRU,还要设置一个LRU的最大容量,LRU的构造函数传入最大容量的值,并初始化map和linkedlist。对于get(int key):先从hashMap中获取key的值,判断是否存在,存在则在list中删除该值并在最前面插入该值,否则输出-1。
对于put(int key, int value):首先判断hashMap中是否含有该key,如果含有,则删除list中的该key并在list最前面插入,同时重新往hashMap中插入该key和value并返回。如果不含有,则判断是否超过最大容量,如果超过,则删除list中最后的结点,并在hashMap中删除该结点及其对应的value,然后往hashMap中插入key和value,list中插入key。
class LRUCache {
private Map<Integer, Integer> cache;
private Deque<Integer> list;
private int capacity;
public LRUCache(int capacity) {
this.capacity = capacity;
cache = new HashMap<>();
list = new LinkedList<>();
}
public int get(int key) {
Integer item = cache.get(key);
if (item == null) {
return -1;
}
changeList(key);
return item;
}
public void put(int key, int value) {
if (cache.containsKey(key)) {
changeList(key);
cache.put(key, value);
return;
}
if (list.size() == capacity) {
Integer last = list.removeLast();
cache.remove(last);
}
cache.put(key, value);
list.offerFirst(key);
}
private void changeList(int key) {
list.remove(key);
list.offerFirst(key);
}
}
/**
* Your LRUCache object will be instantiated and called as such:
* LRUCache obj = new LRUCache(capacity);
* int param_1 = obj.get(key);
* obj.put(key,value);
*/
1. 两数之和
思路:使用哈希表存储数字及其下标,每次查找hash表中是否有(target-nums[i])
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> hashtable = new HashMap<Integer, Integer>();
for (int i = 0; i < nums.length; ++i) {
if (hashtable.containsKey(target - nums[i])) {
return new int[]{hashtable.get(target - nums[i]), i};
}
hashtable.put(nums[i], i);
}
return new int[0];
}
}
121. 买卖股票的最佳时机
思路:双指针或单调栈
双指针:使用两个指针,一个指针记录访问过的最小值,一个指针一直往后走,然后计算他们的差值,保存最大的即可。
单调栈:(始终保持栈顶元素是所有访问过的元素中最小的)如果当前元素小于栈顶元素,就让栈顶元素出栈,让当前元素入栈。如果访问的元素大于栈顶元素,就要计算他和栈顶元素的差值,我们记录最大的即可。
//双指针
public static int maxProfit(int[] prices) {
if (prices == null || prices.length == 0)
return 0;
int maxPro = 0;//记录最大利润
int min = prices[0];//记录数组中访问过的最小值
for (int i = 1; i < prices.length; i++) {
min = Math.min(min, prices[i]);
maxPro = Math.max(prices[i] - min, maxPro);
}
return maxPro;
}
//单调栈
public int maxProfit(int[] prices) {
if (prices == null || prices.length == 0)
return 0;
Stack<Integer> stack = new Stack<>();
stack.push(prices[0]);
int max = 0;
for (int i = 1; i < prices.length; i++) {
//如果栈顶元素大于prices[i],那么栈顶元素出栈,
//把prices[i]压栈,要始终保证栈顶元素是最小的
if (stack.peek() > prices[i]) {
stack.pop();
stack.push(prices[i]);
} else {
//否则如果栈顶元素不大于prices[i],就要计算
//prices[i]和栈顶元素的差值
max = Math.max(max, prices[i] - stack.peek());
}
}
return max;
}
142. 环形链表 II
思路:使用快慢指针
快指针的路程是满指针路程的两倍,当第一次相遇后,新建一个指针从起点出发,慢指针和新指针同时走,相遇的地方就是环形链表的入口,公式推导,画图一试就知道了,2*(a + b)= a + b + a + c
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode detectCycle(ListNode head) {
if(head == null) {
return null;
}
ListNode slow = head, fast = head;
while(fast != null) {
slow = slow.next;
if(fast.next != null) {
fast = fast.next.next;
} else {
return null;
}
if(fast == slow) {
ListNode pre = head;
while(pre != slow) {
pre = pre.next;
slow = slow.next;
}
return pre;
}
}
return null;
}
}
94. 二叉树的中序遍历
思路:使用栈,双while
首先,特判,然后杀入到左子树最左结点(使用while循环),去判断最左结点是否为空,如果左结点为空,则当前结点入列表,然后将当前结点赋为其right结点,重复while循环。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
if(root == null) {
return res;
}
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root;
while(cur != null || !stack.isEmpty()) {
while(cur != null) {
stack.push(cur);
cur = cur.left;
}
TreeNode tmp = stack.pop();
res.add(tmp.val);
cur = tmp.right;
}
return res;
}
}
10-20
102. 二叉树的层序遍历
思路:使用队列,每层入队,取队的size,依次遍历,直到队列为空
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> res = new ArrayList<>();
if(root == null){
return res;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while(!queue.isEmpty()){
int size = queue.size();
List<Integer> subr = new ArrayList<>();
for(int i=0;i<size;i++){
TreeNode cur = queue.poll();
subr.add(cur.val);
if(cur.left != null){
queue.offer(cur.left);
}
if(cur.right != null){
queue.offer(cur.right);
}
}
res.add(subr);
}
return res;
}
}
103. 二叉树的锯齿形层序遍历
思路:也是使用队列,遍历每层,每次判断是奇数层还是偶数层,奇数层直接存入列表,偶数层逆转后存入列表
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
List<List<Integer>> res = new ArrayList<>();
if(root == null) {
return res;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.add(root);
int level = 1;
while(!queue.isEmpty()) {
int size = queue.size();
List<Integer> levelList = new ArrayList<>();
for(int i=0; i<size; i++) {
TreeNode cur = queue.poll();
levelList.add(cur.val);
if(cur.left != null) {
queue.add(cur.left);
}
if(cur.right != null) {
queue.add(cur.right);
}
}
if(level % 2 == 0) {
Collections.reverse(levelList);
}
res.add(levelList);
level++;
}
return res;
}
}
236. 二叉树的最近公共祖先
思路:递归
lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q)
若root为p,q的最近公共祖先,则有三种情况:
1、p和q在root的子树中,且分列root异侧(分别在左右子树)
2、p=root且q在root的子树中
3、q=root且p在root的子树中
考虑通过递归对二叉树进行后序遍历,当遇到结点p或q时返回。从底至顶回溯,当结点p,q在结点root的异侧时,结点root即为最近公共祖先,则向上返回root。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root == null || root == p || root == q) return root;
TreeNode left = lowestCommonAncestor(root.left, p, q);
TreeNode right = lowestCommonAncestor(root.right, p, q);
if(left == null && right == null)
return null;
if(left == null) {
return right;
}
if(right == null) {
return left;
}
return root;
}
}
21. 合并两个有序链表
思路: 用迭代的方法实现,当l1和l2都不是空链表时,判断l1和l2哪一个链表的头结点的值更小,将较小值的结点添加到结里,当一个结点被添加到结果里面之后,将对应链表中的结点向后移一位。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode dummyHead = new ListNode(-1);
ListNode pre = dummyHead;
while(l1 != null && l2 != null) {
if(l1.val < l2.val) {
pre.next = l1;
l1 = l1.next;
} else {
pre.next = l2;
l2 = l2.next;
}
pre = pre.next;
}
pre.next = l1 == null ? l2 : l1;
return dummyHead.next;
}
}
415. 字符串相加
思路:模拟加法的过程
定义两个指针i和j分别指向num1和num2的末尾,即最低位,同时定义一个变量add维护当前是否有进位,然后从末尾开始
逐位相加即可。
class Solution {
public String addStrings(String num1, String num2) {
int i = num1.length()-1, j = num2.length()-1;
int add = 0;
StringBuffer ans = new StringBuffer();
while(i >=0 || j >= 0 || add>0) {
int x = i<0 ? 0 : num1.charAt(i) - '0';
int y = j<0 ? 0 : num2.charAt(j) - '0';
ans.append((x + y + add) % 10);
add = (x + y + add) /10;
i --;
j --;
}
return ans.reverse().toString();
}
}
53. 最大子序和
思路1:使用前缀和数值
class Solution {
public int maxSubArray(int[] nums) {
int len = nums.length;
int[] sumNums = new int[len];
int sum = 0;
for(int i=0; i<len; i++) {
sum += nums[i];
sumNums[i] = sum;
}
int minSum = 0;
int maxSum = nums[0];
for(int i=0; i<len; i++) {
maxSum = Math.max(maxSum, sumNums[i] - minSum);
minSum = Math.min(minSum, sumNums[i]);
}
return maxSum ;
}
}
思路2:动态规划
设f(i)代表以第i个数结尾的连续子树组的最大和。则f(i) = max{f(i-1) + nums[i], nums[i]}
class Solution {
public int maxSubArray(int[] nums) {
int pre = 0, maxAns = nums[0];
for (int x : nums) {
pre = Math.max(pre + x, x);
maxAns = Math.max(maxAns, pre);
}
return maxAns;
}
}
199. 二叉树的右视图
思路:使用队列,每层只存最右边那个结点的值
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<Integer> rightSideView(TreeNode root) {
List<Integer> res = new ArrayList<>();
if(root == null) {
return res;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while(!queue.isEmpty()) {
int size = queue.size();
for(int i=0; i<size; i++) {
TreeNode cur = queue.poll();
if(i == 0) {
res.add(cur.val);
}
if(cur.right != null) {
queue.offer(cur.right);
}
if(cur.left != null) {
queue.offer(cur.left);
}
}
}
return res;
}
}
160. 相交链表
思路:双指针法
两个指针,pA和pB分别初始化为链表A和B的头结点,依次遍历,当pA到达A的尾部时,重新指向B的头结点,当pB到达B的尾部时,重新指向A的头结点。pA和pB相遇的结点即为相交结点。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if (headA == null || headB == null) return null;
ListNode pA = headA, pB = headB;
while(pA != pB) {
pA = pA == null ? headB : pA.next;
pB = pB == null ? headA : pB.next;
}
return pA;
}
}
20. 有效的括号
思路:使用栈
class Solution {
public boolean isValid(String s) {
Stack<Character> stack = new Stack<>();
for(Character c : s.toCharArray()) {
if(stack.isEmpty() && (c == ')' || c == ']' || c== '}')){
stack.push(c);
break;
}
if(c == '(' || c == '[' || c== '{') {
stack.push(c);
} else {
Character tmp = stack.peek();
if(c == ')' && tmp == '(') {
stack.pop();
continue;
}
if(c == ']' && tmp == '[') {
stack.pop();
continue;
}
if(c == '}' && tmp == '{'){
stack.pop();
continue;
}
stack.push(c);
}
}
return stack.isEmpty();
}
}
200. 岛屿数量
https://leetcode-cn.com/problems/number-of-islands/solution/dao-yu-lei-wen-ti-de-tong-yong-jie-fa-dfs-bian-li-/
思路:依次遍历矩阵各个点,如果为1,则结果加1,并且使用dfs将该点和上下左右的点都递归赋值为0,遍历完即得到联通分量的数目.
class Solution {
int res = 0;
int m;
int n;
int[][] dic = {{0,1},{1,0},{0,-1},{-1,0}};
public int numIslands(char[][] grid) {
m = grid.length;
n=grid[0].length;
for(int i=0; i<m; i++) {
for(int j=0; j<n; j++) {
if(grid[i][j] != '0') {
res++;
dfs(grid, i, j);
}
}
}
return res;
}
private void dfs(char[][] grid, int i, int j){
if(i<0 || i>= m || j < 0 || j>= n || grid[i][j] == '0') return;
grid[i][j] = '0';
for(int[] d : dic) {
dfs(grid, i + d[0], j + d[1]);
}
}
}
20-30
104. 二叉树的最大深度
思路:对于二叉树,首先想到的应该是递归,递归左右子树计算高度
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public int maxDepth(TreeNode root) {
if(root == null) return 0;
return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
}
}
54. 螺旋矩阵
思路:按层模拟,一圈一圈的往内遍历,限度left,right,top和buttom,然后依次遍历
1、从左向右遍历上侧元素,依次为(top,left)到(top,right)。
2、从上到下遍历右侧元素,依次为(top+1, left)到(buttom,right)。
3、如果left < right 且 top < buttom, 则从右到左遍历下侧元素,依次为(buttom, right-1)到(buttom, left + 1),以及以下到上遍历左侧元素,依次为(buttom,left)到(top+1, left)。
4、遍历一层后,left,top分别加1,right 和bottom分别减1
1到4在一个while循环中完成。
class Solution {
public List<Integer> spiralOrder(int[][] matrix) {
List<Integer> order = new ArrayList<Integer>();
if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
return order;
}
int rows = matrix.length, columns = matrix[0].length;
int left = 0, right = columns - 1, top = 0, bottom = rows - 1;
while (left <= right && top <= bottom) {
for (int column = left; column <= right; column++) {
order.add(matrix[top][column]);
}
for (int row = top + 1; row <= bottom; row++) {
order.add(matrix[row][right]);
}
if (left < right && top < bottom) {
for (int column = right - 1; column > left; column--) {
order.add(matrix[bottom][column]);
}
for (int row = bottom; row > top; row--) {
order.add(matrix[row][left]);
}
}
left++;
right--;
top++;
bottom--;
}
return order;
}
}
92. 反转链表 II
思路:使用双指针和头插法
1、首先新建一个链表dummy,两个指针pre和cur,移动指针使pre指向要反转部分的前一个结点,cur指向反转部分开头(这好办,将dummy.next指向head,pre指向dummy,cur指向dummy.next,两指针走left步即可)
2、然后使用头插法(将cur后面的元素删除,然后插入pre的后面),将接下来right-left个结点反转(定义一个pNext结点,记录当前结点cur的下一个位置,然后将cur指向他的下下一个结点,pNext指向pre的next结点,然后pre的next结点改为pNext)
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode reverseBetween(ListNode head, int left, int right) {
ListNode dummy = new ListNode(-1);
dummy.next = head;
ListNode pre = dummy;
ListNode cur = dummy.next;
int step = 0;
while(step < left-1) {
pre = pre.next;
cur = cur.next;
step ++;
}
for(int i=0; i<right-left; i++) {
ListNode pNext = cur.next;
cur.next = cur.next.next;
pNext.next = pre.next;
pre.next = pNext;
}
return dummy.next;
}
}
88. 合并两个有序数组
思路:双指针,从大到小比较,大的放在nums1的最右端
class Solution {
public void merge(int[] nums1, int m, int[] nums2, int n) {
int i = m-1, j = n-1;
while(i >=0 && j >= 0) {
if(nums1[i] > nums2[j]) {
nums1[i+j+1] = nums1[i];
i--;
} else {
nums1[i+j+1] = nums2[j];
j--;
}
}
while(j >= 0) {
nums1[j] = nums2[j];
j--;
}
}
}
69. x 的平方根
思路:使用二分查找,比较mid和x/mid的大小(注意,这里mid为int类型),基本二分查找的正常实现
class Solution {
public int mySqrt(int x) {
if(x <= 1) return x;
int l = 1, h = x;
while(l <= h) {
int mid = l + (h-l)/2;
int sqrt = x / mid;
if(mid == sqrt) {
return mid;
} else if (mid >= sqrt) {
h = mid -1;
} else {
l = mid + 1;
}
}
return h;
}
}
判断循环条件是l<h还是l<=h,举例子判断下是否有死循环即可
最后输出h而不是l,也用特例判断
141. 环形链表
思路:使用快慢指针
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public boolean hasCycle(ListNode head) {
if(head == null || head.next==null){
return false;
}
ListNode fast = head.next;
ListNode slow = head;
while(fast != slow){
if(fast.next == null || fast.next.next == null) return false;
fast = fast.next.next;
slow = slow.next;
}
return true;
}
}
543. 二叉树的直径
思路:看见二叉树首先想到递归
分析可知二叉树的直径为最大的左子树深度+右子树深度。
用该函数无法递归时,考虑到改成求深度的函数进行递归,返回深度,用一个全局变量存储最大直径
res = Math.max(res, leftDepth + rightDepth)
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
int res = 0;
public int diameterOfBinaryTree(TreeNode root) {
if(root == null) return 0;
depth(root);
return res;
}
private int depth(TreeNode root) {
if(root == null) {
return 0;
}
int leftD = depth(root.left);
int rightD = depth(root.right);
res = Math.max(res, leftD + rightD);
return Math.max(leftD, rightD) + 1;
}
}
110. 平衡二叉树
思路:和求543二叉树的直径这里异曲同工,都是考虑递归
平衡二叉树与左右子树的深度有关并且当前函数是否为平衡二叉树,因此递归求深度的函数,使用一个全局变量客,在递归过程中判断是否为平衡二叉树。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
boolean res = true;
public boolean isBalanced(TreeNode root) {
depth(root);
return res;
}
private int depth(TreeNode root) {
if(root == null) return 0;
int leftD = depth(root.left);
int rightD = depth(root.right);
boolean tmp = (Math.abs(leftD - rightD) <= 1);
res &= tmp;
return Math.max(leftD, rightD) + 1;
}
}
105. 从前序与中序遍历序列构造二叉树
思路:
前序:[根,左,右]
中序:[左,根,右]
根据前序中的根从中序中找出根的下标,然后可以得到左子树和右子树的个数,通过根构建root,然后递归得到root.left 和root.right
二叉树,首先想到递归。
原函数无法/不方便递归,则新建一个类似得递归函数,可以仅参数不同。
数组通过下标就可获取全部信息。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
Map<Integer, Integer> map = new HashMap<>();
public TreeNode buildTree(int[] preorder, int[] inorder) {
for(int i=0; i<inorder.length; i++) {
map.put(inorder[i], i);
}
int len = preorder.length - 1;
return buildTree(preorder, 0, len, inorder, 0, len);
}
private TreeNode buildTree(int[] preorder, int preLeft, int preRight, int[] inorder, int inLeft, int inRight) {
if(preLeft > preRight || inLeft > inRight) {
return null;
}
TreeNode root = new TreeNode(preorder[preLeft]);
int index = map.get(preorder[preLeft]);
int inLeftLen = index - inLeft;
int inRightLen = inRight - index;
root.left = buildTree(preorder, preLeft+1, preLeft + inLeftLen, inorder, inLeft, index -1);
root.right = buildTree(preorder, preLeft + inLeftLen + 1, preRight, inorder, index + 1, inRight);
return root;
}
}
113. 路径总和 II
思路:使用递归+先序遍历 (即回塑)
递归过程中保持符合条件的结果
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
ArrayList<List<Integer>> results = new ArrayList<>();
public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
List<Integer> res = new ArrayList<>();
pathSum(root, targetSum, res);
return results;
}
private void pathSum(TreeNode root, int target, List<Integer> res) {
if(root == null) {
return;
}
res.add(root.val);
if(target == root.val && root.left == null && root.right == null) {
results.add(new ArrayList<>(res));
} else {
pathSum(root.left, target - root.val, res);
pathSum(root.right, target - root.val, res);
}
res.remove(res.size()-1);
}
}
30-40
42. 接雨水
方法一:用两个数组记录左边的最大值和右边的最大值,两者中最小的减去当前高度即为第i个位置能接的雨水量。
class Solution {
public int trap(int[] height) {
int len = height.length;
int[] maxLeft = new int[len];
int[] maxRight = new int[len];
int maxL = 0, maxR = 0;
for(int i=0; i<len; i++) {
maxL = Math.max(maxL, height[i]);
maxR = Math.max(maxR, height[len - i - 1]);
maxLeft[i] = maxL;
maxRight[len - i -1] = maxR;
}
int res = 0;
for(int i=0; i<len; i++) {
res += (Math.min(maxLeft[i], maxRight[i]) - height[i]);
}
return res;
}
}
方法二:
使用单调栈(单调递减栈)-栈中元素单调递减,当待插入元素大于栈顶元素时候,用while操作出栈,我们的计算面积操作也是在这里进行的
单调栈中存放的是下标,但是我们计算和比较都是用的下标对应的高度
当待插入元素大于栈顶元素时,取出栈顶元素,求凹进去的面积
class Solution {
public int trap(int[] height) {
Stack<Integer> stack = new Stack<>();
int cur = 0, len = height.length;
int sum = 0;
while(cur < len) {
while(!stack.isEmpty() && height[stack.peek()] < height[cur]) {
int tmp = stack.pop();
if(stack.isEmpty()) {
break;
}
int dis = cur - stack.peek() - 1;
int h = Math.min(height[stack.peek()], height[cur]) - height[tmp];
sum += dis * h;
}
stack.push(cur++);
}
return sum;
}
}
124. 二叉树中的最大路径和
思路:使用递归,求根到左子树和右子树的最大长度,在递归过程中求出最大路径和
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
int max = Integer.MIN_VALUE;
public int maxPathSum(TreeNode root) {
maxGain(root);
return max;
}
public int maxGain(TreeNode root) {
if(root == null) return 0;
int leftG = Math.max(maxGain(root.left), 0);
int rightG = Math.max(maxGain(root.right), 0);
max = Math.max(leftG + rightG + root.val, max);
return root.val + Math.max(leftG, rightG);
}
}
23. 合并K个升序链表
思路:使用合并2个胜序链表的思想,每次合并两个,遍历一次即得到最终结果
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
ListNode dummy = null;
int size = lists.length;
for(int i=0; i<size; i++) {
dummy = margeTwoLists(dummy, lists[i]);
}
return dummy;
}
private ListNode margeTwoLists(ListNode list1, ListNode list2) {
if(list1 == null) return list2;
if(list2 == null) return list1;
ListNode head = new ListNode(-1);
ListNode pre = head, pList1 = list1, pList2 = list2;
while(pList1 != null && pList2 != null) {
if(pList1.val <= pList2.val) {
pre.next = pList1;
pList1 = pList1.next;
} else {
pre.next = pList2;
pList2 = pList2.next;
}
pre = pre.next;
}
if(pList1 != null) {
pre.next = pList1;
}
if(pList2 != null) {
pre.next = pList2;
}
return head.next;
}
}
33. 搜索旋转排序数组
思路:二分法,将其划分为两份
中间元素把区间划分为两部分,这两部分一定有一部分是有序的
每次对有序那一部分进行操作
class Solution {
public int search(int[] nums, int target) {
int left = 0, right = nums.length -1;
while(left <= right) {
int mid = left + (right - left) / 2;
if(nums[mid] == target) {
return mid;
}
if(nums[mid] >= nums[left]) {
if(target >= nums[left] && target < nums[mid]) {
right = mid -1;
} else {
left = mid + 1;
}
} else {
if(target <= nums[right] && target > nums[mid]) {
left = mid + 1;
} else {
right = mid - 1;
}
}
}
return -1;
}
}
46. 全排列
思路:dfs + 回溯
class Solution {
public List<List<Integer>> permute(int[] nums) {
int len = nums.length;
List<List<Integer>> res = new ArrayList<>();
if(len == 0) return res;
Deque<Integer> path = new LinkedList<>();
boolean[] used = new boolean[len];
dfs(nums, 0, len, path, used, res);
return res;
}
private void dfs(int[] nums, int level, int len, Deque<Integer> path, boolean[] used,List<List<Integer>> res) {
if(level == len) {
res.add(new ArrayList<>(path));
return;
}
for(int i=0; i<len; i++) {
if(used[i]) {
continue;
}
path.addFirst(nums[i]);
used[i] = true;
dfs(nums, level+1, len, path, used, res);
path.removeFirst();
used[i] = false;
}
}
}
354. 俄罗斯套娃信封问题
思路:信封能够嵌套的条件是信封的长和宽必须都比这个信封大,长和宽两个量不好控制,那我们转化为固定其中一个量,求另一个量,
如何固定?利用排序,将其中一个维度(长)升序,长相等则根据第二个维度降序,这样就只考虑第二个维度(宽)了
即求第二个维度的最长递增子序列
需要注意的是,第一个维度相等的情况下该怎么处理。
class Solution {
public int maxEnvelopes(int[][] envelopes) {
if(envelopes == null || envelopes.length == 0) {
return 0;
}
Arrays.sort(envelopes, new Comparator<int[]>(){
public int compare(int[] nums1, int[] nums2) {
if(nums1[0] == nums2[0]){
return nums2[1] - nums1[1];
} else {
return nums1[0] - nums2[0];
}
}
});
return getLongestSeq(envelopes);
}
private int getLongestSeq(int[][] envelopes) {
int len = envelopes.length;
int[] dp = new int[len];
dp[0] = 1;
for(int i=1; i<len; i++) {
dp[i] = 1;
for(int j=0; j<i; j++) {
if(envelopes[i][1] > envelopes[j][1]) {
dp[i] = Math.max(dp[i], dp[j]+1);
}
}
}
int res = 0;
for(int i=0; i<len; i++) {
res = Math.max(dp[i], res);
}
return res;
}
}
144. 二叉树的前序遍历
思路1:二叉树,递归,很好实现
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
List<Integer> res = new ArrayList<>();
public List<Integer> preorderTraversal(TreeNode root) {
preorderTraversal1(root);
return res;
}
public void preorderTraversal1(TreeNode root) {
if(root == null) {
return;
}
res.add(root.val);
preorderTraversal1(root.left);
preorderTraversal1(root.right);
}
}
思路2:非递归方式
使用栈
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
if(root == null) return res;
Deque<TreeNode> stack = new LinkedList<>();
stack.addFirst(root);
while(!stack.isEmpty()) {
TreeNode cur = stack.removeFirst();
res.add(cur.val);
if(cur.right != null) {
stack.addFirst(cur.right);
}
if(cur.left != null) {
stack.addFirst(cur.left);
}
}
return res;
}
}
5. 最长回文子串
思路:动态规划,
确定状态 dp[i][j] = 字符串s从i到j是是否为回文串
转移方程:dp[i][j] = (s[i] == s[j]) && dp[i+1][j-1]
边界和初始:dp[i][i] = true 记录最大长度及起始下标
顺序:j从1到len-1, i从0 到 j-1
注意:长度要把握准,i-j还是i-j+1,写的时候仔细思考
class Solution {
public String longestPalindrome(String s) {
int len = s.length();
if(len < 2) {
return s;
}
int maxLen = 1;
int begin = 0;
boolean[][] dp = new boolean[len][len];
char[] cs = s.toCharArray();
for(int i=0; i<len; i++) {
dp[i][i] = true;
}
for(int j=1 ; j<len; j++) {
for(int i=0; i<j; i++) {
if(cs[i] != cs[j]) {
dp[i][j] = false;
} else {
if(j - i < 3) {
dp[i][j] = true;
} else {
dp[i][j] = dp[i+1][j-1];
}
}
if(dp[i][j] && j - i + 1 > maxLen) {
maxLen = j - i + 1;
begin = i;
}
}
}
return s.substring(begin, begin + maxLen);
}
}
思路2: 中心扩散法。
回文串,则前后相等
利用回文的性质,对每个点进行探测
然后,回文串分为奇数长度和偶数长度
你以一个中心点进行探测的时候,最后得到的是奇数长度,无法探测出baab这种串
那么在探测前就要判断是否连续两个元素相等,相等则加,再探测
class Solution {
public String longestPalindrome(String s) {
int len = s.length();
if(len < 2) {
return s;
}
char[] cs = s.toCharArray();
int maxLen = 1, start = 0;
for(int i=0; i<len; ) {
int left = i, right = i;
while(right < len - 1 && cs[right] == cs[right+1]) {
right++;
}
i = right + 1;
while(right < len-1 && left > 0 && cs[right+1] == cs[left-1]) {
left --;
right++;
}
if(right - left + 1 > maxLen) {
maxLen = right - left + 1;
start = left;
}
}
return s.substring(start, start + maxLen);
}
}
剑指 Offer 22. 链表中倒数第k个节点
思路:两个指针,一个指针先走k步,然后两个指针同时走,先走的那个到达末尾,另一个则到达倒数第k个节点
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode getKthFromEnd(ListNode head, int k) {
ListNode fast = head, slow = head;
while(k > 0) {
fast = fast.next;
k --;
}
while(fast != null) {
fast = fast.next;
slow = slow.next;
}
return slow;
}
}
98. 验证二叉搜索树
思路1:中序遍历,判断是否递增
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
List<Integer> res = new ArrayList<>();
public boolean isValidBST(TreeNode root) {
inOrder(root);
boolean r = true;
for(int i=0; i<res.size()-1; i++) {
if(res.get(i) >= res.get(i+1)) {
r &= false;
}
}
return r;
}
private void inOrder(TreeNode root) {
if(root == null) {
return;
}
inOrder(root.left);
res.add(root.val);
inOrder(root.right);
}
}
思路2: 递归
比较左边做大的和右边最小的
注意边界值和数据类型,这里使用的是long类型
class Solution {
public boolean isValidBST(TreeNode root) {
return isValidBST(root, Long.MIN_VALUE, Long.MAX_VALUE);
}
public boolean isValidBST(TreeNode root, long min, long max) {
if(root == null) {
return true;
}
if(root.val <= min || root.val >= max) {
return false;
}
return isValidBST(root.left, min, root.val) && isValidBST(root.right, root.val, max);
}
}
40-50
56. 合并区间
https://mp.weixin.qq.com/s/ioUlNa4ZToCrun3qb4y4Ow 这篇文章,详解了区间类题目 ***
思路:先对各区间进行排序,然后遍历各区间,如果前一个的第二维度比后一个的第一维度大时,则合并区间
可以先将数组设为原数组大小,然后再用Arrays.copyOf取其一部分
class Solution {
public int[][] merge(int[][] intervals) {
if(intervals.length == 1) {
return intervals;
}
Arrays.sort(intervals, (v1, v2) -> (v1[0] - v2[0]));
int[][] res = new int[intervals.length][2];
int index = -1;
for(int[] interval : intervals) {
if(index == -1 || interval[0] > res[index][1]) {
res[++index] = interval;
} else {
res[index][1] = Math.max(res[index][1], interval[1]);
}
}
return Arrays.copyOf(res, index+1);
}
}
148. 排序链表
思路1:递归的归并排序
先将链表切分为两半(快慢指针),然后归并这两部分
public ListNode sortList(ListNode head) {
// 1、递归结束条件
if (head == null || head.next == null) {
return head;
}
// 2、找到链表中间节点并断开链表 & 递归下探
ListNode midNode = middleNode(head);
ListNode rightHead = midNode.next;
midNode.next = null;
ListNode left = sortList(head);
ListNode right = sortList(rightHead);
// 3、当前层业务操作(合并有序链表)
return mergeTwoLists(left, right);
}
// 找到链表中间节点(876. 链表的中间结点)
private ListNode middleNode(ListNode head) {
if (head == null || head.next == null) {
return head;
}
ListNode slow = head;
ListNode fast = head.next.next;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
}
return slow;
}
// 合并两个有序链表(21. 合并两个有序链表)
private ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode sentry = new ListNode(-1);
ListNode curr = sentry;
while(l1 != null && l2 != null) {
if(l1.val < l2.val) {
curr.next = l1;
l1 = l1.next;
} else {
curr.next = l2;
l2 = l2.next;
}
curr = curr.next;
}
curr.next = l1 != null ? l1 : l2;
return sentry.next;
}
}
151. 翻转字符串里的单词
思路:使用栈
class Solution {
public String reverseWords(String s) {
if(s.length() < 2) {
return s;
}
s = s.trim();
int left = 0, right = s.length() - 1;
Deque<String> stack = new ArrayDeque<>();
StringBuilder sb = new StringBuilder();
while(left <= right) {
if(sb.length() != 0 && s.charAt(left) == ' ') {
stack.offerFirst(sb.toString());
sb.setLength(0);
} else if (s.charAt(left) != ' ') {
sb.append(s.charAt(left));
}
left ++;
}
stack.offerFirst(sb.toString());
return String.join(" ", stack);
}
}
155. 最小栈
思路:使用两个栈进行操作
一个栈保存数据,另外一个栈保存当前最小值
两个栈每次都同时操作,保持同进同出
class MinStack {
Stack<Integer> data;
Stack<Integer> helper;
/** initialize your data structure here. */
public MinStack() {
data = new Stack<>();
helper = new Stack<>();
}
public void push(int x) {
data.push(x);
if(helper.isEmpty()|| helper.peek() >= x) {
helper.push(x);
} else {
helper.push(helper.peek());
}
}
public void pop() {
data.pop();
helper.pop();
}
public int top() {
if(!data.isEmpty()) {
return data.peek();
} else {
System.out.println("stack is empty");
}
return -1;
}
public int getMin() {
if(!helper.isEmpty()) {
return helper.peek();
} else {
System.out.println("stack is empty");
}
return -1;
}
}
124. 二叉树中的最大路径和
思路:递归
**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
int res = Integer.MIN_VALUE;
public int maxPathSum(TreeNode root) {
depth(root);
return res;
}
public int depth(TreeNode root) {
if (root == null) {
return 0;
}
int left = Math.max(depth(root.left), 0);
int right = Math.max(depth(root.right) , 0);
res = Math.max(left + right + root.val, res);
return Math.max(left, right) + root.val;
}
}
300. 最长递增子序列
思路:使用动态规划
它之前的元素与它进行比较,如果它大,则dp加一
转移方程如下:dp[j] = Math.max(dp[j], dp[i] + 1);
class Solution {
public int lengthOfLIS(int[] nums) {
int len = nums.length;
if(nums == null || len ==0) {
return 0;
}
if(len == 1) {
return 1;
}
int[] dp = new int[len];
dp[0] = 1;
for(int j=1; j < len; j++) {
dp[j] = 1;
for(int i=0; i<j; i++)
{
if(nums[i] < nums[j]) {
dp[j] = Math.max(dp[j], dp[i] + 1);
}
}
}
int res = 0;
for(int i=0; i< len; i++) {
res = Math.max(res, dp[i]);
}
return res;
}
}
4. 寻找两个正序数组的中位数
思路:用两个指针遍历数组,找出中间的两个值
然后根据数组长度,判断总共有奇数个数还是偶数个数
奇数个数:则为正中间点,偶数:则为中间两个值相加除2
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int len1 = nums1.length;
int len2 = nums2.length;
int loc = (len1 + len2) / 2;
int index = (len1 + len2) % 2;
int num = 0;
int per = 0;
int a = 0;
int b = 0;
while(a < len1 || b <len2){
int temp1 = a < len1 ? nums1[a] : Integer.MAX_VALUE;
int temp2 = b < len2 ? nums2[b] : Integer.MAX_VALUE;
if(temp1 < temp2) {
a++;
if(a + b == loc) {
per = temp1;
}
if(a + b == loc+1) {
num = temp1;
}
}else {
b++;
if(a + b == loc) {
per = temp2;
}
if(a + b == loc+1) {
num = temp2;
}
}
}
if(index == 1) {
return num;
} else {
return (per + num) / 2.0;
}
}
}
1143. 最长公共子序列
思路:使用动态规划
class Solution {
public int longestCommonSubsequence(String text1, String text2) {
int len1 = text1.length();
int len2 = text2.length();
int[][] dp = new int[len1+1][len2+1];
for(int i=1; i<=len1; i++) {
for(int j=1; j<=len2; j++) {
if(text1.charAt(i-1) == text2.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]);
}
}
}
return dp[len1][len2];
}
}
169. 多数元素
思路:摩尔投票法
我们维护一个候选众数 candidate 和它出现的次数 count。初始时 candidate 可以为任意值,count 为 0;
我们遍历数组 nums 中的所有元素,对于每个元素 x,在判断 x 之前,如果 count 的值为 0,我们先将 x 的值赋予 candidate,随后我们判断 x:
如果 x 与 candidate 相等,那么计数器 count 的值增加 1;
如果 x 与 candidate 不等,那么计数器 count 的值减少 1。
在遍历完成后,candidate 即为整个数组的众数。
class Solution {
public int majorityElement(int[] nums) {
int count = 0;
Integer candidate = null;
for (int num : nums) {
if (count == 0) {
candidate = num;
}
count += (num == candidate) ? 1 : -1;
}
return candidate;
}
}
34. 在排序数组中查找元素的第一个和最后一个位置
思路:使用二分查找
首先用正常的思路,找出一个与目标值相等的mid,然后mid依次往前遍历和依次往后遍历,得到第一个位置和最后一个位置
class Solution {
public int[] searchRange(int[] nums, int target) {
if(nums == null || nums.length == 0) {
return new int[]{-1,-1};
}
int len = nums.length;
int left = 0, right = len-1;
int start = 0, end = 0;
int mid = 0;
while(left <= right)
{
mid = left + (right - left) / 2;
if (nums[mid] == target) {
break;
} else if(nums[mid] > target) {
right = mid - 1;
} else {
left = mid + 1;
}
}
if(nums[mid] == target) {
start = mid;
end = mid;
while(start > 0 && nums[start-1] == target) {
start--;
}
while(end < len-1 && nums[end+1] == target) {
end ++;
}
return new int[]{start,end};
}
return new int[]{-1,-1};
}
}
60-70
958. 二叉树的完全性检验
思路1:使用层次遍历 (对于一个完全二叉树,层序遍历的过程中遇到第一个空节点之后不应该再出现非空节点)
当出现一次空值后,再出现空值,则不是完全二叉树
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public boolean isCompleteTree(TreeNode root) {
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
boolean isEmptyNode = false;
while(!queue.isEmpty()) {
TreeNode cur = queue.poll();
if(isEmptyNode && cur != null) {
return false;
}
if(cur == null) {
isEmptyNode = true;
continue;
}
queue.offer(cur.left);
queue.offer(cur.right);
}
return true;
}
}
思路2: 对二叉树进行标号, 设root为n,则它的左子结点为2n,它的右子结点为2n + 1;
如果是完全二叉树,这标号是严格无间隔升序的,如1,2,3,4,5。。。
所以,只用比较最后一个结点的标号和二叉树结点个数是否相等即可,如果相等,则为完全二叉树。
即:树的size 是否 等于 最后一个结点的indexCode
class Solution {
// 树的大小
int treeSize = 0;
//最好一个结点的下标,即最大下标
int maxCode = 0;
public boolean isCompleteTree(TreeNode root) {
if(root == null) {
return true;
}
completeTree(root, 1);
return treeSize == maxCode;
}
private void completeTree(TreeNode root, int index) {
if(root == null) {
return;
}
treeSize ++;
maxCode = Math.max(maxCode, index);
completeTree(root.left, 2*index);
completeTree(root.right, 2*index + 1);
}
}
93. 复原 IP 地址
思路:使用DFS,设置一个K存储是第几部分,用StringBuilder存储临时结果,使用回溯的方式找到所有可能的结果;
注意递归的出口 以及 特殊值0 的处理
class Solution {
public List<String> restoreIpAddresses(String s) {
List<String> results = new ArrayList<>();
if( s== null || s.length() < 4 || s.length()>12) { return results; }
StringBuilder tmpAddress = new StringBuilder();
doRestore(0,results, tmpAddress, s);
return results;
}
private void doRestore(int k, List<String> results ,StringBuilder tmpAddress, String s) {
if(k == 4 || s.length() == 0) {
if(k==4 && s.length() == 0) {
results.add(tmpAddress.toString());
}
return;
}
for (int i=0; i < s.length() && i <= 2; i++) {
if(i != 0 && s.charAt(0) == '0') {
break;
}
String part = s.substring(0, i+1);
if(Integer.valueOf(part) <= 255) {
if(tmpAddress.length() != 0) {
part = "." + part;
}
tmpAddress.append(part);
doRestore(k+1, results, tmpAddress, s.substring(i+1));
tmpAddress.delete(tmpAddress.length() - part.length(), tmpAddress.length());
}
}
}
}
64. 最小路径和
思路:二维动态规划
class Solution {
public int minPathSum(int[][] grid) {
if(grid == null || grid.length == 0 || grid[0].length == 0) {
return 0;
}
int n = grid.length, m = grid[0].length;
int[][] dp = new int[n][m];
dp[0][0] = grid[0][0];
for(int i=1; i<n; i++) {
dp[i][0] = dp[i-1][0] + grid[i][0];
}
for(int j=1; j<m; j++) {
dp[0][j] = dp[0][j-1] + grid[0][j];
}
for(int i=1; i<n; i++) {
for(int j=1; j<m; j++) {
dp[i][j] = Math.min(dp[i-1][j] , dp[i][j-1]) + grid[i][j];
}
}
return dp[n-1][m-1];
}
}
240. 搜索二维矩阵 II
思路1:缩小区间法
class Solution {
public boolean searchMatrix(int[][] matrix, int target) {
int n = matrix.length;
int m = matrix[0].length;
int i=0, j = m-1;
while(i < n && j>=0) {
if(matrix[i][j] == target) {
return true;
} else if(matrix[i][j] < target) {
i++;
} else {
j--;
}
}
return false;
}
}
78. 子集
思路:回溯法
深入理解回溯:https://leetcode-cn.com/problems/subsets/solution/xiang-xi-jie-shao-di-gui-hui-su-de-tao-lu-by-reedf/
class Solution {
public List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> results = new ArrayList<>();
// res.add(new ArrayList<>());
int len = nums.length;
for(int i=0; i<=len; i++){
dfs(i, 0, nums, new ArrayList<>(), results);
}
return results;
}
private void dfs(int size, int start, int[] nums, List<Integer> res, List<List<Integer>> results) {
if(res.size() == size) {
results.add(new ArrayList<>(res));
return;
}
for(int i=start; i<nums.length; i++) {
res.add(nums[i]);
dfs(size, i+1, nums, res, results);
res.remove(res.size()-1);
}
}
}
- 组合总和
思路:回溯,
注意:重复组合怎么产生的,由于每次都是从0开始,所以可以引入一个start,每次从start开始 - 组合
思路:回溯
回溯三部曲:1:参数 2:出口 3: 调用 4:回退 - 子集 II
思路:回溯
注意:重复元素的处理,如果与前一个元素相等,而且前一个元素未执行过,则contineu;
用一个visited数组记录状态,并且要是相同元素相邻,需要先对数组排序。 - 组合总和 II
思路:回溯
143. 重排链表
思路1:使用栈实现,先将链表存入栈中,然后依次弹出栈,插入链表中,当链表节点与栈中节点相等,则停止插入
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public void reorderList(ListNode head) {
if(head == null || head.next == null) {
return;
}
Deque<ListNode> stack = new ArrayDeque<>();
ListNode dummy = head;
while(dummy != null) {
stack.push(dummy);
dummy = dummy.next;
}
ListNode stackCur = new ListNode(-1);
ListNode cur = head;
while(cur.next != stackCur) {
stackCur = stack.pop();
stackCur.next = cur.next;
cur.next = stackCur;
cur = cur.next.next;
}
cur.next = null;
}
}
思路2: 1、找中点断开 2、后面一部分逆序 3、从新组合两部分 ***
// 找中点+反转后半部分+合并前后两部分
public void reorderList(ListNode head) {
if(head==null || head.next==null || head.next.next==null)return;
// 1. 找中点,让slow指向中点,或左中点位置
ListNode slow = head, fast = head.next;
while (fast!=null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
}
// 2. 断开中点,反转后半部分
ListNode head2 = null, next = slow.next;
slow.next = null;
slow = next;
while(slow != null) {
next = slow.next;
slow.next = head2;
head2 = slow;
slow = next;
}
// 3. 合并链表head和head2
ListNode curr = head;
ListNode curr2 = head2;
while(curr != null && curr2!=null) {
next = curr.next;
curr.next = curr2;
curr2 = curr2.next;
curr.next.next = next;
curr = next;
}
}
//将各部分都抽出函数
class Solution {
public void reorderList(ListNode head) {
if (head == null) {
return;
}
ListNode mid = middleNode(head);
ListNode l1 = head;
ListNode l2 = mid.next;
mid.next = null;
l2 = reverseList(l2);
mergeList(l1, l2);
}
public ListNode middleNode(ListNode head) {
ListNode slow = head;
ListNode fast = head;
while (fast.next != null && fast.next.next != null) {
slow = slow.next;
fast = fast.next.next;
}
return slow;
}
public ListNode reverseList(ListNode head) {
ListNode prev = null;
ListNode curr = head;
while (curr != null) {
ListNode nextTemp = curr.next;
curr.next = prev;
prev = curr;
curr = nextTemp;
}
return prev;
}
public void mergeList(ListNode l1, ListNode l2) {
ListNode l1_tmp;
ListNode l2_tmp;
while (l1 != null && l2 != null) {
l1_tmp = l1.next;
l2_tmp = l2.next;
l1.next = l2;
l1 = l1_tmp;
l2.next = l1;
l2 = l2_tmp;
}
}
}
239. 滑动窗口最大值
方法一:优先队列
初始时,我们将数组nums 的前 k 个元素放入优先队列中。每当我们向右移动窗口时,我们就可以把一个新的元素放入优先队列中,此时堆顶的元素就是堆中所有元素的最大值。然而这个最大值可能并不在滑动窗口中,在这种情况下,这个值在数组 nums 中的位置出现在滑动窗口左边界的左侧。因此,当我们后续继续向右移动窗口时,这个值就永远不可能出现在滑动窗口中了,我们可以将其永久地从优先队列中移除。
我们不断地移除堆顶的元素,直到其确实出现在滑动窗口中。此时,堆顶元素就是滑动窗口中的最大值。为了方便判断堆顶元素与滑动窗口的位置关系,我们可以在优先队列中存储二元组 (num,index),表示元素 num 在数组中的下标为 index。
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
int n = nums.length;
PriorityQueue<int[]> pq = new PriorityQueue<int[]>(new Comparator<int[]>() {
public int compare(int[] pair1, int[] pair2) {
return pair1[0] != pair2[0] ? pair2[0] - pair1[0] : pair2[1] - pair1[1];
}
});
for (int i = 0; i < k; ++i) {
pq.offer(new int[]{nums[i], i});
}
int[] ans = new int[n - k + 1];
ans[0] = pq.peek()[0];
for (int i = k; i < n; ++i) {
pq.offer(new int[]{nums[i], i});
while (pq.peek()[1] <= i - k) {
pq.poll();
}
ans[i - k + 1] = pq.peek()[0];
}
return ans;
}
}
思路2:
双向队列解决滑动窗口最大值
遍历数组,将 数 存放在双向队列中,并用 L,R 来标记窗口的左边界和右边界。队列中保存的并不是真的 数,而是该数值对应的数组下标位置,并且数组中的数要从大到小排序。如果当前遍历的数比队尾的值大,则需要弹出队尾值,直到队列重新满足从大到小的要求。刚开始遍历时,L 和 R 都为 0,有一个形成窗口的过程,此过程没有最大值,L 不动,R 向右移。当窗口大小形成时,L 和 R 一起向右移,每次移动时,判断队首的值的数组下标是否在 [L,R] 中,如果不在则需要弹出队首的值,当前窗口的最大值即为队首的数
输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
输出: [3,3,5,5,6,7]
解释过程中队列中都是具体的值,方便理解,具体见代码。
初始状态:L=R=0,队列:{}
i=0,nums[0]=1。队列为空,直接加入。队列:{1}
i=1,nums[1]=3。队尾值为1,3>1,弹出队尾值,加入3。队列:{3}
i=2,nums[2]=-1。队尾值为3,-1<3,直接加入。队列:{3,-1}。此时窗口已经形成,L=0,R=2,result=[3]
i=3,nums[3]=-3。队尾值为-1,-3<-1,直接加入。队列:{3,-1,-3}。队首3对应的下标为1,L=1,R=3,有效。result=[3,3]
i=4,nums[4]=5。队尾值为-3,5>-3,依次弹出后加入。队列:{5}。此时L=2,R=4,有效。result=[3,3,5]
i=5,nums[5]=3。队尾值为5,3<5,直接加入。队列:{5,3}。此时L=3,R=5,有效。result=[3,3,5,5]
i=6,nums[6]=6。队尾值为3,6>3,依次弹出后加入。队列:{6}。此时L=4,R=6,有效。result=[3,3,5,5,6]
i=7,nums[7]=7。队尾值为6,7>6,弹出队尾值后加入。队列:{7}。此时L=5,R=7,有效。result=[3,3,5,5,6,7]
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
if(nums == null || nums.length < 2) return nums;
// 双向队列 保存当前窗口最大值的数组位置 保证队列中数组位置的数值按从大到小排序
LinkedList<Integer> queue = new LinkedList();
// 结果数组
int[] result = new int[nums.length-k+1];
// 遍历nums数组
for(int i = 0;i < nums.length;i++){
// 保证从大到小 如果前面数小则需要依次弹出,直至满足要求
while(!queue.isEmpty() && nums[queue.peekLast()] <= nums[i]){
queue.pollLast();
}
// 添加当前值对应的数组下标
queue.addLast(i);
// 判断当前队列中队首的值是否有效
if(queue.peek() <= i-k){
queue.poll();
}
// 当窗口长度为k时 保存当前窗口中最大值
if(i+1 >= k){
result[i+1-k] = nums[queue.peek()];
}
}
return result;
}
}
200. 岛屿数量
import java.util.LinkedList;
import java.util.Queue;
public class Solution {
private final static int[][] DIRECTIONS = {{-1, 0}, {0, -1}, {1, 0}, {0, 1}};
private int rows;
private int cols;
private char[][] grid;
private boolean[][] visited;
public int numIslands(char[][] grid) {
rows = grid.length;
if (rows == 0) {
return 0;
}
cols = grid[0].length;
this.grid = grid;
visited = new boolean[rows][cols];
int count = 0;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
if (!visited[i][j] && grid[i][j] == '1') {
bfs(i, j);
count++;
}
}
}
return count;
}
private void bfs(int i, int j) {
Queue<Integer> queue = new LinkedList<>();
queue.offer(i * cols + j);
// 注意:这里要标记上已经访问过
visited[i][j] = true;
while (!queue.isEmpty()) {
int cur = queue.poll();
int curX = cur / cols;
int curY = cur % cols;
for (int k = 0; k < 4; k++) {
int newX = curX + DIRECTIONS[k][0];
int newY = curY + DIRECTIONS[k][1];
if (inArea(newX, newY) && grid[newX][newY] == '1' && !visited[newX][newY]) {
queue.offer(newX * cols + newY);
// 特别注意:在放入队列以后,要马上标记成已经访问过,语义也是十分清楚的:反正只要进入了队列,迟早都会遍历到它
// 而不是在出队列的时候再标记,如果是出队列的时候再标记,会造成很多重复的结点进入队列,造成重复的操作,这句话如果你没有写对地方,代码会严重超时的
visited[newX][newY] = true;
}
}
}
}
private boolean inArea(int x, int y) {
return x >= 0 && x < rows && y >= 0 && y < cols;
}
}
912. 排序数组
思路:快速排序
选择一个pivot,将数组分为两部分,左边的都比它小,右边的都比它大
class Solution {
public int[] sortArray(int[] nums) {
randomSortArray(nums, 0, nums.length-1);
return nums;
}
private static void randomSortArray(int[] nums, int start, int end) {
if(nums == null || start >= end) {
return;
}
int i = start, j = end;
int pivotkey = nums[start];
while (i < j) {
while(i < j && nums[j] >= pivotkey) j--;
if(i < j) nums[i++] = nums[j];
while(i<j && nums[i] <= pivotkey) i++;
if(i<j) nums[j--] = nums[i];
}
nums[i] = pivotkey;
randomSortArray(nums, start,i-1);
randomSortArray(nums, i+1, end);
}
}
718. 最长重复子数组
思路1:动态规划
用dp[i][j]表示数组A以第i个数结尾和数组B的以第j个数结尾的最长重复子数组
class Solution {
public int findLength(int[] A, int[] B) {
int n = A.length, m = B.length;
int[][] dp = new int[n+1][m+1];
int res = 0;
for(int i=1; i<=n; i++) {
for(int j=1; j<= m; j++) {
if(A[i-1] == B[j-1]) {
dp[i][j] = dp[i-1][j-1] + 1;
res = Math.max(dp[i][j], res);
}
}
}
return res;
}
}
思路2:滑动窗口
枚举 A 和 B 所有的对齐方式。对齐的方式有两类:第一类为 A 不变,B 的首元素与 A 中的某个元素对齐;第二类为 B 不变,A 的首元素与 B 中的某个元素对齐。对于每一种对齐方式,我们计算它们相对位置相同的重复子数组即可。
class Solution {
public int findLength(int[] A, int[] B) {
int n = A.length, m = B.length;
int ret = 0;
for (int i = 0; i < n; i++) {
int len = Math.min(m, n - i);
int maxlen = maxLength(A, B, i, 0, len);
ret = Math.max(ret, maxlen);
}
for (int i = 0; i < m; i++) {
int len = Math.min(n, m - i);
int maxlen = maxLength(A, B, 0, i, len);
ret = Math.max(ret, maxlen);
}
return ret;
}
public int maxLength(int[] A, int[] B, int addA, int addB, int len) {
int ret = 0, k = 0;
for (int i = 0; i < len; i++) {
if (A[addA + i] == B[addB + i]) {
k++;
} else {
k = 0;
}
ret = Math.max(ret, k);
}
return ret;
}
}
70-80
232. 用栈实现队列
class MyQueue {
Stack<Integer> stack1;
Stack<Integer> stack2;
/** Initialize your data structure here. */
public MyQueue() {
stack1 = new Stack<>();
stack2 = new Stack<>();
}
/** Push element x to the back of queue. */
public void push(int x) {
stack1.push(x);
}
/** Removes the element from in front of queue and returns that element. */
public int pop() {
if (stack2.isEmpty()) {
while(!stack1.isEmpty()) {
stack2.push(stack1.pop());
}
}
return stack2.pop();
}
/** Get the front element. */
public int peek() {
if (stack2.isEmpty()) {
while(!stack1.isEmpty()) {
stack2.push(stack1.pop());
}
}
return stack2.peek();
}
/** Returns whether the queue is empty. */
public boolean empty() {
return stack2.isEmpty() && stack1.isEmpty();
}
}
面试题:给一个字符类型的数组chas和一个整数size,请把大小为size的左半区整体右移到右半区,右半区整体移动到左边。
import java.util.*;
public class Main {
//NC翻转字符串(2)
public static String reverse(String str,int start,int end){
char[] ch = str.toCharArray();
while (start < end){
char tmp = ch[start];
ch[start] = ch[end];
ch[end] = tmp;
end--;start++;
}
return String.valueOf(ch);
}
public static String reverse2(String str,int k){
if(str == null ||str.length() == 0 || k<=0 ){
return null;
}
str = reverse(str,0,k-1);
str = reverse(str,k,str.length()-1);
str = reverse(str,0,str.length()-1);
return str;
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
while (scanner.hasNext()){
int k = scanner.nextInt();
String str = scanner.next();
String ret = reverse2(str,k);
System.out.println(ret);
}
}
470. 用 Rand7() 实现 Rand10()
思路:rand7() 到 rand10(),只用将那只要我们把小的数映射到一个大的数,且等概率。
(rand7() - 1) * 7 + rand7()
首先:rand7() - 1得到数到集合为(0,1,2,3,4,5,6)
然后; * 7得到:(0,7,14,21,28,35,42)
最后:+ rand7():(1,2,3,4,5,6,7)
/**
* The rand7() API is already defined in the parent class SolBase.
* public int rand7();
* @return a random integer in the range 1 to 7
*/
class Solution extends SolBase {
public int rand10() {
int num = (rand7() -1) * 7 + rand7();
while(num > 40) {
num = (rand7() -1) * 7 + rand7();
}
return num % 10 + 1;
}
}
rand5得到rand7
int Rand5()
{
int m = rand() % 5 + 1;
return m;
}
int Rand7()
{
int x = 22;
while (x > 21)
x = Rand5() + (Rand5() - 1) * 5;
return x % 7 + 1;
}
剑指 Offer 15. 二进制中1的个数
思路1:如果一个有符号数,右移操作时,用符号位填补最左边的数字。
本题问1的个数,考虑只有1&1 = 1,所以可以将每一位与1做与操作,得到结果为1则表示该位为1。
这里需考虑是将整数右移还是将1左移:整数右移需要考虑为负数的情况,如何判断循环结束,可能造成死循环,而将1左移可以避免这个问题。
public class Solution {
// you need to treat n as an unsigned value
public int hammingWeight(int n) {
int flag = 1;
int count = 0;
for(int i=0; i<32; i++) {
if((flag & n) != 0) {
count ++;
}
flag = flag << 1;
}
return count;
}
}
思路2:
- 核心思路:n & (n - 1) 会把n中的最后一个1变成0
- 循环中每次去除n的最后一个1,res记录循环次数,就是1的个数
public class Solution {
// you need to treat n as an unsigned value
public int hammingWeight(int n) {
int res = 0;
while(n != 0){
n &= n - 1;
res++;
}
return res;
}
}
468. 验证IP地址
思路1:对字符串进行切分
class Solution {
public String validIPAddress(String IP) {
String[] IP4Arr = IP.split("\\.",-1);
if(IP4Arr.length == 4){
return isIP4Arr(IP4Arr);
}
String[] IP6Arr = IP.split(":",-1);
if(IP6Arr.length == 8){
return isIP6Arr(IP6Arr);
}
return "Neither";
}
public String isIP4Arr(String[] IP4Arr){
for(String ip : IP4Arr){
if(ip.length() > 3 || ip.length() <= 0){
return "Neither";
}
for(int i = 0 ;i < ip.length();++i){
if(!Character.isDigit(ip.charAt(i))){
return "Neither";
}
}
int num = Integer.parseInt(ip);
if(num > 255 || String.valueOf(num).length() != ip.length()){
return "Neither";
}
}
return "IPv4";
}
public String isIP6Arr(String[] IP6Arr){
for(String ip : IP6Arr){
if(ip.length() > 4 || ip.length() <= 0){
return "Neither";
}
for(int i = 0 ;i < ip.length();++i){
char c = ip.charAt(i);
if(!Character.isDigit(c) && !( 'a' <= c && c <= 'f') && !('A' <= c && c <= 'F')){
return "Neither";
}
}
}
return "IPv6";
}
}
72. 编辑距离
思路:动态规划 word1 和 word2
int[][] dp = new int[n+1][m+1]
if word1.charAt(i) == word2.charAt(j) : dp[i][j] = dp[i-1][j-1];
else : dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1;
class Solution {
public int minDistance(String word1, String word2) {
int n = word1.length();
int m = word2.length();
if(n == 0) {
return m;
}
if(m == 0) {
return n;
}
int[][] dp = new int[n+1][m+1];
for(int i=1; i<=n; i++) {
dp[i][0] = i;
}
for(int j=1; j<=m; j++) {
dp[0][j] = j;
}
for(int i=1; i<=n; i++) {
for(int j=1; j<=m; j++) {
if(word1.charAt(i-1) == word2.charAt(j-1)) {
dp[i][j] = dp[i-1][j-1];
} else {
dp[i][j] = Math.min(dp[i-1][j], dp[i][j-1]);
dp[i][j] = Math.min(dp[i][j], dp[i-1][j-1]) + 1;
}
}
}
return dp[n][m];
}
}
36进制运算
**
* 36进制由0-9,a-z,共36个字符表示,最小为'0'
* '0''9'对应十进制的09,'a''z'对应十进制的1035
* 例如:'1b' 换算成10进制等于 1 * 36^1 + 11 * 36^0 = 36 + 11 = 47
* 要求按照加法规则计算出任意两个36进制正整数的和
* 如:按照加法规则,计算'1b' + '2x' = '48'
*
* 要求:不允许把36进制数字整体转为10进制数字,计算出10进制数字的相加结果再转回为36进制
*
* @param args
*/
public static void main(String [] args) {
String result = addFunWithStr("1b", "2x");
System.out.println("result = " + result);
}
/**
* 获取值
* @param ch
* @return
*/
public static int getIntFromChar(char ch) {
int ret = -1;
if (ch >='0' && ch <= '9') {
ret = ch - '0';
} else if (ch >= 'a' && ch <= 'z') {
ret = (ch - 'a') + 10;
}
return ret;
}
public static String addFunWithStr(String param1, String param2) {
StringBuffer stringBuffer = new StringBuffer();
String symbol = "0123456789abcdefghijklmnopqrstuvwxyz";
int param1Len = param1.length();
int param2Len = param2.length();
int i = param1Len - 1;
int j = param2Len - 1;
if (i < 0 || j < 0) {
return null;
}
int temp = 0;
while (i >= 0 && j >= 0) {
char ch_1 = param1.charAt(i);
char ch_2 = param2.charAt(j);
int v1 = getIntFromChar(ch_1);
int v2 = getIntFromChar(ch_2);
int ret = v1 + v2;
if (ret >= 36) {
int index = ret - 36 + temp;
char sv = symbol.charAt(index);
stringBuffer.append(sv);
temp = 1; //进位
} else {
int index = ret + temp;
char sv = symbol.charAt(index);
stringBuffer.append(sv);
temp = 0;
}
i--;
j--;
}
while (i >= 0) {
char ch_1 = param1.charAt(i);
stringBuffer.append(ch_1);
i--;
}
while (j >= 0) {
char ch_2 = param2.charAt(i);
stringBuffer.append(ch_2);
j--;
}
StringBuffer result = stringBuffer.reverse();
return result.toString();
}