- 二位数组中的查找
题目
解答:
思路:首先判断是否为空或者数组无元素,直接返回false;
然后从左上角的那个元素开始查找,找到直接返回true,如果比target大,那么列值j–;如果比target小,那么行值i++。最后循环外条件为返回false。
public class Solution {
public boolean Find(int target, int [][] array) {
if(array == null || array.length == 0) return false;
int rows = array.length;
int cows = array[0].length;
int i = 0;
int j = cows - 1;
while(i < rows && j >= 0){
if(array[i][j] == target) return true;
else if(array[i][j] > target){
j--;
}else{
i++;
}
}
return false;
}
}
- 替换空格
题目
思路:思路很清晰,首先将这个字符串转换为字符数组,然后遍历,如果这个字符为空格,那么在res后直接append字符串“%20”,最后返回res;
public class Solution {
public String replaceSpace(StringBuffer str) {
StringBuffer res = new StringBuffer();
char[] tmp = str.toString().toCharArray();
int len = tmp.length;
for(int i = 0; i < len; i++){
if(tmp[i] == ' '){
res.append("%20");
}else{
res.append(tmp[i]);
}
}
return res.toString();
}
}
- 从尾到头打印链表
题目
思路:有两种思路,第一种是递归,首先判断如果为空,则直接返回结果,否则递归该函数,然后输出当前值,这样就是从尾到头输出结果。
非递归:直接的思路是反转链表,这里由于是直接返回value值,所以可以遍历链表放入栈中,然后遍历栈放入arraylist中。
递归:
ArrayList<Integer> res = new ArrayList<Integer>();
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
if(listNode == null) return res;
if(listNode != null){
printListFromTailToHead(listNode.next);
res.add(listNode.val);
}
return res;
}
非递归:
import java.util.*;
public class Solution {
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
Stack<Integer> st = new Stack<Integer>();
ArrayList<Integer> res = new ArrayList<Integer>();
while(listNode != null){
st.push(listNode.val);
listNode = listNode.next;
}
while(!st.isEmpty()){
res.add(st.pop());
}
return res;
}
}
- 用两个栈实现一个队列
题目
思路:插入元素时,首先看辅助栈2是否为空,不为空需要把它放入栈1中,然后再push到栈1中;出队列时需要将栈1的元素放入辅助栈2,然后pop出辅助栈2的栈顶元素,并该输出结果。
import java.util.Stack;
public class Solution {
Stack<Integer> stack1 = new Stack<Integer>();
Stack<Integer> stack2 = new Stack<Integer>();
public void push(int node) {
while(!stack2.isEmpty()){
stack1.push(stack2.pop());
}
stack1.push(node);
}
public int pop() {
while(!stack1.isEmpty()){
stack2.push(stack1.pop());
}
return stack2.pop();
}
}
- 旋转数组中最小的值
题目
思路:二分查找取数组中点,如果中点值小于尾数值,那么中点数一定位于右部分序列,所以end=mid;如果中点值等于尾数值,中点值既可能在左边也可能在右边序列,所以将end–再判断;如果中点值大于尾数值,那么中点值一定位于左边序列,所以start=mid+1;最后输出start或者end下标对应的值。
import java.util.ArrayList;
public class Solution {
public int minNumberInRotateArray(int [] array) {
if(array.length == 0) return 0;
int len = array.length;
int start = 0;
int end = len - 1;
while(start < end){
int mid = start + (end - start)/2;
if(array[mid] < array[end]){
end = mid;
}else if(array[mid] == array[end]){
end--;
}else{
start = mid + 1;
}
}
return array[start];
}
}
- 斐波那契数列
题目
思路:首先n < 2的直接返回n;其他的需要递归循环调用,让a为前两位数,b为前一位数,计算出当前位的值位a + b,然后将a和b的值更新,直到当前位到达n,输出res。
public class Solution {
public int Fibonacci(int n) {
if(n < 2) return n;
int a = 0;
int b = 1;
int res = 0;
for(int i = 2; i <= n; i++){
res = a + b;
a = b;
b = res;
}
return res;
}
}
相同的题目:青蛙跳台阶题目
public class Solution {
public int JumpFloor(int target) {
if(target < 3) return target;
int a = 1;
int b = 2;
int res = 0;
for(int i = 3; i <= target; i++){
res = a + b;
a = b;
b = res;
}
return res;
}
}
变形:贪心青蛙跳题目
思路:当前dp[i]的值为前面所有j<i的dp值之和。
public class Solution {
public int JumpFloorII(int target) {
if(target <= 2) return target;
int[] dp = new int[target + 1];
dp[0] = 1;
dp[1] = 1;
for(int i = 2; i <= target; i++){
for(int j = 0; j < i; j++){
dp[i] += dp[j];
}
}
return dp[target];
}
}
相似:矩形覆盖
题目
思路:和跳台阶一样,当前数由前两个数决定,相加为当前值。
public class Solution {
public int RectCover(int target) {
if(target <= 2) return target;
else{
return RectCover(target - 1) + RectCover(target - 2);
}
}
}
- 二进制中1的个数:
题目
思路:将这个数和这个数-1相与,可以从低位慢慢将右边的1去除,统计加1,不断相与直到这个数等于0,整数和负数都一样。
public class Solution {
public int NumberOf1(int n) {
int res = 0;
while(n != 0){
n = n & (n -1);
res++;
}
return res;
}
}
- 数值的整数次方
题目
思路:首先初始条件底数和幂次方有一个为0的直接返回值,然后判断这个值是正是负,如果是负数就转换为正数,然后二分求幂,直到幂为0,在循环中还有两种情况,一种是幂是1,这种直接乘一次就可以了,其他的乘base的平方。最后返回结果。
public class Solution {
public double Power(double base, int exponent) {
if(base == 0 || exponent == 0) return base == 0? 0: 1.0;
double res = 1;
boolean flag = true;
if(exponent < 0) flag = false;
int absex = Math.abs(exponent);
while(absex > 0){
if(absex == 1){
res = res * base;
}else{
res *= base * base;
}
absex = absex >> 1;
}
return flag == true ? res: 1/res;
}
}
- 调整数组顺序使奇数位于前面偶数位于后面
题目
思路:两种方法求解:第一可以利用两个奇偶栈来更新原来数组中的顺序,这种时间复杂度低,空间复杂度高;第二可以利用冒泡思想,如果前一个为偶数后一个为奇数,交换,这种时间复杂度高,空间复杂度低。
奇偶栈:
import java.util.*;
public class Solution {
public void reOrderArray(int [] array) {
Stack<Integer> st1 = new Stack<Integer>();
Stack<Integer> st2 = new Stack<Integer>();
if(array == null || array.length <= 1) return;
int len = array.length;
for(int i = 0; i < len; i++){
if(array[i] % 2 == 0){
st2.push(array[i]);
}else{
st1.push(array[i]);
}
}
for(int i = len - 1; i >= 0; i--){
if(!st2.isEmpty()){
array[i] = st2.pop();
}else{
array[i] = st1.pop();
}
}
}
}
冒泡交换:
public class Solution {
public void reOrderArray(int [] array) {
if(array == null || array.length <= 1) return;
int len = array.length;
for(int i = 0; i < len - 1; i++){
for(int j = 0; j < len - 1 - i; j++){
if(array[j] % 2 == 0 && array[j + 1] % 2 == 1){
int tmp = array[j];
array[j] = array[j + 1];
array[j + 1] = tmp;
}
}
}
}
}
- 链表中倒数第k个节点:
题目
思路:快慢指针,快指针走K步,如果已经走到null,则直接返回null(说明K>链表长度),然后快慢指针一起走,直到快指针为null,之后输出慢指针当前节点。
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode FindKthToTail(ListNode head,int k) {
if(head == null || k < 1) return null;
ListNode fast = head;
ListNode low = head;
while(k > 0){
if(fast != null){
fast = fast.next;
k--;
}else{
return null;
}
}
while(fast != null){
fast = fast.next;
low = low.next;
}
return low;
}
}
- 反转链表
题目
思路:如果head为null或者head.next为空时,直接返回head;然后设置一个空的pre节点,不断将head节点的next指向pre,然后更新head和pre节点,直到head为空,说明走完了链表,直接返回pre节点。
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode ReverseList(ListNode head) {
if(head == null || head.next == null) return head;
ListNode pre = null;
ListNode tmp = null;
while(head != null){
tmp = head.next;
head.next = pre;
pre = head;
head = tmp;
}
return pre;
}
}
- 合并两个排序的链表
题目
思路:不断比较两个链表那个当前值最小,赋给res的下一个节点,这样直到所有的两个有序链表走完,就可以输出res头节点的下一个节点作为结果返回,因为res的头节点为初始值0,所以需要返回下一个节点。
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode Merge(ListNode list1,ListNode list2) {
if(list1 == null || list2 == null) return list1 == null? list2: list1;
ListNode res = new ListNode(0);
ListNode tmp = res;
while(list1 != null && list2 != null){
if(list1.val <= list2.val){
res.next = list1;
res = res.next;
list1 = list1.next;
}else{
res.next = list2;
res = res.next;
list2 = list2.next;
}
}
while(list1 != null){
res.next = list1;
res = res.next;
list1 = list1.next;
}
while(list2 != null){
res.next = list2;
res = res.next;
list2 = list2.next;
}
return tmp.next;
}
}
- 数的子结构
题目
思路:判断一个数是不是另外一个数的子结构,当root的值相同,则通过subtree函数进一步判断,如果root值不相等,判断大数的左右子树是否包含这颗小数,只要有一个包含就可以了。subtree的判断逻辑是,如果root2为null,那么说明判断完了,直接返回true,如果root1不为空,root2为空,那么字数不存在,返回false,否则继续判断两个数的各自左右子树,这里是相与,需要两个子树都分别相等。最后返回res。
/**
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public boolean HasSubtree(TreeNode root1,TreeNode root2) {
if((root1 == null && root2 != null)|| root2 == null) return false;
boolean res = false;
if(root1.val == root2.val){
res = subtree(root1, root2);
}
if(!res){
res = subtree(root1.left, root2) || subtree(root1.right, root2);
}
return res;
}
public boolean subtree(TreeNode root1, TreeNode root2){
boolean res = false;
if(root2 == null) return true;
if(root1 == null) return false;
if(root1.val == root2.val){
res = subtree(root1.left, root2.left) && subtree(root1.right, root2.right);
}
return res;
}
}
- 二叉树的镜像
题目
思路:如果当前root节点为null,直接返回,只要root节点不为空,交换左右两个子节点,然后如果左子节点不为空,则再调用Mirror处理,如果右子节点不为空,则同样调用Mirror函数处理。
/**
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public void Mirror(TreeNode root) {
if(root == null) return;
if(root.left == null && root.right == null) return;
TreeNode tmp = root.left;
root.left = root.right;
root.right = tmp;
if(root.left != null){
Mirror(root.left);
}
if(root.right != null){
Mirror(root.right);
}
}
}
- 顺时针打印矩阵
题目
思路:设置4个指针,首先从左到右,然后从上到小,之后需要判断上下指针是否相等(可能出现一行矩阵的时候),如果不相等再从右到左打印数字,之后需要判断左右指针是否相等(可能出现矩阵只有一列的情况),如果不相等再从下到上打印数字。
import java.util.ArrayList;
public class Solution {
public ArrayList<Integer> printMatrix(int [][] matrix) {
if(matrix == null) return new ArrayList<Integer>();
ArrayList<Integer> res = new ArrayList<Integer>();
int rows = matrix.length;
int cols = matrix[0].length;
int u = 0;
int d = rows - 1;
int l = 0;
int r = cols - 1;
while(u <= d && l <= r){
for(int i = l; i <= r; i++){
res.add(matrix[u][i]);
}
for(int i = u + 1; i <= d; i++){
res.add(matrix[i][r]);
}
if(u != d){
for(int i = r - 1; i >= l; i--){
res.add(matrix[d][i]);
}
}
if(l != r){
for(int i = d - 1; i > u; i--){
res.add(matrix[i][l]);
}
}
l++;
r--;
u++;
d--;
}
return res;
}
}
- 包含min函数的栈
题目
思路:首先设置一个辅助栈st2,push元素入栈时,如果栈2为空,那么直接加入栈1和栈2,如果栈2不为空,需要判断需要加入的元素是否小于或者等于栈2的栈顶元素,如果满足条件则加入栈2,min函数直接返回栈2的栈顶元素。pop元素时,如果栈1和栈2的栈顶元素相同,则一起pop出来。
import java.util.Stack;
public class Solution {
Stack<Integer> st1 = new Stack<Integer>();
Stack<Integer> st2 = new Stack<Integer>();
public void push(int node) {
st1.push(node);
if(st2.isEmpty()){
st2.push(node);
}else if(st2.peek() >= node){
st2.push(node);
}
}
public void pop() {
if(st1.peek() == st2.peek()){
st2.pop();
}
st1.pop();
}
public int top() {
return st1.peek();
}
public int min() {
return st2.peek();
}
}
- 栈的压入、弹出序列
题目
思路:借助一个辅助栈,将压入序列不断压入,直到辅助栈栈顶元素等于弹出序列第一个元素,然后不断弹出辅助栈,直到辅助栈为空或者辅助栈栈顶元素不等于弹出序列第一个元素,直到将压入序列都遍历完之后,判断辅助序列是否为空,如果为空则返回true,否则返回false。
import java.util.Stack;
public class Solution {
public boolean IsPopOrder(int [] pushA,int [] popA) {
Stack<Integer> help = new Stack<Integer>();
if(pushA.length != popA.length) return false;
int j = 0;
for(int i = 0; i < pushA.length; i++){
help.push(pushA[i]);
while(!help.isEmpty() && help.peek() == popA[j]){
help.pop();
j++;
}
}
if(help.isEmpty()) return true;
else return false;
}
}
- 从上往下打印二叉树
题目
思路:借助队列的思想不断将节点的左右子节点放入,然后不断将左右子节点取出,将节点值放入res中,直到队列为空,说明所有的节点都遍历结束。
/**
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
import java.util.LinkedList;
public class Solution {
public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
if(root == null) return new ArrayList<Integer>();
ArrayList<Integer> res = new ArrayList<Integer>();
LinkedList<TreeNode> que = new LinkedList<TreeNode>();
que.offer(root);
while(!que.isEmpty()){
TreeNode tmp = que.pop();
if(tmp.left != null){
que.offer(tmp.left);
}
if(tmp.right != null){
que.offer(tmp.right);
}
res.add(tmp.val);
}
return res;
}
}
- 二叉搜索树的后序遍历序列
题目
思路:二叉搜索树要求根节点的左子树都小于根节点,右子树都大于根结点,而且后续序列的最后一个元素为根节点,所以所以从头遍历找到第一个小于end对应元素的值,作为左右子树的分界,如果右子树中有元素不大于end对应的元素,那么不是二叉搜索树,如果都大于end对应的元素,那么将左右子树的元素再递归进行判断。
public class Solution {
public boolean VerifySquenceOfBST(int [] sequence) {
if(sequence == null || sequence.length == 0) return false;
int start = 0;
int end = sequence.length - 1;
boolean res = isSearchTree(sequence, start, end);
return res;
}
public static boolean isSearchTree(int[] sequence, int start, int end){
if(start >= end) return true;
int i = 0;
while(i < end && sequence[i] < sequence[end]){
i++;
}
int j = i;
for(; j < end; j++){
if(sequence[j] < sequence[end]){
return false;
}
}
return isSearchTree(sequence, start, i - 1) && isSearchTree(sequence, i, end - 1);
}
}
- 二叉树中和为某一值的路径
题目
思路:回溯法求解,将结果作为函数外的变量,不断尝试将target减去当前值,如果值为0且该节点为叶子节点,那么将tmp放入res中,如果target还大于0且该节点不是叶子节点,那么将该节点继续深入递归,之后将tmp中之前放入的这个点删除,表示不满足条件的路径,去掉该点。
import java.util.ArrayList;
/**
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
ArrayList<ArrayList<Integer>> res = new ArrayList<ArrayList<Integer>>();
ArrayList<Integer> tmp = new ArrayList<Integer>();
public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) {
if(root == null) return res;
tmp.add(root.val);
target = target - root.val;
if(target == 0 && root.left == null && root.left == null){
res.add(new ArrayList<Integer>(tmp));
}
if(target > 0){
res = FindPath(root.left, target);
}
if(target > 0){
res = FindPath(root.right, target);
}
tmp.remove(tmp.size() - 1);
return res;
}
}
- 根据前序和中序建立二叉树
题目
思路:首先判断是否符合同一个二叉树,不是直接返回null;
然后通过函数findRoot递归返回各个节点,其中首先找到pre数组中第一个即根节点在in数组中的位置,然后确定根节点左右子树的各自数据,分成两段,分别递归调用findRoot函数求下一个根节点。
public class Solution {
public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
if(pre == null || in == null || pre.length == 0 || in.length == 0 || pre.length != in.length) return null;
TreeNode root = new TreeNode(0);
root = findRoot(pre, 0, pre.length - 1, in, 0, in.length - 1);
return root;
}
public static TreeNode findRoot(int[] pre, int preStart, int preEnd, int[] in, int inStart, int inEnd){
if(preStart > preEnd || inStart > inEnd) return null;
TreeNode root = new TreeNode(pre[preStart]);
for(int i = inStart; i <= inEnd; i++){
if(in[i] == pre[preStart]){
root.left = findRoot(pre, preStart + 1, preEnd - inEnd + i, in, inStart, i - 1);
root.right = findRoot(pre, preEnd - inEnd + i + 1, preEnd, in, i + 1, inEnd);
}
}
return root;
}
}
- 字符串的排列
题目
思路:首先是字符串为空和长度为0的判断,直接返回;
然后递归,将字符串第一个字符和后续多个字符进行交换,然后递归helper(tmp, start + 1, set),最后需要交换回来。同时利用treeset可以做到去重。
import java.util.*;
public class Solution {
public ArrayList<String> Permutation(String str) {
Set<String> set = new TreeSet<>();
if(str == null || str.length() ==0) return new ArrayList<>();
char[] tmp = str.toCharArray();
int len = tmp.length;
helper(tmp, 0, set);
return new ArrayList<>(set);
}
public static void helper(char[] tmp, int start, Set<String> set){
if(start == tmp.length) set.add(String.valueOf(tmp));
for(int i = start; i < tmp.length; i++){
swap(tmp, i, start);
helper(tmp, start + 1, set);
swap(tmp, i, start);
}
}
public static void swap(char[] tmp, int i, int j){
char c = tmp[i];
tmp[i] = tmp[j];
tmp[j] = c;
}
}
- 数组中次数超过一半的数字
题目
思路:初始条件判断,对于数组为空,长度分别为0和1的直接返回结果,然后遍历数组,将每个数字判断是否在新建的hash表中,如果在就将值加1,否则将将该数组元素放进map中,值为1,最后判断该元素的值是否超过数组长度一半,超过一半直接返回,循环结束返回0即不存在出现次数超过数组长度一半的元素。
import java.util.*;
public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
if(array == null || array.length == 0) return 0;
int len = array.length;
for(int i = 0; i < len; i++){
if(map.containsKey(array[i])){
map.put(array[i], map.get(array[i]) + 1);
}else{
map.put(array[i], 1);
}
if(map.get(array[i]) > len/2){
return array[i];
}
}
return 0;
}
}
- 数组中最小的K个数
题目
采用选择排序的思路,求出排序后前K个数的值。时间复杂度高点,当然也可以用最大堆来做,这里后面可以考虑。
import java.util.*;
public class Solution {
public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
if(input == null || input.length == 0 || k > input.length) return new ArrayList<Integer>();
int len = input.length;
ArrayList<Integer> res = new ArrayList<Integer>();
for(int i = 0; i < k; i++){
for(int j = i + 1; j < len; j++){
if(input[i] > input[j]){
int tmp = input[i];
input[i] = input[j];
input[j] = tmp;
}
}
res.add(input[i]);
}
return res;
}
}
- 连续子数组最大连续和
题目
思路:判断一个tmp和当前元素相加和这个元素本身的大小,如果这个元素本身大,那么tmp赋值为这个元素,否则tmp更新为tmp+这个元素;然后将res和tmp取最大,最后输出结果就是最大的子连续数组和。
public class Solution {
public int FindGreatestSumOfSubArray(int[] array) {
if(array == null || array.length == 0) return 0;
int res = array[0];
int tmp = array[0];
for(int i = 1; i < array.length; i++){
tmp = Math.max(tmp + array[i], array[i]);
res = Math.max(res, tmp);
}
return res;
}
}
- 整数中1出现的个数
题目
数学方法:复杂度比较低,lgn
public class Solution {
public int NumberOf1Between1AndN_Solution(int n) {
int res = 0;
for(int m = 1; m <= n; m = m * 10){
res += ((n/m + 8)/10 * m) + (n/m % 10 == 1 ? n % m + 1: 0);
}
return res;
}
}
直接解法:简直直接
public class Solution {
public int NumberOf1Between1AndN_Solution(int n) {
if(n == 0) return 0;
StringBuffer tmp = new StringBuffer();
int res = 0;
for(int i = 1; i <= n; i++){
tmp.append(String.valueOf(i));
}
for(int i = 0; i < tmp.length(); i++){
if(tmp.charAt(i) == '1'){
res++;
}
}
return res;
}
}
- 把数组排成最小的数
题目
思路:主要是重新定义排序,将相邻的两个字符串左右分别连接比较哪种小,最后排序后的字符串连接起来就是最小的。
import java.util.*;
public class Solution {
public String PrintMinNumber(int [] numbers) {
int len = numbers.length;
String[] tmp = new String[len];
for(int i = 0; i < len; i++){
tmp[i] = String.valueOf(numbers[i]);
}
String res = Arrays.stream(tmp).sorted((a, b)->(a + b).compareTo(b + a)).reduce("",(a, b) -> (a + b));
return res;
}
}
- 丑数
题目
思路:给基数质因子2,3和5设置数量,从0开始,不断比较最小的一个并让其数量加1,将最小的丑数赋给数组当前下标值。
public class Solution {
public int GetUglyNumber_Solution(int index) {
if(index < 7) return index;
int[] res = new int[index];
res[0] = 1;
int flag2 = 0;
int flag3 = 0;
int flag5 = 0;
for(int i = 1; i <= index - 1; i++){
int tmp = Math.min(res[flag2] * 2, Math.min(res[flag3] * 3, res[flag5] * 5));
if(tmp == res[flag2] * 2) flag2++;
if(tmp == res[flag3] * 3) flag3++;
if(tmp == res[flag5] * 5) flag5++;
res[i] = tmp;
}
return res[index - 1];
}
}
- 第一个只出现一次的字符
题目
思路:利用hashmap遍历统计每个字符出现的次数,再次遍历判断是否由只出现一次的字符,有直接返回。
import java.util.*;
public class Solution {
public int FirstNotRepeatingChar(String str) {
if(str == null) return -1;
if(str.length() < 2) return str.length() - 1;
char[] tmp = str.toCharArray();
int len = tmp.length;
HashMap<Character, Integer> res = new HashMap<Character, Integer>();
for(int i = 0; i < len; i++){
if(res.containsKey(tmp[i])){
int j = res.get(tmp[i]) + 1;
res.put(tmp[i], j);
}else{
res.put(tmp[i], 1);
}
}
for(int i = 0; i < len; i++){
if(res.get(tmp[i]) == 1){
return i;
}
}
return -1;
}
}
- 两个链表的第一个公共节点
题目
思路:统计两个链表的长度,然后让长的先走一部分,这样两个链表后面长度就相等。之后遍历两个链表,找到第一个相等的公共节点。
当然还可以将两个链表遍历,放入两个栈中,然后不断弹出,直到找到第一个相等的公共节点。这里给出第一种常规思路解法。
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
if(pHead1 == null || pHead2 == null) return null;
int i = 0;
int j = 0;
ListNode p1 = pHead1;
ListNode p2 = pHead2;
while(p1 != null){
p1 = p1.next;
i++;
}
while(p2 != null){
p2 = p2.next;
j++;
}
int gap = i - j;
p1 = pHead1;
p2 = pHead2;
if(gap > 0){
while(gap > 0){
p1 = p1.next;
gap--;
}
}else{
while(gap < 0){
p2 = p2.next;
gap++;
}
}
while(p1 != null && p2 != null){
if(p1 != p2){
p1 = p1.next;
p2 = p2.next;
}else{
return p1;
}
}
return null;
}
}
- 数字在排序数组中出现的次数
题目
思路:对于排序数组找到等于特地的数采用二分思想,确定到一个这个元素的位置,然后从两边分别遍历找到所有相等的元素,将次数加1,得到最后结果输出。对于二分迭代过程中的左右边界更新按照二分取得的值和目标的值谁大谁小来更新。
public class Solution {
public int GetNumberOfK(int [] array , int k) {
if(array == null || array.length == 0) return 0;
int start = 0;
int end = array.length - 1;
int res = 0;
while(start <= end){
int mid = start + (end - start) / 2;
if(array[mid] == k){
int i = mid;
int j = mid;
while(i >= start && array[i] == k){
res++;
i--;
}
while(j <= end && array[j] == k){
res++;
j++;
}
break;
}
if(array[mid] > k){
end = mid - 1;
continue;
}
if(array[mid] < k){
start = mid + 1;
continue;
}
}
return res > 0? res - 1: 0;
}
}
- 二叉树的深度
题目
思路:首先是初始条件的判断,如果root为空,直接输出0,如果不为空,那么令初始值为1,然后借助TreeLen函数递归求出最长的路径,其中TreeLen函数的思想是:如果root为空,表示已经到了空节点,直接输出maxLen,如果不为空,那么那么递归求出该节点左右子节点的分支长度,最后返回最大的maxLen值。
/**
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public int TreeDepth(TreeNode root) {
if(root == null) return 0;
int maxLen = 0;
return maxLen = TreeLen(root, maxLen);
}
public int TreeLen(TreeNode root, int maxLen){
if(root == null) return maxLen;
maxLen++;
return Math.max(TreeLen(root.left, maxLen), TreeLen(root.right, maxLen));
}
}
- 平衡二叉树
题目
思路:初始判断如果root为空则输出true,否则算出左右子树的深度,如果深度差超过了1,说明不是平衡树,然后进一步对左右子树是否满足平衡要求进行递归,值相与。
public class Solution {
public boolean IsBalanced_Solution(TreeNode root) {
if(root == null) return true;
int leftLen = TreeLen(root.left);
int rightLen = TreeLen(root.right);
if(Math.abs(leftLen - rightLen) > 1) return false;
return IsBalanced_Solution(root.left) && IsBalanced_Solution(root.right);
}
public int TreeLen(TreeNode root){
if(root == null) return 0;
int leftLen = 1 + TreeLen(root.left);
int rightLen = 1 + TreeLen(root.right);
return Math.max(leftLen, rightLen);
}
}
-数组中只出现一次的数字
题目
思路:两种常规思路:第一种遍历数组放入hashmap中,次数为1的数就是要找的数,另外一种借助位运算的性质来找到这两个只出现一次的数。
第一种hashmap统计次数思路:
//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
import java.util.*;
public class Solution {
public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
int len = array.length;
for(int i = 0; i < len; i++){
if(map.containsKey(array[i])){
map.put(array[i], map.get(array[i]) + 1);
}else{
map.put(array[i], 1);
}
}
boolean flag = true;
for(int i = 0; i < len; i++){
if(map.get(array[i]) == 1 && flag == true){
num1[0] = array[i];
flag = false;
}else if(map.get(array[i]) == 1 && flag == false){
num2[0] = array[i];
}
}
}
}
第二种位运算思路:将所有的元素异或得到两个出现一次数的结果,然后找到这个数中二进制位是1的那一位,那么这两个数肯定在这一位上不同,也就将数组元素分为两组,一组在这一位上为0,一组这一位上为1,然后这两组数相异或得到的就是这两个数。
//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
public class Solution {
public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
int len = array.length;
int tmp = 0;
for(int i = 0; i < len; i++){
tmp ^= array[i];
}
int flag = 1;
while((flag & tmp) == 0){
flag = flag << 1;
}
for(int i = 0; i < len; i++){
if((flag & array[i]) == 0){
num1[0] ^= array[i];
}else{
num2[0] ^= array[i];
}
}
}
}
- 和为S的连续正数序列
题目
思路:借助双指针,不断计算左右指针内的序列和,如果target<sum,那么将右指针右移,如果target>sum,将左指针右移,如果target=sum,则将该序列加入res,循环结束条件是left和right指针相同。
import java.util.ArrayList;
public class Solution {
public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {
if(sum < 3) return new ArrayList<ArrayList<Integer>>();
ArrayList<ArrayList<Integer>> res = new ArrayList<ArrayList<Integer>>();
int left = 1;
int right = 2;
while(left < right){
int target = (right + left) * (right - left + 1) / 2;
if(target == sum){
ArrayList<Integer> tmp = new ArrayList<Integer>();
for(int i = left; i <= right; i++){
tmp.add(i);
}
left++;
res.add(tmp);
}else if(target > sum){
left++;
}else{
right++;
}
}
return res;
}
}
- 和为S的两个数字
题目
思路:左右指针计算和是否等于sum,等于的话判断这两个数的乘积是否小于min,小于就赋值给a和b,循环条件为left<right,最后判断a和b是否为0,是的话说明不存在这样的两个数,直接返回空,否则返回res。
import java.util.ArrayList;
public class Solution {
public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
if(array == null || array.length < 2) return new ArrayList<Integer>();
int left = 0;
int right = array.length - 1;
int min = Integer.MAX_VALUE;
int a = 0;
int b = 0;
ArrayList<Integer> res = new ArrayList<>();
while(left < right){
if(array[left] + array[right] == sum){
if(min > array[left] * array[right]){
min = Math.min(min, array[left] * array[right]);
a = array[left];
b = array[right];
}
left++;
right--;
}else if(array[left] + array[right] < sum){
left++;
}else{
right--;
}
}
if(a == 0 && b == 0) return new ArrayList<Integer>();
res.add(a);
res.add(b);
return res;
}
}
- 左旋转字符串
题目
思路:利用substring方法截取两端字符串再连接起来
public class Solution {
public String LeftRotateString(String str,int n) {
if(str == null || str.length() == 0) return "";
if(n == 0 || n % str.length() == 0) return str;
int m = n % str.length();
String a = str.substring(m);
String b = str.substring(0, m);
return a + b;
}
}
- 翻转单词顺序列
题目
思路:如果为空格,则判断输出str;然后将str用空格切分之后,从后往前用空格连接,对于第一个单词后面不加空格,所以需要在循环中加一个判断条件。
public String ReverseSentence(String str) {
if(str == " ") {
return " ";
}
StringBuilder res = new StringBuilder();
ArrayList<String> tmp = new ArrayList<>();
for(String a: str.split(" ")){
tmp.add(a);
}
for(int i = tmp.size() - 1; i >= 0 ; i--){
res.append(tmp.get(i));
res.append(" ");
}
return res.toString().trim();
}
- 扑克牌顺子
题目
思路:先排序,然后统计大小王的数量,如果数量是4直接返回true,然后遍历后面的牌,统计之间的空隙,比较大小王是否可以填空。
import java.util.*;
public class Solution {
public boolean isContinuous(int [] numbers) {
if(numbers == null || numbers.length < 5) return false;
Arrays.sort(numbers);
int king = 0;
int kong = 0;
int i = 0;
for(; i < numbers.length; i++){
if(numbers[i] == 0){
king++;
}else{
break;
}
}
if(king == 4) return true;
int tmp = numbers[i];
i++;
for(; i < numbers.length; i++){
if(numbers[i] - tmp > 0){
kong += numbers[i] - tmp - 1;
tmp = numbers[i];
}else{
return false;
}
}
if(kong == king) return true;
else return false;
}
}
- 圆圈中最后剩下的数
题目
思路:构造一个长度为n的数组,从下标为0的元素开始遍历m个数,并将那个数标记为1,然后继续以下一个元素为起始点遍历m个数,循环条件为剩下的孩子数为1,返回数组中唯一没有被标记为1的那个下标值。
public class Solution {
public int LastRemaining_Solution(int n, int m) {
if(n == 0) return -1;
if(n == 1) return 0;
int re = n;
int[] state = new int[n];
int p = 0;
while(re > 1){
int k = 1;
while(k <= m){
if(state[p] == 0){
k++;
}
p++;
if(p == n){
p = 0;
}
}
if(p == 0){
state[n - 1] = 1;
}else{
state[p - 1] = 1;
}
re--;
}
for(int i = 0; i < state.length; i++){
if(state[i] == 0) return i;
}
return -1;
}
}
- 求1+2+3+…+n
题目
思路:由于不能用题目要求的运算,只能借助短路与运算符来实现递归计算
public class Solution {
public int Sum_Solution(int n) {
int res = n;
boolean flag = (res > 0) && ((res += Sum_Solution(n - 1)) > 0);
return res;
}
}
- 不用加减乘除做加法
题目
思路:两个数异或的结果为各二进制位数之和,两个数相与是二进制位上的,然后将相与左移一位并和异或结果相加,也就是不断异或和相与,直到相与结果为0,则输出相异或的结果即可。
public class Solution {
public int Add(int num1,int num2) {
if(num1 == 0 || num2 == 0) return num1 == 0? num2: num1;
int sum = 0;
int carry = 0;
while(num2 != 0){
sum = num1 ^ num2;
carry = (num1 & num2) << 1;
num1 = sum;
num2 = carry;
}
return num1;
}
}
- 把字符串转换为整数
题目
思路:首先判断是正还是负,前面一个数是否为0。在进行累加的时候需要判断是否超过了整数上下限,超过了直接返回0。
public class Solution {
public int StrToInt(String str) {
if(str == null || str.length() == 0) return 0;
char[] tmp = str.toCharArray();
int flag = 0;
int i = 0;
boolean zero = false;
if(tmp[0] == '+' || tmp[0] == '-'){
flag = tmp[0] == '+'? 1: -1;
i++;
}
if(i < tmp.length && tmp[i] == '0'){
return 0;
}
int sum = 0;
for(; i < tmp.length; i++){
if(tmp[i] - '0' >= 0 && tmp[i] - '9' <= 0){
if(sum / 10 > 214748364) return 0;
if(flag == -1 && Integer.MIN_VALUE + sum * 10 + tmp[i] - '0' > 0) return 0;
if(flag != -1 && Integer.MAX_VALUE - sum * 10 - tmp[i] + '0' < 0) return 0;
sum = sum * 10 + tmp[i] - '0';
}else{
return 0;
}
}
return flag == -1? -1 * sum: sum;
}
}
- 数组中重复的数字
题目
思路:遍历数组,将当前下标和当前值比较,如果相等,则继续遍历,如果不相等,将当前值和他应该在的位置上的元素交换,不断重复直到当前值和当前下标相等,如果需要交换的位置上的值有了这个当前值,那么这个值为重复元素。
public class Solution {
// Parameters:
// numbers: an array of integers
// length: the length of array numbers
// duplication: (Output) the duplicated number in the array number,length of duplication array is 1,so using duplication[0] = ? in implementation;
// Here duplication like pointor in C/C++, duplication[0] equal *duplication in C/C++
// 这里要特别注意~返回任意重复的一个,赋值duplication[0]
// Return value: true if the input is valid, and there are some duplications in the array number
// otherwise false
public boolean duplicate(int numbers[],int length,int [] duplication) {
if(length <= 1) return false;
boolean res = false;
for(int i = 0; i < length; i++){
while(numbers[i] != i){
if(numbers[i] == numbers[numbers[i]]){
duplication[0] = numbers[i];
return true;
}
int t = numbers[numbers[i]];
numbers[numbers[i]] = numbers[i];
numbers[i] = t;
}
}
return false;
}
}
- 构建乘积数组
题目
思路:将B的每个元素分为两个部分乘积,想想为一个矩阵,分别计算上三角和下三角乘积,然后上下三角相乘。
如下图:
import java.util.ArrayList;
public class Solution {
public int[] multiply(int[] A) {
if(A == null || A.length < 1) return null;
int len = A.length;
int[] B = new int[len];
B[0] = 1;
for(int i = 1; i < len; i++){
B[i] = B[i - 1] * A[i - 1];
}
int tmp = 1;
for(int i = len - 2; i >= 0; i--){
tmp *= A[i + 1];
B[i] *= tmp;
}
return B;
}
}
- 正则表达式匹配
题目
思路:动态规划从str和pattern逆向匹配,从后面往前面不断求矩阵,最后返回dp[0][0]。其中需要注意如果pattern[j+1]==*,那么dp[i][j]由dp[i][j+2]和dp[i+1][j]相或决定。
public class Solution {
public boolean match(char[] str, char[] pattern)
{
int len1 = str.length;
int len2 = pattern.length;
boolean[][] dp = new boolean[len1 + 1][len2 + 1];
dp[len1][len2] = true;
for(int i = len1; i >= 0; i--){
for(int j = len2 - 1; j >= 0; j--){
if(j < len2 - 1 && pattern[j + 1] == '*'){
if(i < len1 && (str[i] == pattern[j] || pattern[j] == '.')){
dp[i][j] = dp[i][j + 2] || dp[i + 1][j];
}else{
dp[i][j] = dp[i][j + 2];
}
}else{
if(i != len1 && (str[i] == pattern[j] || pattern[j] == '.')){
dp[i][j] = dp[i + 1][j +1];
}
}
}
}
return dp[0][0];
}
}
- 表示数值的字符串
题目
思路:借助字符串的matches方法来正则匹配。
public class Solution {
public boolean isNumeric(char[] str) {
String tmp = String.valueOf(str);
return tmp.matches("[\\+\\-]?\\d*(\\.\\d+)?([eE][\\+\\-]?\\d+)?");
}
}
- 字符流中第一个不重复的字符
题目
思路:构建一个map和list,map统计字符出现的次数,list记录字符的流入序列。
然后不断检测list的字符在map中出现的次数,如果出现一个字符出现一次直接返回结果,否则返回‘#’。
import java.util.*;
public class Solution {
//Insert one char from stringstream
HashMap<Character, Integer> map = new HashMap<>();
ArrayList<Character> list = new ArrayList<>();
public void Insert(char ch)
{
if(map.containsKey(ch)){
map.put(ch, map.get(ch) + 1);
}else{
map.put(ch, 1);
}
list.add(ch);
}
//return the first appearence once char in current stringstream
public char FirstAppearingOnce()
{
char ch = '#';
for(int i = 0; i < list.size(); i++){
if(map.get(list.get(i)) == 1){
return list.get(i);
}
}
return ch;
}
}
- 链表中环的入口节点
题目
思路:首先需要判断链表节点数目是否大于2;然后利用快慢指针,快指针为pHead.next.next,满指针为pHead.next。循环使得两个指针相遇,然后让快指针重新指向pHead,快慢指针同时以步长为1遍历,知道两个指针相等,那么该节点就是环的入口节点。
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead)
{
if(pHead == null || pHead.next == null || pHead.next.next == null) return null;
ListNode fast = pHead.next.next;
ListNode low = pHead.next;
while(fast != low){
if(fast == null || low == null) return null;
fast = fast.next.next;
low = low.next;
}
fast = pHead;
while(fast != low){
fast = fast.next;
low = low.next;
}
return fast;
}
}
- 删除链表中重复节点
题目
思路:设置前后两个指针,前指针记录不重复节点路径,后指针遍历链表找到不重复的前后两个节点。
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public ListNode deleteDuplication(ListNode pHead)
{
if(pHead == null || pHead.next == null) return pHead;
ListNode tmp = new ListNode(0);
tmp.next = pHead;
ListNode p1 = tmp;
ListNode p2 = pHead;
while(p2 != null){
if(p2.next != null && p2.next.val == p2.val){
while(p2.next != null && p2.val == p2.next.val){
p2 = p2.next;
}
p1.next = p2.next;
p2 = p2.next;
}else{
p1 = p1.next;
p2 = p2.next;
}
}
return tmp.next;
}
}
- 二叉树的下一个节点
题目
思路:分为另种情况,第一种是当前节点有右子树,那么遍历找到右子树的左子树为止输出;第二种是当前节点无右子树,那么要判断该节点是否是父节点的左子树,如果是,输出父节点,否则需要从父节点再往上找,直到该节点为父节点到了根节点也不满足该节点是父节点的左子树,循环外输出null。
/*
public class TreeLinkNode {
int val;
TreeLinkNode left = null;
TreeLinkNode right = null;
TreeLinkNode next = null;
TreeLinkNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public TreeLinkNode GetNext(TreeLinkNode pNode)
{
if(pNode == null) return null;
if(pNode.right != null){
TreeLinkNode tmp = pNode.right;
while(tmp.left != null){
tmp = tmp.left;
}
return tmp;
}
while(pNode.next != null){
if(pNode.next.left == pNode){
return pNode.next;
}else{
pNode = pNode.next;
}
}
return null;
}
}
- 对称的二叉树
题目
递归思路:如果根节点为空,直接返回true。否则将root.left和root.right递归判断。需要left.val == right.val,同时left的左子树和right的右子树,left的右子树和right的左子树都满足对称。
/*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
boolean isSymmetrical(TreeNode pRoot)
{
if(pRoot == null) return true;
return isSymmetricalall(pRoot.left, pRoot.right);
}
public static boolean isSymmetricalall(TreeNode left, TreeNode right){
if(left == null && right == null) return true;
if(left == null || right == null) return false;
return left.val == right.val && isSymmetricalall(left.left, right.right) && isSymmetricalall(left.right, right.left);
}
}
非递归思路:可以用BFS或者DFS来判断。以BFS为例,借助一个栈一次将两个节点放入,再取出来判断值是否相等,然后再放,再判断,循环条件是栈不为空。
非递归DFS:
/*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
import java.util.*;
public class Solution {
boolean isSymmetrical(TreeNode pRoot)
{
if(pRoot == null) return true;
Stack<TreeNode> st = new Stack<>();
st.push(pRoot.left);
st.push(pRoot.right);
while(!st.isEmpty()){
TreeNode t1 = st.pop();
TreeNode t2 = st.pop();
if(t1 == null && t2 == null) continue;
if(t1 == null || t2 == null) return false;
if(t1.val != t2.val) return false;
else{
st.push(t1.left);
st.push(t2.right);
st.push(t1.right);
st.push(t2.left);
}
}
return true;
}
}
非递归BFS:借助queue
/*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
import java.util.*;
public class Solution {
boolean isSymmetrical(TreeNode pRoot)
{
if(pRoot == null) return true;
Queue<TreeNode> st = new LinkedList<>();
st.offer(pRoot.left);
st.offer(pRoot.right);
while(!st.isEmpty()){
TreeNode t1 = st.poll();
TreeNode t2 = st.poll();
if(t1 == null && t2 == null) continue;
if(t1 == null || t2 == null) return false;
if(t1.val != t2.val) return false;
else{
st.offer(t1.left);
st.offer(t2.right);
st.offer(t1.right);
st.offer(t2.left);
}
}
return true;
}
}
注:LinkedList是基于双向链表实现的。
- 按之字形顺序打印二叉树
题目
思路:借助两个栈来实现,一个栈来顺序访问,一个栈来逆序访问。当顺序栈部位空时,顺序打印出值,并将当前pop出来的节点的左右子节点push进逆序栈;当逆序栈不为空时,顺序打印出值,并将当前pop出来的节点的左右子节点push进顺序栈。
/*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
import java.util.*;
public class Solution {
public ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
if(pRoot == null) return new ArrayList<ArrayList<Integer>>();
ArrayList<ArrayList<Integer>> res = new ArrayList<ArrayList<Integer>>();
Stack<TreeNode> st1 = new Stack<>();
Stack<TreeNode> st2 = new Stack<>();
st1.push(pRoot);
while(!st1.isEmpty() || !st2.isEmpty()){
ArrayList<Integer> tmp = new ArrayList<>();
if(!st1.isEmpty()){
while(!st1.isEmpty()){
TreeNode t = st1.pop();
tmp.add(t.val);
if(t.left != null){
st2.push(t.left);
}
if(t.right != null){
st2.push(t.right);
}
}
}else{
while(!st2.isEmpty()){
TreeNode t = st2.pop();
tmp.add(t.val);
if(t.right != null){
st1.push(t.right);
}
if(t.left != null){
st1.push(t.left);
}
}
}
res.add(tmp);
}
return res;
}
}
- 把二叉树打印成多行
题目
思路:借助一个队列,一行一行的进,一行一行的出,相当于上题 按之字形顺序打印二叉树的简化。
import java.util.*;
/*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
ArrayList<ArrayList<Integer>> res = new ArrayList<ArrayList<Integer>>();
if(pRoot == null) return res;
Queue<TreeNode> q = new LinkedList<>();
q.offer(pRoot);
while(!q.isEmpty()){
int len = q.size();
ArrayList<Integer> tmp = new ArrayList<Integer>();
while(len > 0){
TreeNode t = q.poll();
tmp.add(t.val);
if(t.left != null){
q.offer(t.left);
}
if(t.right != null){
q.offer(t.right);
}
len--;
}
res.add(new ArrayList<Integer>(tmp));
}
return res;
}
}
- 序列化二叉树
题目
思路:这里采用前序序列化的方式,序列化时,如果当前节点为空则直接append“#,”,否则将值+,append进入res,然后遍历左子树和右子树,最后输出res。反序列化时,判断当前节点是否为空,如果不为空则将当前值赋值给当前节点,然后遍历左右子树,通过借助index变量来找到对应的值,注意这里不能使用静态变量,会出错。
/*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
int index = -1;
String Serialize(TreeNode root) {
StringBuilder res = new StringBuilder();
if(root == null){
res.append("#,");
}else{
res.append(root.val + ",");
res.append(Serialize(root.left));
res.append(Serialize(root.right));
}
return res.toString();
}
TreeNode Deserialize(String str) {
index++;
String[] tmp = str.split(",");
TreeNode res = null;
if(!tmp[index].equals("#")){
res = new TreeNode(Integer.valueOf(tmp[index]));
res.left = Deserialize(str);
res.right = Deserialize(str);
}
return res;
}
}
- 二叉搜索树的第K个节点
题目
思路:二叉搜索树的中序遍历就是升序排列,所以按照中序遍历找到第k个节点。
注:需要琢磨一下
/*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
int index = 0;
TreeNode KthNode(TreeNode pRoot, int k)
{
if(k <= 0 || pRoot == null) return null;
if(pRoot != null){
TreeNode tmp = KthNode(pRoot.left, k);
if(tmp != null) return tmp;
index++;
if(index == k){
return pRoot;
}
tmp = KthNode(pRoot.right, k);
if(tmp != null) return tmp;
}
return null;
}
}
- 数据流中的中位数
题目
思路:借助优先级队列构造两个最小堆和最大堆。
注:需要琢磨一下
import java.util.PriorityQueue;
import java.util.Comparator;
public class Solution {
//小顶堆
private PriorityQueue<Integer> minHeap = new PriorityQueue<Integer>();
//大顶堆
private PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(15, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
//记录偶数个还是奇数个
int count = 0;
//每次插入小顶堆的是当前大顶堆中最大的数
//每次插入大顶堆的是当前小顶堆中最小的数
//这样保证小顶堆中的数永远大于等于大顶堆中的数
//中位数就可以方便地从两者的根结点中获取了
public void Insert(Integer num) {
//个数为偶数的话,则先插入到大顶堆,然后将大顶堆中最大的数插入小顶堆中
if(count % 2 == 0){
maxHeap.offer(num);
int max = maxHeap.poll();
minHeap.offer(max);
}else{
//个数为奇数的话,则先插入到小顶堆,然后将小顶堆中最小的数插入大顶堆中
minHeap.offer(num);
int min = minHeap.poll();
maxHeap.offer(min);
}
count++;
}
public Double GetMedian() {
//当前为偶数个,则取小顶堆和大顶堆的堆顶元素求平均
if(count % 2 == 0){
return new Double(minHeap.peek() + maxHeap.peek())/2;
}else{
//当前为奇数个,则直接从小顶堆中取元素即可
return new Double(minHeap.peek());
}
}
}
解法二:
import java.util.*;
public class Solution {
LinkedList<Integer> list = new LinkedList<>();
public void Insert(Integer num) {
if(list.size() == 0 || num < list.getFirst()) {
list.addFirst(num);
}else{
boolean flag = false;
for(Integer e : list){
if(e > num){
int index = list.indexOf(e);
flag = true;
list.add(index, num);
break;
}
}
if(!flag){
list.addLast(num);
}
}
}
public Double GetMedian() {
Double res = 0.0;
if(list.size() == 0) return null;
int i = list.size() / 2;
if(list.size() % 2 == 0){
res = Double.valueOf(list.get(i - 1) + list.get(i));
return res / 2;
}else{
return Double.valueOf(list.get((list.size() + 1) / 2 - 1));
}
}
}
- 滑动窗口的最大值
题目
思路:借助双重循环不断求出内循环的最大值。
import java.util.*;
public class Solution {
public ArrayList<Integer> maxInWindows(int [] num, int size)
{
ArrayList<Integer> res = new ArrayList<>();
if(num == null || num.length < size || size == 0) return res;
int len = num.length;
for(int i = 0; i <= len - size; i++){
int max = num[i];
for(int j = i + 1; j <= size + i - 1; j++){
if(num[j] > max){
max = num[j];
}
}
res.add(max);
}
return res;
}
}
- 矩阵中的路径
题目
思路:一般路径就是回溯法,通过设置flag标志是否走过这个格子。
public class Solution {
public boolean hasPath(char[] matrix, int rows, int cols, char[] str)
{
if(matrix == null || rows == 0 || cols == 0 || str == null) return false;
int[] flag = new int[matrix.length];
for(int i = 0; i < rows; i++){
for(int j = 0; j < cols; j++){
if(isPath(matrix, rows, cols, str, flag, i, j, 0)){
return true;
}
}
}
return false;
}
public boolean isPath(char[] matrix, int rows, int cols, char[] str, int[] flag, int i, int j, int k){
int index = cols * i + j;
if(i < 0 || j < 0 || i >= rows || j >= cols || matrix[index] != str[k] || flag[index] == 1){
return false;
}
if(k == str.length - 1) return true;
flag[index] = 1;
if(isPath(matrix, rows, cols, str, flag, i + 1, j, k + 1)
|| isPath(matrix, rows, cols, str, flag, i - 1, j, k + 1)
|| isPath(matrix, rows, cols, str, flag, i, j + 1, k + 1)
|| isPath(matrix, rows, cols, str, flag, i, j - 1, k + 1)){
return true;
}
flag[index] = 0;
return false;
}
}
- 机器人的运动范围
题目
思路:借助上一题的思路,用flag数组来记录是否走过该路径,通过判断是否可以走这个格子来讲结果加1,或者返回0。
public class Solution {
public int movingCount(int threshold, int rows, int cols)
{
if(rows == 0 || cols == 0) return 0;
if(threshold == 0) return 1;
int[] flag = new int[rows * cols];
return number(threshold, 0, 0, rows, cols, flag);
}
public int number(int threshold, int i, int j, int rows, int cols, int[] flag){
int index = cols * i + j;
if(i < 0 || j < 0 || i >= rows || j >= cols || flag[index] == 1 || (count(i) + count(j)) > threshold) return 0;
flag[index] = 1;
return 1+ number(threshold, i + 1, j, rows, cols, flag)
+ number(threshold, i - 1, j, rows, cols, flag)
+ number(threshold, i, j + 1, rows, cols, flag)
+ number(threshold, i, j - 1, rows, cols, flag);
}
public int count(int i){
int num = 0;
while(i > 0){
num += i % 10;
i = i / 10;
}
return num;
}
}
- 剪绳子
题目
思路:如果绳子小于4,那么直接加成1和target-1的两条,如果target大于等于4,进一步判断,如果剩下的绳子大于等于5,就先减3,然后再循环判断,如果绳子小于5,那么就不减,直接输出。
public class Solution {
public int cutRope(int target) {
if(target <= 3) return target - 1;
int res = 1;
while(target >= 2){
if(target >= 5){
target -= 3;
res = res * 3;
}else{
res = res * target;
break;
}
}
return res;
}
}