题目:在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
// 解法一:220ms 暴力n^2
public class Solution {
public boolean Find(int target, int [][] array) {
int x = array.length, y = array[0].length;
boolean flag = false;
for(int i = x - 1;i >= 0;i--) {
for(int j = y - 1;j >= 0;j--) {
if(array[i][j] == target) {
flag = true;
break;
}
}
}
return flag;
}
}
// 解法二:182ms 把每一行看成有序递增的数组,利用二分查找,通过遍历每一行得到答案,时间复杂度是nlogn
public class Solution {
public boolean Find(int target, int [][] array) {
if(array == null || array.length == 0 || array[0].length == 0) {
return false;
}
int row = array.length, col = array[0].length;
for(int i = 0;i < row;i++) {
// 每一行进行二分
int left = 0, right = col - 1;
while(left <= right) {
int mid = (left + right) / 2;
if(array[i][mid] > target) {
right = mid - 1;
} else if(array[i][mid] < target) {
left = mid + 1;
} else {
return true;
}
}
}
return false;
}
}
/*解法三:195ms 利用二维数组由上到下,由左到右递增的规律,那么选取右上角或者左下角的元素a[row][col]与target进行比较,当target小于元素
a[row][col]时,那么target必定在元素a所在行的左边,即col--;当target大于元素a[row][col]时,
那么target必定在元素a所在列的下边,即row++;
*/
public class Solution {
public boolean Find(int target, int [][] array) {
if(array == null || array.length == 0 || array[0].length == 0) {
return false;
}
int row = array.length, col = array[0].length;
int r = 0, c = col - 1;//从右上角开始
while(r < row && c >= 0) {
if(array[r][c] > target) {
c--;
} else if(array[r][c] < target) {
r++;
} else {
return true;
}
}
return false;
}
}
题目:请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
// 解法一:20ms 常规解法
public class Solution {
public String replaceSpace(StringBuffer str) {
StringBuilder tempStr = new StringBuilder();
for(int i = 0;i < str.length();i++) {
if(str.charAt(i) == ' ') {
tempStr.append("%20");
} else {
tempStr.append(str.charAt(i));
}
}
return tempStr.toString();
}
}
题目:输入一个链表,按链表值从尾到头的顺序返回一个ArrayList。
// 解法一:运行时间:20ms 占用内存:9252k
// 利用递归求解
/**
* public class ListNode {
* int val;
* ListNode next = null;
*
* ListNode(int val) {
* this.val = val;
* }
* }
*
*/
import java.util.ArrayList;
public class Solution {
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
ArrayList<Integer> resList = new ArrayList<Integer>();
if(listNode == null) {
return resList;
} else {
return returnListFromTailToHead(listNode);
}
}
public ArrayList<Integer> returnListFromTailToHead(ListNode listNode) {
ArrayList<Integer> tempList;
if(listNode.next == null) {
tempList = new ArrayList<Integer>();
tempList.add(listNode.val);
} else {
tempList = returnListFromTailToHead(listNode.next);
tempList.add(listNode.val);
}
return tempList;
}
}
// 解法二: 超简洁版,运行时间:30ms 占用内存:9340k
// 相当于也是递归,然后输出
/**
* public class ListNode {
* int val;
* ListNode next = null;
*
* ListNode(int val) {
* this.val = val;
* }
* }
*
*/
import java.util.ArrayList;
public class Solution {
ArrayList<Integer> resList = new ArrayList<Integer>();
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
if(listNode != null) {
printListFromTailToHead(listNode.next);
resList.add(listNode.val);
}
return resList;
}
}
//解法三:运行时间:27ms 占用内存:9248k
//先获得正序的ArrayList,在借助Collections.reverse()反转链表
/**
* public class ListNode {
* int val;
* ListNode next = null;
*
* ListNode(int val) {
* this.val = val;
* }
* }
*
*/
import java.util.ArrayList;
import java.util.Collections;
public class Solution {
ArrayList<Integer> resList = new ArrayList<Integer>();
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
while(listNode != null) {
resList.add(listNode.val);
listNode = listNode.next;
}
Collections.reverse(resList);
return resList;
}
}
题目:输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
// 解法一: 运行时间:190ms 占用内存:22148k
/* 代码里有注释
基本思想是递归分治,利用了先序遍历和中序遍历的特性。
先序遍历的特性是:第一个访问的结点一定是根结点。(即数组第一个值是根结点的值)
中序遍历的特性是:数组中的一个值(现结点),其左边的全部是现结点的左子树中的值,右边的全部是现结点的右子树中的值。
*/
/**
* Definition for binary tree
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
public class Solution {
public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
return buildTree(pre, 0, pre.length - 1, in, 0, in.length - 1);
}
// 根据pre[pre_l, pre_r], in[in_l, in_r] 构建二叉树
public TreeNode buildTree(int [] pre, int pre_l, int pre_r, int [] in, int in_l, int in_r) {
// 边界处理
if(pre_l > pre_r) {
return null;
}
if(pre_l == pre_r) return new TreeNode(pre[pre_l]);
//找到前序遍历的根节点,在中序遍历中的位置
int index = find(in, in_l, in_r, pre[pre_l]);
//左子树长度
int len_l = index - in_l;
// 构建根节点
TreeNode root = new TreeNode(pre[pre_l]);
// 根据pre[pre_l + 1, pre_l + len_l], in[in_l, index - 1]构建左子树
TreeNode leftNode = buildTree(pre, pre_l+1, pre_l+len_l, in, in_l, index - 1);
// 根据pre[pre_l + len_l + 1, pre_r], in[index+1, in_r]构建右子树
TreeNode rightNode = buildTree(pre, pre_l+len_l+1, pre_r, in, index+1, in_r);
root.left = leftNode;
root.right = rightNode;
return root;
}
// 找到根节点在中序遍历中的位置
public int find(int [] in, int in_l, int in_r, int target) {
int len = in.length;
for(int i = 0;i < len;i++) {
if(target == in[i]) {
return i;
}
}
return -1;
}
}
//解法二: 运行时间:179ms 占用内存:22980k 超简洁版本
/**
* Definition for binary tree
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
import java.util.*;
public class Solution {
public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
if(pre.length == 0) {
return null;
}
TreeNode root = new TreeNode(pre[0]);
if(pre.length == 1) {
return root;
}
for(int i = 0;i < in.length;i++) {
if(pre[0] == in[i]) { //根节点在中序遍历中的位置
//Arrays.copyOfRange() 是左闭右开区间
root.left = reConstructBinaryTree(Arrays.copyOfRange(pre, 1, i + 1), Arrays.copyOfRange(in, 0, i));
root.right = reConstructBinaryTree(Arrays.copyOfRange(pre, i + 1, pre.length), Arrays.copyOfRange(in, i + 1, in.length));
}
}
return root;
}
}
题目:用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。
// 解法一: 运行时间:15ms 占用内存:9404k
import java.util.Stack;
public class Solution {
Stack<Integer> stack1 = new Stack<Integer>();
Stack<Integer> stack2 = new Stack<Integer>();
public void push(int node) {
stack1.push(node);
}
public int pop() {
if(stack2.empty()) {
while(!stack1.empty()) {
int x = stack1.peek();
stack1.pop();
stack2.push(x);
}
}
int node = stack2.peek();
stack2.pop();
return node;
}
}
题目:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
//解法一:运行时间:383ms 占用内存:30980k
//排序
import java.util.ArrayList;
import java.util.Collections;
import java.util.*;
public class Solution {
public int minNumberInRotateArray(int [] array) {
if(array.length == 0) {
return 0;
}
Arrays.sort(array);
return array[0];
}
}
//解法二:运行时间:324ms 占用内存:28728k
//变向二分法,分析在注释里,也可以看这个https://www.nowcoder.com/questionTerminal/9f3231a991af4f55b95579b44b7a01ba
//~~~这种二分法,时间小了点,内存小了点
import java.util.ArrayList;
import java.util.Collections;
import java.util.*;
public class Solution {
public int minNumberInRotateArray(int [] array) {
if(array.length == 0) {
return 0;
}
int left = 0, right = array.length - 1;
int mid = 0;
// 确保left在左边递增序列,right在右边递增序列
while(array[left] >= array[right]) {
//边界条件
if(right - left == 1) {
mid = right;
break;
}
mid = (left + right) / 2;
//三者相等的情况下,无法判断,只能顺序查找
if(array[mid] == array[left] && array[mid] == array[right]){
return minNumOfArray(array, left, right + 1);
}
// 中间元素位于前面的递增子数组
// 此时最小元素位于中间元素的后面
if(array[mid] >= array[left]) {
left = mid;
}
// 中间元素位于后面的递增子数组
// 此时最小元素位于中间元素的前面
else {
right = mid;
}
}
return array[mid];
}
/**
* 获得一段数组中的最小值
*/
public int minNumOfArray(int [] array, int left, int right) {
int minNum = array[left];
for(int i = left;i < right;i++) {
if(array[i] < minNum) {
minNum = array[i];
}
}
return minNum;
}
}
// 解法三:运行时间:354ms 占用内存:28200k
// 也是二分,上面那种方法的简洁版本,不需要定义一个辅助函数
import java.util.ArrayList;
import java.util.Collections;
import java.util.*;
public class Solution {
public int minNumberInRotateArray(int [] array) {
int low = 0 ; int high = array.length - 1;
while(low < high){
int mid = (low + high) / 2;
if(array[mid] > array[low]){
low = mid;
} else if(array[mid] == array[low]){
low = low + 1;
}else{
high = mid;
}
}
return array[low];
}
}
题目:大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)。n<=39
// 解法一:运行时间:1348ms 占用内存:9408k
// 暴力递归,最简单的
public class Solution {
public int Fibonacci(int n) {
if(n == 0 || n == 1) {
return n;
}
return Fibonacci(n-1) + Fibonacci(n-2);
}
}
// 解法二: 运行时间:1408ms 占用内存:9300k
// 存下中间值(按道理应该比上面那种方法快的)
import java.util.*;
public class Solution {
int [] fibo = new int[40];
public int fibona(int n) {
if(n == 0 || n == 1) {
return n;
}
if(fibo[n] != 0) {
return fibo[n];
} else {
return fibona(n - 1) + fibona(n - 2);
}
}
public int Fibonacci(int n) {
Arrays.fill(fibo, 0);
fibo[1] = 1;
return fibona(n);
}
}
// 解法三: O(n) 运行时间:16ms 占用内存:9324k
// 循环求,复杂度O(n),递归的好处是简单,但是做了很多无用的操作
import java.util.*;
public class Solution {
int [] fibo = new int[40];
public int Fibonacci(int n) {
fibo[1] = 1;
fibo[0] = 0;
for(int i = 2;i <= n;i++) {
fibo[i] = fibo[i - 1] + fibo[i - 2];
}
return fibo[n];
}
}
// 解法四:O(logn) 运行时间:14ms 占用内存:9412k
// 矩阵快速幂:https://blog.csdn.net/aaakkk_1996/article/details/87927108
/*
* O(logN)解法:由f(n) = f(n-1) + f(n-2),可以知道
* [f(n),f(n-1)] = [f(n-1),f(n-2)] * {[1,1],[1,0]}
* 所以最后化简为:[f(n),f(n-1)] = [1,1] * {[1,1],[1,0]}^(n-2)
* 所以这里的核心是:
* 1.矩阵的乘法
* 2.矩阵快速幂(因为如果不用快速幂的算法,时间复杂度也只能达到O(N))
*/
public class Solution {
public int Fibonacci(int n) {
if(n == 0) {
return 0;
} else if(n == 2 || n == 1) {
return 1;
}
int [][]base = {{1, 1}, {1, 0}};
int [][]res = matrixPower(base, n - 2);
return res[0][0] + res[0][1];
}
//矩阵乘法
public int [][] matrixMultiply(int [][] m1, int [][]m2) {
int [][] m = new int[m1.length][m2[0].length];
for(int i = 0;i < m1.length;i++) {
for(int j = 0;j < m2[0].length;j++) {
for(int k = 0;k < m2.length;k++) {
m[i][j] += m1[i][k] * m2[k][j];
}
}
}
return m;
}
/*
* 矩阵的快速幂:
* 1.假如不是矩阵,叫你求m^n,如何做到O(logn)?答案就是整数的快速幂:
* 假如不会溢出,如10^75,把75用用二进制表示:1001011,那么对应的就是:
* 10^75 = 10^64*10^8*10^2*10
* 2.把整数换成矩阵,是一样的
*/
public int [][] matrixPower(int [][] m, int p) {
int [][]res = new int [m.length][m[0].length];
//初始化res微单位矩阵
for(int i = 0;i < res.length;i++) {
res[i][i] = 1;
}
// tmp矩阵保存m^n, n是2的指数倍,初始化为m
int [][] tmp = m;
//快速幂
for(;p != 0;p >>= 1) {
if((p&1) != 0) {
res = matrixMultiply(res, tmp);
}
tmp = matrixMultiply(tmp, tmp);
}
return res;
}
}
题目:一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。
//解法:多列几个,找找规律,依旧是斐波那契,直接按题目7来就行了
题目:一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
//解法一:运行时间:13ms 占用内存:9412k
// record[i] = 2 * record[i - 1]
public class Solution {
public static final int N = 100001;
public int JumpFloorII(int target) {
if(target == 1) {
return 1;
}
int a = 1;
for(int i = 2;i <= target;i++) {
a *= 2;
}
return a;
}
}
// 解法二: 运行时间:21ms 占用内存:9228k
// 相当于求2^(n - 1)这就可以直接库函数
public class Solution {
public static final int N = 100001;
public int JumpFloorII(int target) {
if(target == 1) {
return 1;
}
return (int)Math.pow(2, target - 1);
}
}
// 解法三:运行时间:22ms 占用内存:11620k
// 快速幂
public class Solution {
public static final int N = 100001;
public int JumpFloorII(int target) {
if(target == 1) {
return 1;
}
return ksm(2, target - 1);
}
//快速幂
public static int ksm(int a, int p) {
int res = 1;
int tmp = a;
for(; p != 0;p >>= 1) {
if((p&1) != 0) {
res *= tmp;
}
tmp *= tmp;
}
return res;
}
}
题目:我们可以用21的小矩形横着或者竖着去覆盖更大的矩形。请问用n个21的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?
// 解法: 斐波那契数列,参考7、8题
// 1.递归做 运行时间:311ms 占用内存:9276k
// 2.存中间值,防止重复求值:运行时间:50ms 占用内存:25736k
// 3.矩阵快速幂 运行时间:22ms 占用内存:9364k
**题目:输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。
**
//解法一:运行时间:15ms 占用内存:9344k
//转二进制字符串,遍历字符串
public class Solution {
public int NumberOf1(int n) {
int num = 0;
String str = Integer.toBinaryString(n);
for(int i = 0;i < str.length();i++) {
if(str.charAt(i) == '1') {
num++;
}
}
return num;
}
}
// 解法二:运行时间:20ms 占用内存:9340k
// 最优解法
/*
如果一个整数不为0,那么这个整数至少有一位是1。如果我们把这个整数减1,那么原来处在整数最右边的1就会变为0,原来在1后面的所有的0都会变成1(如果最右边的1后面还有0的话)。其余所有位将不会受到影响。
举个例子:一个二进制数1100,从右边数起第三位是处于最右边的一个1。减去1后,第三位变成0,它后面的两位0变成了1,而前面的1保持不变,因此得到的结果是1011.我们发现减1的结果是把最右边的一个1开始的所有位都取反了。这个时候如果我们再把原来的整数和减去1之后的结果做与运算,从原来整数最右边一个1那一位开始所有位都会变成0。如1100&1011=1000.也就是说,把一个整数减去1,再和原整数做与运算,会把该整数最右边一个1变成0.那么一个整数的二进制有多少个1,就可以进行多少次这样的操作。
*/
public class Solution {
public int NumberOf1(int n) {
int num = 0;
while(n != 0) {
num ++;
n = n & (n - 1);
}
return num;
}
}
题目:
//解法一:运行时间:60ms 占用内存:10336k
//需要对exponent进行判断,如果是负的,就取正,然后结果输出倒数
//同时使用快速幂求解
public class Solution {
public double Power(double base, int exponent) {
double res = 1;
double tmp = base;
boolean isN = false;
if(exponent < 0) {
isN = true;
exponent = -exponent;
}
for(; exponent != 0;exponent >>= 1) {
if((exponent & 1) != 0) {
res = res * tmp;
}
tmp = tmp * tmp;
}
return isN ? 1 / res : res;
}
}
题目:输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
//解法一: 运行时间:17ms 占用内存:9328k
//定义两个数组,分别存奇数和偶数,最后再拷贝在一起
import java.util.*;
public class Solution {
public void reOrderArray(int [] array) {
int []even = new int[array.length];
int []ood = new int[array.length];
int idx_even = 0, idx_odd = 0;
for(int i = 0;i < array.length;i++) {
if(array[i] % 2 == 0) {
even[idx_even] = array[i];
idx_even++;
} else {
ood[idx_odd] = array[i];
idx_odd++;
}
}
for(int i = idx_odd;i < array.length;i++) {
ood[i] = even[i - idx_odd];
}
for(int i = 0;i < array.length;i++) {
array[i] = ood[i];
}
}
}
题目:
//解法一:运行时间:31ms 占用内存:9552k
//两个指针,一个先走k步,这样第一个到达链表尾部时,第二个就在倒数第k个位置
/*
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 <= 0) {
return null;
}
ListNode firstNode = head;
ListNode secNode = head;
while(firstNode != null) {
firstNode = firstNode.next;
if(k < 1) {
secNode = secNode.next;
}
k--;
}
//如果k > 0 表示链表长度不足k,返回null
if(k > 0) {
return null;
} else {
return secNode;
}
}
}
题目:
//解法:运行时间:25ms 占用内存:9580k
//递归(感觉链表的题都可以有一种非常简洁的方法做,通常是递归)
/*
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 node = ReverseList(head.next);
head.next.next = head;
head.next = null;
return node;
}
}
题目:
//解法:运行时间:27ms 占用内存:9672k
//递归(还是那句话,感觉链表的题都可以有一种非常简洁的方法做,通常是递归)
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode Merge(ListNode list1,ListNode list2) {
ListNode head = null;
if(list1 == null) {
return list2;
}
if(list2 == null) {
return list1;
}
if(list1.val <= list2.val) {
head = list1;
head.next = Merge(list1.next, list2);
}
if(list1.val > list2.val) {
head = list2;
head.next = Merge(list1, list2.next);
}
return head;
}
}
题目:输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)
//解法:运行时间:15ms 占用内存:9556k
//递归,(感觉树的问题也都可以用一种非常简洁的方法解决,通常是递归)
/**
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) {
boolean result = false;
if(root1 == null || root2 == null) {
return false;
}
if(root1.val == root2.val) {
result = doTree1hasTree2(root1, root2);
}
if(!result) {
result = doTree1hasTree2(root1.left, root2);
}
if(!result) {
result = doTree1hasTree2(root1.right, root2);
}
return result;
}
public static boolean doTree1hasTree2(TreeNode root1, TreeNode root2) {
if(root2 == null) {
return true;
}
if(root1 == null) {
return false;
}
if(root1.val != root2.val) {
return false;
}
return doTree1hasTree2(root1.left, root2.left) && doTree1hasTree2(root1.right, root2.right);
}
}
带代码解释版本:
//运行时间:25ms 占用内存:9276k
public class Solution {
public static boolean HasSubtree(TreeNode root1, TreeNode root2) {
boolean result = false;
//当Tree1和Tree2都不为零的时候,才进行比较。否则直接返回false
if (root2 != null && root1 != null) {
//如果找到了对应Tree2的根节点的点
if(root1.val == root2.val){
//以这个根节点为为起点判断是否包含Tree2
result = doesTree1HaveTree2(root1,root2);
}
//如果找不到,那么就再去root的左儿子当作起点,去判断时候包含Tree2
if (!result) {
result = HasSubtree(root1.left,root2);
}
//如果还找不到,那么就再去root的右儿子当作起点,去判断时候包含Tree2
if (!result) {
result = HasSubtree(root1.right,root2);
}
}
//返回结果
return result;
}
public static boolean doesTree1HaveTree2(TreeNode node1, TreeNode node2) {
//如果Tree2已经遍历完了都能对应的上,返回true
if (node2 == null) {
return true;
}
//如果Tree2还没有遍历完,Tree1却遍历完了。返回false
if (node1 == null) {
return false;
}
//如果其中有一个点没有对应上,返回false
if (node1.val != node2.val) {
return false;
}
//如果根节点对应的上,那么就分别去子节点里面匹配
return doesTree1HaveTree2(node1.left,node2.left) && doesTree1HaveTree2(node1.right,node2.right);
}
}
题目:操作给定的二叉树,将其变换为源二叉树的镜像。
二叉树的镜像定义:源二叉树
8
/ \
6 10
/ \ / \
5 7 9 11
镜像二叉树
8
/ \
10 6
/ \ / \
11 9 7 5
//解法:运行时间:38ms 占用内存:9616k
/**
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;
}
TreeNode temp = null;
temp = root.left;
root.left = root.right;
root.right = temp;
if(root.left != null) {
Mirror(root.left);
}
if(root.right != null) {
Mirror(root.right);
}
}
}
题目:输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.
//解法一:运行时间:50ms 占用内存:9468k
//模拟,根据圈数来,注意只有一行的那种圈,最好画个例子模拟下
import java.util.ArrayList;
public class Solution {
public ArrayList<Integer> printMatrix(int [][] matrix) {
ArrayList<Integer> arrayList = new ArrayList<Integer>();
int row = matrix.length;
if(row == 0) {
return arrayList;
}
int col = matrix[0].length;
int circle = ((row < col ? row : col) + 1 ) / 2;
for(int i = 0;i < circle;i++) {
for(int j = i;j < col - i;j++) {
arrayList.add(matrix[i][j]);
}
for(int j = i+1;j < row - i;j++) {
arrayList.add(matrix[j][col - i - 1]);
}
for(int j = col - i - 2;j >= i && row - 1 != 2*i;j--) {
arrayList.add(matrix[row-i-1][j]);
}
for(int j = row - i - 2;j >= i + 1 && col - 1 != 2*i;j--) {
arrayList.add(matrix[j][i]);
}
}
return arrayList;
}
}
//解法二:运行时间:36ms 占用内存:9300k
//魔方旋转:一次取一行,然后旋转
import java.util.ArrayList;
public class Solution {
public ArrayList<Integer> printMatrix(int [][] matrix) {
ArrayList<Integer> arrayList = new ArrayList<Integer>();
int row = matrix.length;
while(row != 0) {
for(int i = 0;i < matrix[0].length;i++) {
arrayList.add(matrix[0][i]);
}
if(row == 1) break;
matrix = turn(matrix);
row = matrix.length;
}
return arrayList;
}
public static int [][] turn(int[][] matrix) {
int row = matrix.length;
int col = matrix[0].length;
int [][]temp = new int[col][row - 1];
for(int i = col - 1;i >= 0;i--) {
for(int j = 1;j < row;j++) {
temp[col - i - 1][j - 1] = matrix[j][i];
}
}
return temp;
}
}
题目:定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。
//解法一:运行时间:14ms 占用内存:9376k
//关键在于,如果push的元素比min大,则minStack还是push的是min,
//否则push的是node,这样可以保证,每次minStack的栈顶都是当前栈内元素的最小值!!!
import java.util.Stack;
import java.util.Arrays;
public class Solution {
private int size;
private Stack<Integer> minStack = new Stack<Integer>();
private int [] elements = new int[10];
private int min = Integer.MAX_VALUE;
public void push(int node) {
ensureCapacity(size + 1);
elements[size] = node;
size++;
if(node < min) {
minStack.push(node);
min = node;
} else {
minStack.push(min);
}
}
private void ensureCapacity(int size) {
if(size > elements.length) {
int newLen = (elements.length*3) / 2 + 1;
elements = Arrays.copyOf(elements, newLen);
}
}
public void pop() {
Integer num = top();
if(num != null) {
size--;
}
minStack.pop();
min = minStack.peek();
}
public int top() {
if(size != 0) {
return elements[size - 1];
} else {
return (Integer)null;
}
}
public int min() {
return min;
}
}
题目:
//解法一:运行时间:25ms 占用内存:9032k
//比较简洁的一种解法
import java.util.ArrayList;
import java.util.Stack;
public class Solution {
public boolean IsPopOrder(int [] pushA,int [] popA) {
Stack<Integer> s = new Stack<Integer>();
for(int i = 0, j = 0;i < pushA.length;i++) {
s.push(pushA[i]);
while(j < popA.length && s.peek() == popA[j]) {
s.pop();
j++;
}
}
return s.empty();
}
}
题目:从上往下打印出二叉树的每个节点,同层节点从左至右打印。
//解法:运行时间:25ms 占用内存:9504k
//队列结构
import java.util.ArrayList;
/**
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
import java.util.ArrayList;
public class Solution {
public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
ArrayList<Integer> arrayList = new ArrayList<Integer>();
ArrayList<TreeNode> treeList = new ArrayList<TreeNode>();
if(root == null) {
return arrayList;
}
treeList.add(root);
while(treeList.size() != 0) {
TreeNode node = treeList.remove(0);
arrayList.add(node.val);
if(node.left != null) {
treeList.add(node.left);
}
if(node.right != null) {
treeList.add(node.right);
}
}
return arrayList;
}
}
//解法二:运行时间:18ms 占用内存:9360k
//使用队列实现的,Java里的队列要实现Queue接口
import java.util.ArrayList;
/**
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
import java.util.ArrayList;
import java.util.Queue;
import java.util.LinkedList;
public class Solution {
public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
ArrayList<Integer> arrayList = new ArrayList<Integer>();
Queue<TreeNode> treeQueue = new LinkedList<TreeNode>();
if(root == null) {
return arrayList;
}
treeQueue.add(root);
while(treeQueue.size() != 0) {
TreeNode node = treeQueue.remove();
arrayList.add(node.val);
if(node.left != null) {
treeQueue.add(node.left);
}
if(node.right != null) {
treeQueue.add(node.right);
}
}
return arrayList;
}
}
题目:输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。
//解法:
//递归:
//BST的后序序列的合法序列是,对于一个序列S,最后一个元素是x (也就是根),如果去掉最后一个元素的序列为T,那么T满足:T可以分成两段,前一段(左子树)小于x,后一段(右子树)大于x,且这两段(子树)都是合法的后序序列。完美的递归定义 : ) 。
public class Solution {
public boolean VerifySquenceOfBST(int [] sequence) {
if(sequence.length == 0) {
return false;
}
return judge(sequence, 0, sequence.length - 1);
}
//判断一顿序列是否二叉搜索树
public static boolean judge(int [] sequence, int l, int r) {
if(l >= r) return true;
int i = l;
while(i < r && sequence[i] < sequence[r]) i++;
for(int j = i;j < r;j++) {
if(sequence[j] < sequence[r]) {
return false;
}
}
return judge(sequence, l, i - 1) && judge(sequence, i, r - 1);
}
}
题目:
//解法一:运行时间:22ms 占用内存:9660k
//满足递归定义,第一种递归方法,在每一个分支节点,new一个新路径,分别处理左右路径
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 {
public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) {
ArrayList<ArrayList<Integer>> paths = new ArrayList<ArrayList<Integer>>();
if(root == null) {
return paths;
}
findPath(paths, new ArrayList<Integer>(), root, target);
return paths;
}
public void findPath(ArrayList<ArrayList<Integer>> paths, ArrayList<Integer> path, TreeNode root, int target) {
path.add(root.val);
if(root.left == null && root.right == null) {
if(target == root.val) {
paths.add(path);
}
return;
}
ArrayList<Integer> path2 = new ArrayList<Integer>();
path2.addAll(path);
if(root.left!=null) findPath(paths, path, root.left, target - root.val);
if(root.right!=null) findPath(paths, path2, root.right, target - root.val);
}
}
//解法二:运行时间:23ms 占用内存:9536k
//也是递归版本(加上排序),但是中途不重新申请new一个ArrayList,而是每一层删去最后一个树后回退,有点难理解,我也不太能接受其实
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
/**
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) {
ArrayList<ArrayList<Integer>> paths = new ArrayList<ArrayList<Integer>>();
if(root == null) {
return paths;
}
findPath(paths, new ArrayList<Integer>(), root, target);
Collections.sort(paths, new Comparator<ArrayList<Integer>>(){
public int compare(ArrayList<Integer> a1, ArrayList<Integer> a2) {
if(a1.size() > a2.size()) {
return -1;
} else {
return 1;
}
}
});
return paths;
}
public void findPath(ArrayList<ArrayList<Integer>> paths, ArrayList<Integer> path, TreeNode root, int target) {
if(root == null) return;
path.add(root.val);
if(target == root.val && root.left == null && root.right == null) {
paths.add(new ArrayList<Integer>(path));
}
if(root.left!=null) findPath(paths, path, root.left, target - root.val);
if(root.right!=null) findPath(paths, path, root.right, target - root.val);
path.remove(path.size() - 1);
}
}
题目:输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
//解法:运行时间:23ms 占用内存:9712k
//浅显易懂,其实我不晓得这个题目究竟是个啥意思!!!
//用HashMap映射,一个原来的节点对应一个新的复制过的节点
/*
public class RandomListNode {
int label;
RandomListNode next = null;
RandomListNode random = null;
RandomListNode(int label) {
this.label = label;
}
}
*/
import java.util.HashMap;
public class Solution {
public RandomListNode Clone(RandomListNode pHead) {
HashMap<RandomListNode, RandomListNode> map = new HashMap<RandomListNode, RandomListNode>();
RandomListNode cur = pHead;
//复制label的值
while(cur != null) {
map.put(cur, new RandomListNode(cur.label));
cur = cur.next;
}
//复制next的值
cur = pHead;
while(cur != null) {
map.get(cur).next = map.get(cur.next);
cur = cur.next;
}
//复制random的值
cur = pHead;
while(cur != null) {
map.get(cur).random = map.get(cur.random);
cur = cur.next;
}
RandomListNode root = map.get(pHead);
return root;
}
}
题目:输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。
//解法:运行时间:19ms 占用内存:9308k
//递归:二叉树的中序遍历,改进即可
/**
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
private TreeNode leftNode = null;
private TreeNode rightNode = null;
public TreeNode Convert(TreeNode pRootOfTree) {
if(pRootOfTree == null) {
return null;
}
Convert(pRootOfTree.left);
if(leftNode == null) {
leftNode = rightNode = pRootOfTree;
} else {
rightNode.right = pRootOfTree;
pRootOfTree.left = rightNode;
rightNode = pRootOfTree;
}
Convert(pRootOfTree.right);
return leftNode;
}
}
题目:输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
输入描述:
输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。
//解法:运行时间:157ms 占用内存:11828k
//递归全排列,需要注意的是,如果有重复的字符的话,需要在进入递归前判断条件j == i || ch[j] != ch[i]
import java.util.ArrayList;
import java.util.Collections;
public class Solution {
public ArrayList<String> Permutation(String str) {
ArrayList<String> arrayList = new ArrayList<String>();
if(str != null && str.length() > 0) {
PermutationHelper(str.toCharArray(), 0, arrayList);
}
Collections.sort(arrayList);
return arrayList;
}
public void PermutationHelper(char [] ch, int i, ArrayList<String> res) {
if(i == ch.length - 1) {
res.add(String.valueOf(ch));
} else {
for(int j = i;j < ch.length;j++) {
if(j == i || ch[j] != ch[i]) {
swap(ch, i, j);
PermutationHelper(ch, i + 1, res);
swap(ch, i, j);
}
}
}
}
public void swap(char [] ch, int i, int j) {
char temp = ch[i];
ch[i] = ch[j];
ch[j] = temp;
}
}
题目:数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
//解法:运行时间:22ms 占用内存:9352k
//排序后找中间的那个数,就是次数超过一半的,再遍历一遍,看是不是,因为有sort,复杂度:O(nlogn)
import java.util.Arrays;
public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
Arrays.sort(array);
int middle = array[array.length / 2];
int num = 0;
for(int i = 0;i < array.length;i++) {
if(array[i] == middle) {
num++;
}
}
if(num > array.length / 2) {
return middle;
} else {
return 0;
}
}
}
题目:输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。
//解法一:运行时间:33ms 占用内存:9600k
//排序,取前k个数
import java.util.ArrayList;
import java.util.Arrays;
import java.util.*;
public class Solution {
public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
ArrayList<Integer> arrayList = new ArrayList<Integer>();
if(k > input.length || k < 0) {
return arrayList;
}
Arrays.sort(input);
int [] temp = Arrays.copyOfRange(input, 0, k);
for(int i = 0;i < temp.length;i++) {
arrayList.add(temp[i]);
}
return arrayList;
}
}
//解法二:运行时间:24ms 占用内存:9484k
//最小堆:java的优先队列是用堆实现的
import java.util.ArrayList;
import java.util.Arrays;
import java.util.PriorityQueue;
import java.util.Comparator;
public class Solution {
public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
ArrayList<Integer> arrayList = new ArrayList<Integer>();
if(k > input.length || k <= 0) {
return arrayList;
}
PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(k, new Comparator<Integer>(){
public int compare(Integer o1, Integer o2) {
if(o1 > o2) {
return -1;
} else {
return 1;
}
}
});
for(int i = 0;i < input.length;i++) {
if(maxHeap.size() != k) {
maxHeap.offer(input[i]);
} else if(maxHeap.peek() > input[i]) {
maxHeap.poll();
maxHeap.offer(input[i]);
}
}
for(Integer i : maxHeap) {
arrayList.add(i);
}
return arrayList;
}
}
题目:HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。给一个数组,返回它的最大连续子序列的和,你会不会被他忽悠住?(子向量的长度至少是1)
//解法一:运行时间:15ms 占用内存:9368k
//
public class Solution {
public int FindGreatestSumOfSubArray(int[] array) {
if(array.length == 0) {
return -1;
}
int res = 0;
int max = 0;
for(int i = 0;i < array.length;i++) {
res += array[i];
if(res <= 0) {
res = 0;
} else {
max = res > max? res : max;
}
}
if(max == 0) {
return -1;
} else {
return max;
}
}
}
//解法二:运行时间:25ms 占用内存:9036k
//动态规划
public class Solution {
public int FindGreatestSumOfSubArray(int[] array) {
if(array.length == 0) {
return -1;
}
int res = 0;
int[] dp = new int[array.length];
dp[0] = array[0] < 0 ? 0 : array[0];
for(int i = 1;i < array.length;i++) {
//以array[i]为末尾元素的子数组的和的最大值,子数组的元素的相对位置不变
dp[i] = Math.max(dp[i - 1] + array[i], array[i]);
res = Math.max(dp[i], res);
}
if(res <= 0) {
return -1;
} else {
return res;
}
}
}
题目:求出113的整数中1出现的次数,并算出1001300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数)。
//解法一:运行时间:17ms 占用内存:9508k
//暴力
public class Solution {
public int NumberOf1Between1AndN_Solution(int n) {
int totalNum = 0;
for(int i = 1;i <= n;i++) {
totalNum += get1OfNum(i);
}
return totalNum;
}
public int get1OfNum(int n) {
int num = 0;
while(n != 0) {
if(n % 10 == 1) {
num++;
}
n /= 10;
}
return num;
}
}
//解法二;运行时间:14ms 占用内存:9464k
//找规律解法
//https://www.nowcoder.com/questionTerminal/bd7f978302044eee894445e244c7eee6
public class Solution {
public int NumberOf1Between1AndN_Solution(int n) {
int totalNum = 0;
if(n <= 0) {
return 0;
}
for(long i = 1;i <= n;i *= 10) {
long div = i * 10;
totalNum += (n / div) * i + Math.min(Math.max(n % div - i + 1, 0), i);
}
return totalNum;
}
}
题目:输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。
//解法:运行时间:23ms 占用内存:9800k
//重载sort
import java.util.ArrayList;
import java.util.Collections;
import java.util.Arrays;
import java.util.Comparator;
public class Solution {
public String PrintMinNumber(int [] numbers) {
ArrayList<Integer> arrayList = new ArrayList<Integer>();
for(int num : numbers) {
arrayList.add(num);
}
Collections.sort(arrayList, new Comparator<Integer>(){
public int compare(Integer a, Integer b) {
String str1 = a + "" + b;
String str2 = b + "" + a;
return str1.compareTo(str2);
}
});
String res = "";
for(int num : arrayList) {
res += num;
}
return res;
}
}
题目:把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。
//解法一:运行时间:19ms 占用内存:9544k
//暴力求解,注意p2, p3, p5队列里存的是res[]*(2, 3, 5), 而不是对应2,3,5的倍数,看懵了好久
//解析:https://www.nowcoder.com/questionTerminal/6aa9e04fc3794f68acf8778237ba065b
//例如:P1队列:2 4 6 8 10 12 18,这里每一个队列里存的是丑数队列里*2、*3、*5的值,而不是队列里前一个数加2,所以P1队列里12后面是18而不是14
import java.util.ArrayList;
public class Solution {
public int GetUglyNumber_Solution(int index) {
if(index < 7) {
return index;
}
ArrayList<Integer> arrayList = new ArrayList<Integer>();
int p2 = 0, p3 = 0, p5 = 0;
arrayList.add(1);
for(int i = 1;i < index;i++) {
int Min = Math.min(arrayList.get(p2) * 2, Math.min(arrayList.get(p3) * 3, arrayList.get(p5) * 5));
arrayList.add(Min);
if(Min == arrayList.get(p2) * 2) p2++;
if(Min == arrayList.get(p3) * 3) p3++;
if(Min == arrayList.get(p5) * 5) p5++;
}
return arrayList.get(index - 1);
}
}
题目:在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写).
//解法:运行时间:52ms 占用内存:9892k
//HashMap查询
import java.util.HashMap;
public class Solution {
public int FirstNotRepeatingChar(String str) {
if(str.length() == 0 || str == null) {
return -1;
}
HashMap<Character, Integer> m = new HashMap<>();
for(int i = 0;i < str.length();i++) {
if(m.containsKey(str.charAt(i))) {
int t = m.get(str.charAt(i));
m.put(str.charAt(i), ++t);
} else {
m.put(str.charAt(i), 1);
}
}
for(int i = 0;i < str.length();i++) {
if(m.get(str.charAt(i)) == 1) {
return i;
}
}
return -1;
}
}
题目:在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007
输入描述:
题目保证输入的数组中没有的相同的数字
数据范围:
对于%50的数据,size<=10^4
对于%75的数据,size<=10^5
对于%100的数据,size<=2*10^5
//解法一:暴力O(n^2),这种方法过不了,(—_-)
public class Solution {
public int InversePairs(int [] array) {
int count = 0;
for(int i = 0;i < array.length;i++) {
for(int j = i + 1;j < array.length;j++) {
if(array[i] > array[j]) count++;
}
}
return count;
}
}
//解法二:运行时间:683ms 占用内存:51552k
//归并排序的变形,刚开始过不了,是因为没有对1000000000...7取模!!!
//看了大部分题解,也都是归并来做的
//看代码前,建议把归并排序的源代码看一看,就可以发现这个代码非常相似!
public class Solution {
public int InversePairs(int [] array) {
if(array.length == 0) {
return 0;
}
return MergeSortFindInversePairs(array, 0, array.length - 1);
}
public static int MergeSortFindInversePairs(int [] array, int start, int end) {
if(start >= end) return 0;
int mid = (start + end) / 2;
int leftCount = MergeSortFindInversePairs(array, start, mid);
int rightCount = MergeSortFindInversePairs(array, mid + 1, end);
int foreIdx = start;
int backIdx = mid + 1;
int k = 0;
int[] tmp = new int[end - start + 1];
int count = 0;
while(foreIdx <= mid && backIdx <= end) {
if(array[foreIdx] < array[backIdx]) {
tmp[k++] = array[foreIdx++];
} else {
tmp[k++] = array[backIdx++];
count += mid - foreIdx + 1;
if(count>=1000000007) {//数值过大求余
count%=1000000007;
}
}
}
while(foreIdx <= mid) {
tmp[k++] = array[foreIdx++];
}
while(backIdx <= end) {
tmp[k++] = array[backIdx++];
}
for(int i = 0;i < k;i++) {
array[start + i] = tmp[i];
}
return (leftCount + rightCount + count) % 1000000007;
}
}
题目:输入两个链表,找出它们的第一个公共结点。
//解法一:运行时间:30ms 占用内存:9396k
//先让长的链表先走链表长度差,再比较,因为两个链表有公共尾部
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
int len1 = FindListLen(pHead1);
int len2 = FindListLen(pHead2);
if(len1 > len2) {
int k = len1 - len2;
while(k != 0) {
pHead1 = pHead1.next;
k--;
}
} else {
int k = len2 - len1;
while(k != 0) {
pHead2 = pHead2.next;
k--;
}
}
while(pHead1 != null) {
if(pHead1 == pHead2) {
return pHead1;
}
pHead1 = pHead1.next;
pHead2 = pHead2.next;
}
return null;
}
public int FindListLen(ListNode pHead) {
ListNode p = pHead;
int len = 0;
while(p != null) {
len++;
p = p.next;
}
return len;
}
}
//解法二:运行时间:19ms 占用内存:9564k
//利用HashMap
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
import java.util.HashMap;
public class Solution {
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
HashMap<ListNode, Integer> m = new HashMap<ListNode, Integer>();
while(pHead1 != null) {
m.put(pHead1, null);
pHead1 = pHead1.next;
}
while(pHead2 != null) {
if(m.containsKey(pHead2)) {
return pHead2;
}
pHead2 = pHead2.next;
}
return null;
}
}
题目:统计一个数字在排序数组中出现的次数。
//解法一:运行时间:21ms 占用内存:9348k
//二分后,向两边找
public class Solution {
public int GetNumberOfK(int [] array , int k) {
int start = 0;
int end = array.length - 1;
int count = 0;
while(start <= end) {
int mid = (start + end) / 2;
if(array[mid] < k) {
start = mid + 1;
} else if(array[mid] > k) {
end = mid - 1;
} else {
int i = mid, j = mid + 1;
while(i >= 0 && array[i] == k) {
count++;
i--;
}
while(j < array.length && array[j] == k) {
count++;
j++;
}
return count;
}
}
return 0;
}
}
//解法二:运行时间:13ms 占用内存:9264k
//二分黑科技,学习了
//经实验,如果二分法找的值k,不在数组中,那么最后返回的下标是小于k的那个值下标,这样我们找(k-0.5, k+0.5)就行了
public class Solution {
public int GetNumberOfK(int [] array , int k) {
return binarySearch(array, k+0.5) - binarySearch(array, k-0.5);
}
public int binarySearch(int[] array, double k) {
int start = 0, end = array.length - 1;
while(start <= end) {
int mid = (start + end) / 2;
if(array[mid] < k) {
start = mid + 1;
} else if(array[mid] > k) {
end = mid - 1;
}
}
return start ;
}
}
题目:输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。
//解法一:运行时间:16ms 占用内存:9420k
//递归版本
/**
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 leftDepth = TreeDepth(root.left);
int rightDepth = TreeDepth(root.right);
return 1 + Math.max(leftDepth, rightDepth);
}
}
//解法二:运行时间:35ms 占用内存:9356k
//非递归版
/**
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
import java.util.LinkedList;
import java.util.Queue;
public class Solution {
public int TreeDepth(TreeNode root) {
if(root == null) {
return 0;
}
Queue<TreeNode> q = new LinkedList<TreeNode>();
q.add(root);
int depth = 0;
while(q.size() != 0) {
int size = q.size();
depth++;
for(int i = 0;i < size;i++) {
TreeNode temp = q.poll();
if(temp.left != null) q.add(temp.left);
if(temp.right != null) q.add(temp.right);
}
}
return depth;
}
}
题目:输入一棵二叉树,判断该二叉树是否是平衡二叉树。
//解法一:运行时间:20ms 占用内存:9328k
//递归版,复杂度较大
public class Solution {
public boolean IsBalanced_Solution(TreeNode root) {
if(root == null) {
return true;
}
return Math.abs(binaryDepth(root.left) - binaryDepth(root.right)) <= 1 &&
IsBalanced_Solution(root.left) && IsBalanced_Solution(root.right);
}
public int binaryDepth(TreeNode root) {
if(root == null) return 0;
return 1 + Math.max(binaryDepth(root.left), binaryDepth(root.right));
}
}
//解法二:运行时间:16ms 占用内存:9204k
//剪枝版
public class Solution {
public boolean IsBalanced_Solution(TreeNode root) {
return binaryDepth(root) != -1;
}
public int binaryDepth(TreeNode root) {
if(root == null) return 0;
int left = binaryDepth(root.left);
if(left == -1) return -1;
int right = binaryDepth(root.right);
if(right == -1) return -1;
return Math.abs(left - right) > 1 ? -1 : 1 + Math.max(left, right);
}
}
题目:一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
//解法一:运行时间:25ms 占用内存:9612k
//利用HahMap,有点小题大作的感觉
//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
import java.util.HashMap;
public class Solution {
public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
HashMap<Integer, Integer> mp = new HashMap<Integer, Integer>();
for(int i = 0;i < array.length;i++) {
if(mp.containsKey(array[i])) {
int count = mp.get(array[i]);
mp.put(array[i], ++count);
} else {
mp.put(array[i], 1);
}
}
boolean flagNum1 = false;
for(int i = 0;i < array.length;i++) {
if(mp.get(array[i]) == 1) {
if(flagNum1) {
num2[0] = array[i];
} else {
num1[0] = array[i];
flagNum1 = true;
}
}
}
}
}
//解法二:运行时间:15ms 占用内存:9440k
//通过异或,(技术流方法,解析请看https://www.nowcoder.com/questionTerminal/e02fdb54d7524710a7d664d082bb7811)
//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
import java.util.HashMap;
public class Solution {
public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
int len = array.length;
if(len == 2) {
num1[0] = array[0];
num2[0] = array[1];
}
int bitNum = 0;
for(int i = 0;i < len;i++) {
bitNum ^= array[i];
}
int index = findFirst1(bitNum);
for(int i = 0;i < len;i++) {
if(isBit1(array[i], index)) {
num1[0] ^= array[i];
} else {
num2[0] ^= array[i];
}
}
}
public int findFirst1(int num) {
int index = 0;
while((num & 1) == 0 && num < 32) {
num >>= 1;
index++;
}
return index;
}
public boolean isBit1(int num, int index) {
return ((num >> index) & 1) == 1;
}
}
题目:小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列? Good Luck!
输出描述:
输出所有和为S的连续正数序列。序列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序
//解法:运行时间:32ms 占用内存:9620k
//穷举
import java.util.ArrayList;
public class Solution {
public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {
if(sum <= 0) {
return null;
}
ArrayList<ArrayList<Integer>> res = new ArrayList<ArrayList<Integer>>();
int[] Plus = new int[sum + 5];
Plus[0] = 0;
Plus[1] = 1;
for(int i = 2;i <= sum;i++) {
Plus[i] = Plus[i - 1] + i;
}
for(int i = 0;i < sum;i++) {
for(int j = 0;j < sum;j++) {
if(Plus[j] - Plus[i] == sum) {
ArrayList<Integer> arrayList = new ArrayList<Integer>();
for(int k = i + 1;k <= j;k++) {
arrayList.add(k);
}
res.add(arrayList);
break;
} else if(Plus[j] - Plus[i] > sum) {
break;
}
}
}
return res;
}
}
//解法二:运行时间:20ms 占用内存:9556k
//滑动窗口法
import java.util.ArrayList;
public class Solution {
public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {
ArrayList<ArrayList<Integer>> res = new ArrayList<ArrayList<Integer>>();
int pLow = 1;
int pHigh = 2;
while(pHigh > pLow) {
int cur = (pHigh + pLow) * (pHigh - pLow + 1) / 2;
if(cur == sum) {
ArrayList<Integer> arrayList = new ArrayList<Integer>();
for(int i = pLow;i <= pHigh;i++) {
arrayList.add(i);
}
res.add(arrayList);
pLow++;
} else if(cur < sum) {
pHigh++;
} else {
pLow++;
}
}
return res;
}
}
题目:输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。
输出描述:
对应每个测试案例,输出两个数,小的先输出。
//解法一:运行时间:16ms 占用内存:9304k
//二分查找
import java.util.ArrayList;
public class Solution {
public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
ArrayList<Integer> res = new ArrayList<>();
for(int i = 0;i < array.length;i++) {
if(binarySearch(array, sum - array[i])) {
res.add(array[i]);
res.add(sum - array[i]);
return res;
}
}
return res;
}
public boolean binarySearch(int [] array,int num) {
int s = 0;
int e = array.length - 1;
while(s <= e) {
int mid = (s + e) / 2;
if(array[mid] < num) {
s = mid + 1;
} else if(array[mid] > num) {
e = mid - 1;
} else {
return true;
}
}
return false;
}
}
//解法二:运行时间:13ms 占用内存:9268k
//前后指针法
import java.util.ArrayList;
public class Solution {
public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
ArrayList<Integer> res = new ArrayList<>();
int s = 0, e = array.length - 1;
while(s < e) {
if(array[s] + array[e] == sum) {
res.add(array[s]);
res.add(array[e]);
return res;
} else if(array[s] + array[e] < sum) {
s++;
} else {
e--;
}
}
return res;
}
}
题目:汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它!
//解法一:运行时间:25ms 占用内存:11224k
//substring即可
public class Solution {
public String LeftRotateString(String str,int n) {
String resStr = "";
if(n > str.length()) {
return resStr;
}
resStr += str.substring(n, str.length());
resStr += str.substring(0, n);
return resStr;
}
}
题目:牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上。同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思。例如,“student. a am I”。后来才意识到,这家伙原来把句子单词的顺序翻转了,正确的句子应该是“I am a student.”。Cat对一一的翻转这些单词顺序可不在行,你能帮助他么?
//解法:运行时间:31ms 占用内存:9384k
//注意str.trim().equals()的使用,比较字符串内容时,应该用equals,而不是==
public class Solution {
public String ReverseSentence(String str) {
if(str.trim().equals("")) {
return str;
}
String[] arrayStr = str.split(" ");
StringBuilder sb = new StringBuilder();
for(int i = arrayStr.length - 1;i >= 0;i--) {
sb.append(arrayStr[i]);
if(i >= 1) {
sb.append(" ");
}
}
return sb.toString();
}
}
题目:LL今天心情特别好,因为他去买了一副扑克牌,发现里面居然有2个大王,2个小王(一副牌原本是54张_)…他随机从中抽出了5张牌,想测测自己的手气,看看能不能抽到顺子,如果抽到的话,他决定去买体育彩票,嘿嘿!!“红心A,黑桃3,小王,大王,方片5”,“Oh My God!”不是顺子…LL不高兴了,他想了想,决定大\小 王可以看成任何数字,并且A看作1,J为11,Q为12,K为13。上面的5张牌就可以变成“1,2,3,4,5”(大小王分别看作2和4),“So Lucky!”。LL决定去买体育彩票啦。 现在,要求你使用这幅牌模拟上面的过程,然后告诉我们LL的运气如何, 如果牌能组成顺子就输出true,否则就输出false。为了方便起见,你可以认为大小王是0。
//解法:运行时间:14ms 占用内存:9248k
/*
1、排序
2、计算所有相邻数字间隔总数
3、计算0的个数
4、如果2、3相等,就是顺子
5、如果出现对子,则不是顺子
*/
import java.util.Arrays;
public class Solution {
public boolean isContinuous(int [] numbers) {
if(numbers.length == 0) {
return false;
}
int numOfZero = 0; //0的数目
int numOfInterval = 0;//间隔数目
Arrays.sort(numbers);
for(int i = 0;i < numbers.length - 1;i++) {
if(numbers[i] == 0) {
numOfZero++;
continue;
}
if(numbers[i] == numbers[i+1]) {
return false;
}
numOfInterval += numbers[i+1] - numbers[i] - 1;
}
if(numOfZero >= numOfInterval) {
return true;
}
return false;
}
}
每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0…m-1报数…这样下去…直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!_)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)
//解法:运行时间:14ms 占用内存:9176k
//约瑟夫环,递归解法
import java.util.LinkedList;
import java.util.List;
public class Solution {
public int LastRemaining_Solution(int n, int m) {
if(m == 0 || n==0) {
return -1;
}
int res = 0;
for(int i = 2;i <= n;i++) {
res = (res + m) % i;
}
return res;
}
}
题目:求1+2+3+…+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。
//解法:运行时间:12ms 占用内存:9184k
//等差数列求和公式....
public class Solution {
public int Sum_Solution(int n) {
return n * (1 + n) / 2;
}
}
//解法二:运行时间:17ms 占用内存:9252k
//大神的做法,&& 逻辑与的短路操作
public class Solution {
public int Sum_Solution(int n) {
int ans = n;
boolean flag = (ans > 0) && ((ans += Sum_Solution(n - 1)) > 0);
return ans;
}
}
题目:写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。
//解法:运行时间:13ms 占用内存:9416k
public class Solution {
public int Add(int num1,int num2) {
while (num2!=0) {
int temp = num1^num2;
num2 = (num1&num2)<<1;
num1 = temp;
}
return num1;
}
}
题目:将一个字符串转换成一个整数(实现Integer.valueOf(string)的功能,但是string不符合数字要求时返回0),要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0。
//解法: 运行时间:13ms 占用内存:9276k
public class Solution {
public int StrToInt(String str) {
if(str == null || str.length() == 0) {
return 0;
}
int res = 0;
boolean flag = false;//判断是否负数,false ----> 正数
int i = 0;//循环变量
if(!Character.isDigit(str.charAt(0))) {
if(str.charAt(0) == '-') {
flag = true;
} else if(str.charAt(0) == '+') {
flag = false;
} else { //如果是其他字符,就不是字符串
return 0;
}
i++;
}
for(;i < str.length();i++) {
if(!Character.isDigit(str.charAt(i))) {
return 0;
}
res = res*10 + (int)(str.charAt(i) - '0');
}
if(flag) {
res = 0 - res;
}
return res;
}
}
题目:在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。
//解法:运行时间:18ms 占用内存:9660k
//HashMap做法
import java.util.HashMap;
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) {
HashMap<Integer, Integer> mp = new HashMap<>();
for(int i = 0;i < length;i++) {
if(!mp.containsKey(numbers[i])) {
mp.put(numbers[i], 1);
} else {
duplication[0] = numbers[i];
return true;
}
}
return false;
}
}
//解法二:运行时间:23ms 占用内存:9656k
//相当于哈希数组法
import java.util.Arrays;
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) {
boolean[] isDup = new boolean[length];
Arrays.fill(isDup, false);
for(int i = 0;i < length;i++) {
if(!isDup[numbers[i]]) {
isDup[numbers[i]] = true;
} else {
duplication[0] = numbers[i];
return true;
}
}
return false;
}
}
题目:给定一个数组A[0,1,…,n-1],请构建一个数组B[0,1,…,n-1],其中B中的元素B[i]=A[0]·A[1]·…·A[i-1]·A[i+1]·…·[n-1]。不能使用除法
//解法:运行时间:25ms 占用内存:9244k
import java.util.ArrayList;
//倒三角解法
public class Solution {
public int[] multiply(int[] A) {
int[] b = new int[A.length];
b[0] = 1;
if(A.length == 0) {
return b;
}
for(int i = 1;i < A.length;i++) {
b[i] = b[i - 1] * A[i - 1];
}
int temp = 1;
for(int i = A.length - 2;i >= 0;i--) {
temp *= A[i + 1];
b[i] *= temp;
}
return b;
}
}
题目:请实现一个函数用来匹配包括’.‘和’‘的正则表达式。模式中的字符’.‘表示任意一个字符,而’'表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"abaca"匹配,但是与"aa.a"和"ab*a"均不匹配
//解法:运行时间:14ms 占用内存:9272k
//https://www.nowcoder.com/questionTerminal/45327ae22b7b413ea21df13ee7d6429c
//注意每一次访问数组的时候都要进行下表判断
public class Solution {
public boolean match(char[] str, char[] pattern) {
if(str == null || pattern == null) {
return false;
}
return matchHelper(str, 0, pattern, 0);
}
public boolean matchHelper(char[] str, int strIdx, char[] pattern, int patternIdx) {
//字符串走到尾,模式走到尾,返回true
if(strIdx == str.length && patternIdx == pattern.length) {
return true;
}
//字符串没有走到尾,模式走到尾,返回false
if(strIdx != str.length && patternIdx == pattern.length) {
return false;
}
//如果模式串第二个是*
/*
if(patternIdx + 1 < pattern.length && pattern[patternIdx + 1] == '*') {
//如果模式串第一个与字符串第一个匹配,三匹配方式
if((str[strIdx] == pattern[patternIdx] && strIdx < str.length)|| (pattern[patternIdx] == '.' && strIdx < str.length)) {
return matchHelper(str, strIdx, pattern, patternIdx + 2) ||
matchHelper(str, strIdx + 1, pattern, patternIdx) ||
matchHelper(str, strIdx + 1, pattern, patternIdx + 2);
} else {
return matchHelper(str, strIdx, pattern, patternIdx + 2);
}
}
*/
if (patternIdx + 1 < pattern.length && pattern[patternIdx + 1] == '*') {
if ((strIdx != str.length && pattern[patternIdx] == str[strIdx]) || (pattern[patternIdx] == '.' && strIdx != str.length)) {
return matchHelper(str, strIdx, pattern, patternIdx + 2)//模式后移2,视为x*匹配0个字符
|| matchHelper(str, strIdx + 1, pattern, patternIdx + 2)//视为模式匹配1个字符
|| matchHelper(str, strIdx + 1, pattern, patternIdx);//*匹配1个,再匹配str中的下一个
} else {
return matchHelper(str, strIdx, pattern, patternIdx + 2);
}
}
if((strIdx != str.length && pattern[patternIdx] == str[strIdx]) || (pattern[patternIdx] == '.' && strIdx != str.length)) {//如果不是*
return matchHelper(str, strIdx + 1, pattern, patternIdx + 1);
}
return false;
}
}
题目:请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100",“5e2”,"-123",“3.1416"和”-1E-16"都表示数值。 但是"12e",“1a3.14”,“1.2.3”,"±5"和"12e+4.3"都不是。
//解法:运行时间:22ms 占用内存:9336k
//这题完全按题意写就行,一个一个查
public class Solution {
public boolean isNumeric(char[] str) {
int len = str.length;
int decNum = 0;//小数点数目
int eNum = 0;//是否是指数,指数后面不能有小数点
//如果第一位是数字或者正负号
if(Character.isDigit(str[0]) || str[0] == '+' || str[0] == '-') {
for(int i = 1;i < len;i++) {
if(str[i] == 'e' || str[i] == 'E') {
eNum++;
if(i + 1 == len) return false; //如果e后面没有数字,返回false
if(str[i+1] == '+' || str[i+1] == '-') {//指数可以是正负号
i++;
}
} else if(str[i] == '.') {
if(eNum != 0 || decNum != 0) {//说明是指数,不能有小数点
return false;
}
decNum++;
} else if(!Character.isDigit(str[i])) {
return false;
}
}
} else {
return false;
}
return true;
}
}
**题目:请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。
**
//解法一:运行时间:18ms 占用内存:9668k
//哈希
public class Solution {
int[] hash = new int[256];
StringBuilder sb = new StringBuilder();
//Insert one char from stringstream
public void Insert(char ch)
{
sb.append(ch);
if(hash[ch] == 0) {
hash[ch] = 1;
} else {
hash[ch] += 1;
}
}
//return the first appearence once char in current stringstream
public char FirstAppearingOnce()
{
char [] ch = sb.toString().toCharArray();
for(char c : ch) {
if(hash[c] == 1) {
return c;
}
}
return '#';
}
}
题目:给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
//解法:运行时间:17ms 占用内存:9544k
//解析:https://www.nowcoder.com/questionTerminal/253d2c59ec3e4bc68da16833f79a38e4?f=discussion
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead)
{
ListNode fast = pHead;
ListNode low = pHead;
//找相遇点
while(fast != null && fast.next != null) {
fast = fast.next.next;
low = low.next;
if(fast == low) {
break;
}
}
if(fast == null || fast.next == null) return null;
low = pHead;
while(fast != low) {
fast = fast.next;
low = low.next;
}
return low;
}
}
题目:在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5
//解法一:运行时间:19ms 占用内存:9712k
//递归法
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
*/
import java.util.HashMap;
public class Solution {
public ListNode deleteDuplication(ListNode pHead) {
if(pHead == null || pHead.next == null) {
return pHead;
}
if(pHead.val == pHead.next.val) {
ListNode pNode = pHead.next;
while(pNode != null && pNode.val == pHead.val) {
pNode = pNode.next;
}
return deleteDuplication(pNode);
} else {
pHead.next = deleteDuplication(pHead.next);
return pHead;
}
}
}
//解法二:运行时间:19ms 占用内存:9660k
//非递归,有点难度,没看懂
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
*/
import java.util.HashMap;
public class Solution {
public ListNode deleteDuplication(ListNode pHead) {
if(pHead == null || pHead.next == null) {
return pHead;
}
ListNode root = new ListNode(0);
root.next = pHead;
ListNode pre = root;
ListNode last = root.next;
while(last != null) {
if(last.next != null && last.val == last.next.val) {
while(last.next != null && last.val == last.next.val) {
last = last.next;
}
pre.next = last.next;
last = last.next;
} else {
pre = pre.next;
last = last.next;
}
}
return root.next;
}
}
给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。
//解法:运行时间:30ms 占用内存:9604k
/*
1、有右子树的,那么下个结点就是右子树最左边的点
2、没有右子树的,也可以分成两类,a)是父节点左孩子,那么父节点就是下一个节点b)是父节点的右孩子,找他的父节点的父节点的父节点...直到当前结点是其父节点的左孩子位置。如果没有,那么他就是尾节点。
*/
/*
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 node = pNode.right;
while(node.left != null) {
node = node.left;
}
return node;
}
//如果没有右子树,找父节点为左子树的父节点
while(pNode.next != null) {
if(pNode.next.left == pNode) {
return pNode.next;
}
pNode = pNode.next;
}
//如果没有找到,那么这个节点就是尾节点,下一个节点是null
return null;
}
}
题目:请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。
//解法;运行时间:22ms 占用内存:9404k
//依旧是递归(二叉树和链表类的题目基本上都递归的简便解法)
/*
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 isSymmetricalHelper(pRoot.left, pRoot.right);
}
boolean isSymmetricalHelper(TreeNode left, TreeNode right) {
if(left == null) return right == null;
if(right == null) return false;
if(left.val != right.val) return false;
return isSymmetricalHelper(left.right, right.left) && isSymmetricalHelper(left.left, right.right);
}
}
题目:请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。
//解法:运行时间:23ms 占用内存:9692k
//层序遍历,偶数层进行reverse
import java.util.ArrayList;
/*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
import java.util.Queue;
import java.util.LinkedList;
import java.util.Collections;
public class Solution {
public 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.add(pRoot);
int k = 0;//当前在第几层,需不需要取反
while(q.size() != 0) {
int size = q.size();
ArrayList<Integer> arrayList = new ArrayList<>();
for(int i = 0;i < size;i++) {
TreeNode t = q.remove();
arrayList.add(t.val);
if(t.left != null) {
q.add(t.left);
}
if(t.right != null) {
q.add(t.right);
}
}
if(k % 2 != 0) {
Collections.reverse(arrayList);
}
k++;
res.add(arrayList);
}
return res;
}
}
题目:从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。
//解法;运行时间:33ms 占用内存:9428k
//二叉树的层序遍历
import java.util.ArrayList;
import java.util.Queue;
import java.util.LinkedList;
import java.util.Collections;
/*
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.add(pRoot);
while(q.size() != 0) {
int size = q.size();
ArrayList<Integer> arrayList = new ArrayList<>();
for(int i = 0;i < size;i++) {
TreeNode t = q.remove();
arrayList.add(t.val);
if(t.left != null) {
q.add(t.left);
}
if(t.right != null) {
q.add(t.right);
}
}
res.add(arrayList);
}
return res;
}
}
//解法二:运行时间:23ms 占用内存:9664k
//递归做法:
import java.util.ArrayList;
import java.util.Queue;
import java.util.LinkedList;
import java.util.Collections;
/*
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;
}
PrintHelper(pRoot, 1, res);
return res;
}
private void PrintHelper(TreeNode root, int depth, ArrayList<ArrayList<Integer>> list) {
if(root == null) {
return;
}
if(depth > list.size()) {
list.add(new ArrayList<Integer>());
}
list.get(depth - 1).add(root.val);
PrintHelper(root.left, depth+1, list);
PrintHelper(root.right, depth+1, list);
}
}
题目:请实现两个函数,分别用来序列化和反序列化二叉树
//解法:运行时间:38ms 占用内存:9728k
/*
1. 对于序列化:使用前序遍历,递归的将二叉树的值转化为字符,并且在每次二叉树的结点
不为空时,在转化val所得的字符之后添加一个' , '作为分割。对于空节点则以 '#' 代替。
2. 对于反序列化:按照前序顺序,递归的使用字符串中的字符创建一个二叉树(特别注意:
在递归时,递归函数的参数一定要是char ** ,这样才能保证每次递归后指向字符串的指针会
随着递归的进行而移动!!!)
*/
/*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public int index = -1;//节点序号
String Serialize(TreeNode root) {
StringBuilder sb = new StringBuilder();
if(root == null) {
sb.append("#,");
return sb.toString();
}
sb.append(root.val + ",");
sb.append(Serialize(root.left));
sb.append(Serialize(root.right));
return sb.toString();
}
TreeNode Deserialize(String str) {
index ++;
int len = str.length();
if(index > len) return null;
String[] strs = str.split(",");
TreeNode node = null;
if(!strs[index].equals("#")) {
node = new TreeNode(Integer.valueOf(strs[index]));
node.left = Deserialize(str);
node.right = Deserialize(str);
}
return node;
}
}
题目:给定一棵二叉搜索树,请找出其中的第k小的结点。例如, (5,3,7,2,4,6,8) 中,按结点数值大小顺序第三小结点的值为4。
//解法一:运行时间:26ms 占用内存:9560k
//中序遍历,k时返回
/*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
private int index = 0;
TreeNode KthNode(TreeNode pRoot, int k) {
if(pRoot == null) {
return null;
}
TreeNode node = KthNode(pRoot.left, k);
if(node != null) {
return node;
}
index++;
if(index == k) {
return pRoot;
}
node = KthNode(pRoot.right, k);
if(node != null) {
return node;
}
return null;
}
}
//解法二:运行时间:26ms 占用内存:9564k
//容易理解的递归版
/*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
import java.util.ArrayList;
public class Solution {
private ArrayList<TreeNode> res = new ArrayList<>();
TreeNode KthNode(TreeNode pRoot, int k) {
if(pRoot == null) {
return null;
}
KthNodeHelper(pRoot);
if(res.size() < 1 || k < 1 || res.size() < k) {
return null;
}
return res.get(k-1);
}
private void KthNodeHelper(TreeNode root) {
if(root != null) {
KthNodeHelper(root.left);
res.add(root);
KthNodeHelper(root.right);
}
}
}
题目:如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。
//解法:运行时间:21ms 占用内存:9680k
//最大堆,最小堆法
import java.util.PriorityQueue;
import java.util.Comparator;
public class Solution {
private int count = 0;
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;
}
});
public void Insert(Integer num) {
if(count % 2 == 0) {
maxHeap.offer(num);
int temp = maxHeap.poll();
minHeap.offer(temp);
} else {
minHeap.offer(num);
int temp = minHeap.poll();
maxHeap.offer(temp);
}
count++;
}
public Double GetMedian() {
if((count & 1) == 0) {
return new Double(maxHeap.peek() + minHeap.peek()) / 2;
} else {
return new Double(minHeap.peek());
}
}
}
题目:给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。
//解法:运行时间:21ms 占用内存:9184k
//暴力 O(n^2)
import java.util.Comparator;
import java.util.PriorityQueue;
import java.util.ArrayList;
public class Solution {
public ArrayList<Integer> maxInWindows(int [] num, int size) {
ArrayList<Integer> res = new ArrayList<Integer>();
if(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 < i + size;j++) {
if(num[j] > max) {
max = num[j];
}
}
res.add(max);
}
return res;
}
}
//解法二:运行时间:18ms 占用内存:9212k
//左神,双端队列法
import java.util.LinkedList;
import java.util.ArrayList;
public class Solution {
public ArrayList<Integer> maxInWindows(int [] num, int size) {
if (num == null || num.length == 0 || size <= 0 || num.length < size) {
return new ArrayList<Integer>();
}
ArrayList<Integer> res = new ArrayList<Integer>();
LinkedList<Integer> qmax = new LinkedList<>();
for(int i = 0;i < num.length;i++) {
//将队列前面比num[i]小的全部删掉
while(!qmax.isEmpty() && num[qmax.peekLast()] < num[i]) {
qmax.pollLast();
}
qmax.addLast(i);
//判断队首元素是否过期(因为每一层循环都会判断,所以最多只有一个过期的元素)
if(qmax.peekFirst() < i - size + 1) {
qmax.pollFirst();
}
//加入滑动窗口最大值
if(i >= size - 1) {
res.add(num[qmax.peekFirst()]);
}
}
return res;
}
}
题目:请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则之后不能再次进入这个格子。 例如 a b c e s f c s a d e e 这样的3 X 4 矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。
//解法:运行时间:15ms 占用内存:9044k
//回溯法 - 搜索
public class Solution {
public boolean hasPath(char[] matrix, int rows, int cols, char[] str) {
boolean[] flag = new boolean[matrix.length];
for(int i = 0;i < rows;i++) {
for(int j = 0;j < cols;j++) {
if(hasPathHelper(matrix, i, j, rows, cols, str, flag, 0)) {
return true;
}
}
}
return false;
}
//访问标志flag, k:正在查找第个字符
public boolean hasPathHelper(char[] matrix, int i, int j, int rows, int cols, char[] str, boolean[] flag, int k) {
//计算二维字符在一维中的下标
int index = i * cols + j;
if(i < 0 || i >= rows || j < 0 || j >= cols || matrix[index] != str[k] || flag[index] == true) {
return false;
}
//匹配到了最后一个字符
if(k == str.length - 1) {
return true;
}
flag[index] = true;
if(hasPathHelper(matrix, i - 1, j, rows, cols, str, flag, k+1)||
hasPathHelper(matrix, i + 1, j, rows, cols, str, flag, k+1)||
hasPathHelper(matrix, i, j + 1, rows, cols, str, flag, k+1)||
hasPathHelper(matrix, i, j - 1, rows, cols, str, flag, k+1)) {
return true;
}
//回溯
flag[index] = false;
return false;
}
}
题目:地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?
//解法一:运行时间:24ms 占用内存:9420k
//回溯
public class Solution {
public int movingCount(int threshold, int rows, int cols) {
int flag[][] = new int[rows][cols]; //记录是否已经走过
return helper(0, 0, rows, cols, flag, threshold);
}
private int helper(int i, int j, int rows, int cols, int[][] flag, int threshold) {
if (i < 0 || i >= rows || j < 0 || j >= cols || numSum(i) + numSum(j) > threshold || flag[i][j] == 1) return 0;
flag[i][j] = 1;
return helper(i - 1, j, rows, cols, flag, threshold)
+ helper(i + 1, j, rows, cols, flag, threshold)
+ helper(i, j - 1, rows, cols, flag, threshold)
+ helper(i, j + 1, rows, cols, flag, threshold)
+ 1;
}
private int numSum(int i) {
int sum = 0;
do{
sum += i%10;
}while((i = i/10) > 0);
return sum;
}
}