1.
思路一:双栈的思路,就是一个valuestack,来存放值得,不管最小值。另一个是minstack,来存放最小值的
20ms
class MinStack {
Deque<Integer> A, B;
public MinStack() {
A = new LinkedList<Integer>();
B = new LinkedList<Integer>();
}
public void push(int x) {
A.push(x);
if(B.isEmpty() || B.peek() >= x)
B.push(x);
}
public void pop() {
if(A.pop().equals(B.peek()))
B.pop();
}
public int top() {
return A.peek();
}
public int min() {
return B.peek();
}
}
思路二:非常难想到,但是速度挺快 17ms 使用链表的表结构来表示这个栈,把这个最小值作为表结构里面的一个属性
class MinStack {
private Node head;
public MinStack() {
}
public void push(int x) {
if (head == null)
head = new Node(x, x, null);
else
head = new Node(x, Math.min(head.min, x), head);
}
public void pop() {
head = head.next;
}
public int top() {
return head.val;
}
public int min() {
return head.min;
}
private class Node {
int val;
int min;
Node next;
public Node(int val, int min, Node next) {
this.val = val;
this.min = min;
this.next = next;
}
}
}
2.
我的解法:0ms 实际上就是链表删除
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode deleteNode(ListNode head, int val) {
ListNode tmp = head;
while(head != null){
if(head.val == val){
tmp = head.next;
head.next =null;
return tmp;
}else if(head.next.val == val){
head.next = head.next.next;
return tmp;
}else{
head = head.next;
}
}
return tmp;
}
}
3.
递归:1ms 思路因为每一个点左右子树最终的层数相减的绝对值都不能大于1,所以依次就行递归
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
boolean tmp = true; ;
public boolean isBalanced(TreeNode root) {
if(root == null) return true;
height(root);
return tmp;
}
public int height(TreeNode root){
if(root == null) return 0;
int left = height(root.left);
int right = height(root.right);
if(Math.abs(left-right) > 1){
tmp = false;
}
return Math.max(left,right)+1;
}
}
4.
这道题乍一看很容易,但其实有难度的,我一开始想到的是HashMap,key存放字符,value存放的是出现次数,然后遍历这个map,第一个出现value值为1,那就是这个字符,但是发现HashMap是无序的,第一出现value值为1不一定是第一个出现一次的字符。这就很麻烦了,后来发现LinkedHashMap完美地解决了这个问题
解法一:LinkedHashMap 37ms 这个思路就是简单但是效率低
class Solution {
public char firstUniqChar(String s) {
Map<Character, Integer> map = new LinkedHashMap<>();
for(int i=0; i<s.length(); i++){
map.put(s.charAt(i), map.getOrDefault(s.charAt(i),0)+1);
}
for(char sss : map.keySet()){
if(map.get(sss) == 1) return sss;
}
return ' ';
}
}
解法二 难以想到但是效率很高
class Solution {
public char firstUniqChar(String s) {
int[] count = new int[26];
for(char c : s.toCharArray()){
count[c - 'a']++;
}
for(char c : s.toCharArray()){
if(count[c - 'a'] == 1) return c;
}
return ' ';
}
}
4.
解法一:递归
具体的实现思路,这个大佬已经写得非常详细和准确,直接转载过来。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public boolean isSymmetric(TreeNode root) {
if(root == null) return true;
boolean tmp = helper(root.left,root.right);
return tmp;
}
public boolean helper(TreeNode L,TreeNode R){
if(L == null && R == null) return true;
if(L == null || R == null || L.val != R.val) return false;
return helper(L.left,R.right) && helper(L.right,R.left);
}
}
方法二:层次遍历法 非递归
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
public class Solution {
public boolean isSymmetric(TreeNode root) {
if (root == null) {
return true;
}
// 实现类不能选用 ArrayDeque,因为该类的 add 方法会对添加的元素做非空检查
Deque<TreeNode> deque = new LinkedList<>();
deque.addFirst(root.left);
deque.addLast(root.right);
while (!deque.isEmpty()) {
TreeNode leftNode = deque.removeFirst();
TreeNode rightNode = deque.removeLast();
if (leftNode == null && rightNode == null) {
continue;
}
if (leftNode == null || rightNode == null) {
return false;
}
if (leftNode.val != rightNode.val) {
return false;
}
deque.addFirst(leftNode.right);
deque.addFirst(leftNode.left);
deque.addLast(rightNode.left);
deque.addLast(rightNode.right);
}
return true;
}
}
5.
1.我的思路就比较复杂,(N-1)*(N-1)的复杂度,输出数组上每一个数都要遍历一遍原来的数组。
2.转载一个大佬的思路它只遍历两遍
class Solution {
public int[] constructArr(int[] a) {
if (Objects.isNull(a) || a.length == 0) {
return new int[]{};
}
int[] res = new int[a.length];
int left = 1;
for (int i = 0; i < res.length; i++) {
res[i] =left;
left *= a[i];
}
int right = 1;
for (int i = res.length - 1; i >= 0; i--) {
res[i] *= right;
right *= a[i];
}
return res;
}
}
6.
解法:动态规划,下面代码是我复现大佬的思想的
class Solution {
public int maxSubArray(int[] nums) {
int [] dp = new int[nums.length];
dp[0]= nums[0];
int res = nums[0];
for(int i=1;i<nums.length;i++){
if(dp[i-1]>0){
dp[i] = dp[i-1] + nums[i];
}else{
dp[i] = nums[i];
}
}
for(int i=0;i<nums.length;i++){
if(dp[i] > res){
res = dp[i];
}
}
return res;
}
}
但看了大佬的代码,发现更是简单
class Solution {
public int maxSubArray(int[] nums) {
int res = nums[0];
for(int i = 1; i < nums.length; i++) {
nums[i] += Math.max(nums[i - 1], 0);
res = Math.max(res, nums[i]);
}
return res;
}
}
7.
解法1:先排序,然后把最前面的K个数输出。这里排序就有很多种了
1.使用Arrays.sort方法,这办法就是简单,速度不慢,因为该办法使用的是快速排序。
2.使用堆排序
3.快速排序
除了1方法外,其余的都需要自己去手写排序算法,我估计在面试时可能不会让你使用方法1,而是让你手写,所以几种排序方法,先慢慢练着去写。
下面就是手写堆排序,16ms,速度还可以。
class Solution {
public int[] getLeastNumbers(int[] a, int k) {
int [] arr = Arrays.copyOf(a,a.length);
int len = a.length;
buildMaxHeap(arr,len);
for(int i = len - 1;i>0;i--){
swap(arr,0,i);
len--;
heapify(arr,0,len);
}
int [] res = new int[k];
for(int j=0;j<k;j++){
res[j] = arr[j];
}
return res;
}
void buildMaxHeap(int[] arr,int len){//创建一个最大堆
for(int i = (int)Math.floor(len/2);i>=0;i--){
heapify(arr,i,len);
}
}
void heapify(int[] arr,int i,int len){//堆化,就是乱序堆变成最大堆
int left = 2*i+1;
int right = 2*i+2;
int largest = i;
if(left <len && arr[left] > arr[largest]){
largest = left;
}
if(right <len && arr[right] > arr[largest]){
largest = right;
}
if(largest != i){
swap(arr,largest,i);
heapify(arr,largest,len);
}
}
void swap(int[] arr,int i,int j ){//用于交换的方法
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}
使用sort()方法
class Solution {
public int[] getLeastNumbers(int[] arr, int k) {
Arrays.sort(arr);
int[] res = new int[k];
for (int i = 0; i < k; i++)
res[i] = arr[i];
return res;
}
}
手写快排:速度和sort()方法几乎一样,但必须要会,万一面试需要你手写呢
class Solution {
public int[] getLeastNumbers(int[] arr, int k) {
int left = 0;
int right = arr.length -1;
quickSort(arr,left,right);
int [] res = new int[k];
for(int j=0;j<k;j++){
res[j] = arr[j];
}
return res;
}
public void quickSort(int [] arr,int left,int right) {
int pivot = 0;
if(left < right) {
pivot = partition(arr,left,right);
quickSort(arr,left,pivot-1);
quickSort(arr,pivot+1,right);
}
}
private static int partition(int[] arr,int left,int right) {
int key = arr[left];
while(left < right) {
while(left < right && arr[right] >= key) {
right--;
}
arr[left] = arr[right];
while(left < right && arr[left] <= key) {
left++;
}
arr[right] = arr[left];
}
arr[left] = key;
return left;
}
}
解法二:一个大佬的解法,速度非常快2ms
先创建一个大小为10001的数组line(随便起的名字),默认值是0不用管。
遍历arr,每次将arr[i]的值作为line的下标,然后line[arr[i]]++。这样遍历完后,line这个数组存放的就是arr中每个值存放的次数。如arr=[3,2,1]。那么最后line=[0,1,1,1,0.,0..],又如arr=[0,1,2,1],则line=[1,2,1,0....]
class Solution {
public int[] getLeastNumbers(int[] arr, int k) {
if(k ==0) return new int[0];
int[] line = new int[10001];
int[] res = new int[k];
for(int i=0;i<arr.length;i++){
line[arr[i]]++;
}
int count =0;
for(int i=0;i<line.length;i++){
if(line[i]>0){
int n = line[i];
while(n>0){
res[count++] = i;
n--;
k--;
if(k == 0) return res;
}
}
}
return res;
}
}
8.
我的解法:超出了时间限制
class Solution {
public int lastRemaining(int n, int m) {
int count = 0;
if(n==1) return 0;
Queue<Integer> queue = new LinkedList<Integer>();
for(int i=0;i<n;i++){//添加元素
queue.offer(i);
}
while(queue.size() != 1){
int element = queue.poll();
count++;
if(count == m){
count = 0;
}else{
queue.offer(element);
}
}
return queue.poll();
}
}
解法一:在我基础上修改,不再计数,也不再依次弹出后,再加到队列最后面。而是使用ArrayList,因为每次删除掉的元素的下表都是有规律的,第一个被删除的元素下标为(m-1)%list.size并且记为c ,之后被删除的元素下标为(c+m-1)%list.size。
class Solution {
public int lastRemaining(int n, int m) {
if(n==0||m==0)
return -1;
List<Integer> list=new ArrayList<>();
for(int i=0;i<n;i++)
list.add(i);
int c=(m-1)%n;
while(list.size()!=1) {
list.remove(c);
c=(c+m-1)%list.size();
}
return list.get(0);
}
}
解法二和三:参考一个大佬的思路,运用数学思想,一步一步递归,思路如下:
这样这个思路可以用递归来逐步退出,也可以用迭代(动态规划)来解。
//迭代
public int lastRemaining(int n, int m) {
int flag = 0;
for (int i = 2; i <= n; i++) {
flag = (flag + m) % i;
//动态规划的思想,将f(n,m)替换成flag存储
}
return flag;
}
//递归
public int lastRemaining(int n, int m){
if(n < 1 || m < 1)
return -1;
if(n == 1)
return 0;
return (lastRemaining(n-1, m) + m) % n;
}
9.
解法:使用二进制的异或和位运算
class Solution {
public int add(int a, int b) {
while (a != 0) {//所以循环结束条件就是直到不存在进位了,那temp就是最终结果
int temp = a ^ b;//代表没有进位的加法
a = (a & b) << 1; //代表进位
b = temp; //不停循环代表:把没有进位的加上进位不就是原数了嘛
}
return b;
}
}
10.
这道题比较有难度的,有的用是动态规划的思路,我是完全没有做出来。
class Solution {
public double[] twoSum(int n) {
int [][]dp = new int[n+1][6*n+1];
//边界条件
for(int s=1;s<=6;s++)dp[1][s]=1;
for(int i=2;i<=n;i++){
for(int s=i;s<=6*i;s++){
//求dp[i][s]
for(int d=1;d<=6;d++){
if(s-d<i-1)break;//为0了
dp[i][s]+=dp[i-1][s-d];
}
}
}
double total = Math.pow((double)6,(double)n);
double[] ans = new double[5*n+1];
for(int i=n;i<=6*n;i++){
ans[i-n]=((double)dp[n][i])/total;
}
return ans;
}
}