1.best-time-to-buy-and-sell-stoc

问题描述:假设你有一个数组,其中第i个元素是某只股票在第i天的价格。
如果你最多只能完成一笔交易(即买一股和卖一股股票),设计一个算法来求最大利润。

解题思路:我的想法是
1.对于每一个结点求出能获益的最大利润,即i点后的最大值减去i点的值
2.在上述最大之中取最大

2.best-time-to-buy-and-sell-stoc ii

问题描述:假设你有一个数组,其中第i个元素表示某只股票在第i天的价格。
设计一个算法来寻找最大的利润。你可以完成任意数量的交易(例如,多次购买和出售股票的一股)。但是,你不能同时进行多个交易(即,你必须在再次购买之前卖出之前买的股票)。

解题思路:一开始我的思维不够灵活,把问题想得复杂了想先把第i天买入第j天卖出所获利润的表格算出来,从prices[i]最小值点必然买入或最大值点必然卖出的角度思考,贪心都不行,要动态规划才可能求出。网上看了别人的思路,才发现原来问题这样简单:

  • 只要第二天的价格高于第一天,就进行交易。(这样的话就默认可以同一天内先卖出再买进)

写出来代码比第一题还要短。

3.best-time-to-buy-and-sell-stoc iii

问题描述:假设你有一个数组,其中第i个元素是某只股票在第i天的价格。
设计一个算法来求最大的利润。你最多可以进行两次交易
注意:你不能同时进行多个交易(即,你必须在再次购买之前出售之前买的股票)。

解题思路:一开始被问题难住了,看了讨论之后又整理了思路。为不采用盲目包里搜索的方式,尽可能做有效的查找。因第一次交易本身对第二次交易有影响,所以“可进行两次交易”的求解方法不是传统分治法的分割,第二次交易要在第一次的基础上进行。而想要在一趟遍历中求出两次交易的最优利润,需要有一个整体的评价标准,每进行下一步操作都假设上一步操作已经完成,而此刻总资产已经可以算出。
(我觉得这实际上是动态规划的思想,此问题中k = 2,常见的动态规划问题k = n)
于是我们可设第一次买入后的总资产 buy1[i] = -prices[i]
第一次卖出后获得的利润为 sell1 = max(sell1, buy1 + prices[i])
第二次买入 buy2 = max(sell1, sell1 - prices[i])
第二次卖出 sell2 = max(sell2, buy2 + prices[i])

public int maxProfit(int[] prices) {
        int buy1 = -65535, sell1 = 0, buy2 = -65535, sell2 = 0;
        for(int i = 0; i < prices.length; i++) {
            buy1 = Math.max(buy1, -prices[i]);
            sell1 = Math.max(sell1, buy1 + prices[i]);//保证第一次买卖后资产最多
            buy2 = Math.max(buy2, sell1 - prices[i]);
            sell2 = Math.max(sell2, buy2 + prices[i]);//保证第二次买卖后资产最多
        }
           return sell2;
    }

4.spiral-matrix

问题描述:给定一个m x n大小的矩阵(m行,n列),按螺旋的顺序返回矩阵中的所有元素。
例如,给出以下矩阵:
[[ 1, 2, 3 ], [ 4, 5, 6 ], [ 7, 8, 9 ]]
你应该返回[1,2,3,6,9,8,7,4,5]。

解题思路
每次循环做一层(由外向内),需解决两个问题:每一层内部该怎么做?(最后剩下的)单独行/列怎样处理?
整体分为两步:
1.每一次循环做一层分为上下左右四条边
2.遇到一层不够完整一圈(结束条件)

public ArrayList<Integer> spiralOrder(int[][] matrix) {
        int k, i, j, m, n;
        ArrayList<Integer> reslist = new ArrayList<>();
        m = matrix.length;
        if(m==0)
            return reslist;
        n = matrix[0].length;

      //每一次循环做一层:从外向里
        int step = (int) Math.min(Math.ceil((double)m/2),Math.ceil((double)n/2));
        for(k = 0; k<step; k++){

            //判断结束
            if((n-2*k)==1 || (m-2*k)==1) {
                for(i = k; i<m-k; i++) {
                    for(j = k; j<n-k; j++)
                        reslist.add(matrix[i][j]);
                }
                return reslist;
            }

            //part1:上边
            for(i = k; i<n-k; i++)
                reslist.add(matrix[k][i]);
            //part2:右边
            for(i = k+1; i<m-k; i++)
                reslist.add(matrix[i][n-k-1]);
            //part3:下边
            for(i = n-k-2; i>=k; i--)
                reslist.add(matrix[m-k-1][i]);
            //part4:左边
            for(i = m-k-2; i>k; i--)
                reslist.add(matrix[i][k]);
        }
        //所有层刚好都是完整一圈
        return reslist;
    }

5.spiral-matrix ii

问题描述:给定一个整数n,将数字1到n^2按螺旋的顺序填入n×n的矩阵
例如:给出的n=3,你应该返回如下矩阵:
[[ 1, 2, 3 ],[ 8, 9, 4 ],[ 7, 6, 5 ]]

解题思路:还是上一题的思想,将add改为数组赋值语句,好像比上一题还要简单。

public int[][] generateMatrix(int n){
        int k, i, j;
        int[][] res = new int[n][n];

      //每一次循环做一层:从外向里
        int step = (int) Math.ceil((double)n/2);
        int num = 1;
        for(k = 0; k<step; k++){
            //part1:上边
            for(i = k; i<n-k; i++){
                res[k][i] = num;
                num++;
            }
            //part2:右边
            for(i = k+1; i<n-k; i++) {
                res[i][n-k-1] = num;
                num++;
            }
            //part3:下边
            for(i = n-k-2; i>=k; i--) {
                res[n-k-1][i] = num;
                num++;
            }
            //part4:左边
            for(i = n-k-2; i>k; i--) {
                res[i][k] = num;
                num++;
            }
        }
        return res;
    }

6.first-missing-positive

问题描述:给出一个无序的整数型数组,求不在给定数组里的最小的正整数.例如:
给出的数组为[1,2,0] 返回3,
给出的数组为[3,4,-1,1] 返回2.
你需要给出时间复杂度在O(n)之内并且空间复杂度为常数级的算法

解题思路:看到题目描述就觉得肯定很简单,没怎么仔细想就开始代码。结果写完的程序稀巴烂,像是筛子总是漏掉各种样例,啪啪打脸。
由题目要求,只能遍历常数次数组,需要在一层循环内完成搜索或者排序操作。我的思路是:遍历一遍数组采用交换的策略将元素归位,最终使得:小于A.length的元素K都归位于A[k-1]。那么如下问题就来了:

  • 大于A.length的元素怎样处理才不会影响下一步
  • 如果入到要交换的两个元素恰好相等,处理以保证不循环
  • 如果要交换的元素a恰好已归位(与本身交换),处理以保证不循环
  • 如果要交换后到a位置的元素自然归位,处理以保证不循环

有这么多补丁要打,我觉得只能说明一开始解决问题的逻辑就很垃圾。于是我看了通过的其他人的代码,也是交换的思想,但是出发点不同,他是像冒泡排序一样每一次都将第i位置的元素归位,但实际上完成了k次交换,0=<k<=n,虽然代码能够通过,但我觉得这并不是严格意义上的O(n)复杂度,最好O(n),最坏O(n^2),平均O(nlogn)。

public class Solution {
    public int firstMissingPositive(int[] A) {
        int n=A.length;
        for(int i=0;i<n;i++){
            while(A[i]>0&&A[i]<=n&&A[A[i]-1]!=A[i]){
                swap(A,i,A[i]-1);
            }
        }
         for(int i=0;i<n;i++){
            if(A[i]-1!=i){
                return i+1;
            }
        }
        return n+1;
    }
     public void swap(int[] A,int i,int j){
        int temp=A[i];
        A[i]=A[j];
        A[j]=temp;
    }
}

代码反思:即使觉得再简单的问题最开始也要想清楚步骤,用几层循环来完成,精确到是用for 还是 while,对于后续写程序处理具体细节真的很重要。有时用while()需要打很多补丁而for来写就会简洁清晰,反之亦然。
发现一个清晰代码满足O(n)的题目要求,贴在下面供自己学习,代码来自用户:湖人总冠军LA

链接:https://www.nowcoder.com/questionTerminal/ea7ca311264d4c579a1ef6c1f7f69138
来源:牛客网

import java.util.ArrayList;

public class Solution {
    /*解题思路:先将数组存入Arraylist中,因为这样遍历数组中的任何一个元素的时间变成O(1),即以空间换取时间。
    步骤:(1)遍历数组,找到最大值max,并将数组存入Arraylist中,时间复杂度为O(n)
         (2)遍历从正整数1到最大值max之间的数,并判断Arraylist中是否包含该数。若不包含该数字,则该数字就是第一个缺失的正整数;
         若循环走到底,即Arraylist包含了1到max之间所有数,则第一个缺失数字就是max后一个数字,即max+1.时间复杂度为O(n)*/
    public int firstMissingPositive(int[] A) {
        if(A.length == 0)
            return 1;
        ArrayList<Integer> list = new ArrayList<>();
        int max = 0;
            //步骤(1)
        for(int i = 0;i < A.length;i ++){
            list.add(A[i]);
            if(A[i] > max){
                max = A[i];
            }
        }
           //步骤(2)
        for(int i = 1;i <= max;i ++){
            if(!list.contains(i))
                return i;
        }
        return max + 1;
    }
}

7.search-a-2d-matrix

问题描述:请写出一个高效的在m*n矩阵中判断目标值是否存在的算法,矩阵具有如下特征:
每一行的数字都从左到右排序
每一行的第一个数字都比上一行最后一个数字大
例如:对于下面的矩阵:
[↵ [1, 3, 5, 7],↵ [10, 11, 16, 20],↵ [23, 30, 34, 50]↵]
要搜索的目标值为3,返回true;

解题思路:实现功能很简单只需要遍历一次矩阵,就能找出来,而题目的用意就是减少搜索的次数,尽可能查找最少的数组元素。整体需要两层嵌套循环实现,第一层循环判断该在矩阵的哪一行搜索元素,第二层循环进行搜索,第二层循环只会执行一次,达到减少搜索次数的目的。

public class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        boolean res = false;
        int m = matrix.length, n = matrix[0].length;
        if(target<matrix[0][0])    return res;
        for(int i = 0; i<m; i++){
            if(target>matrix[i][n-1])
                continue;
            for(int j = 0; j<n; j++){
               if(target==matrix[i][j])
                        return true;
                }
        }
        return res;
    }
}

代码反思:如果有很多补丁需要打的情况,需要从逻辑出发进行优化而不是哪漏补哪。


8.convert-sorted-array-to-binary-search-tree

问题描述:给出一个升序排序的数组,将其转化为平衡二叉搜索树(BST)

解题思路:做了链表部分的convert-sorted-list-to-binary-search-tree紧接着就锁了这道题,思路完全相同,只是对数据结构的处理方式有不同,链表只需给出子链的头结点,数组需要标记起止位置;也因此,在递归过程中的终止条件不同。

public class Solution {
    public TreeNode sortedArrayToBST(int[] num) {
        if(num.length == 0) return null;
        return buildBST(num, 0, num.length-1);
    }
    public TreeNode buildBST(int[] num,int left,int right){
        if(left==right)    
            return new TreeNode(num[left]);
        if(left>right)
            return null;
        //想要中间(或偏后)的结点
        int mid = (right+left)/2+(left+right)%2;
        TreeNode root = new TreeNode(num[mid]);
        root.left = buildBST(num, left, mid-1);
        root.right = buildBST(num, mid+1, right);
        return root;
    }
}

9.merge-sorted-array

问题描述:给出两个有序的整数数组A和B,请将数组B合并到数组A中,变成一个有序的数组
注意:可以假设A数组有足够的空间存放B数组的元素,A和B中初始的元素数目分别为m和n

解题思路:把A copy到C,然后再合并到A.不过题如果这么做毫无思维容量在里边,今天有点急也没仔细想,想敷衍了事。

public class Solution {
    public void merge(int A[], int m, int B[], int n) {
        int a = 0, b = 0, i;
        int[] C = new int[m];
        for(i = 0; i<m; i++){
            C[i] = A[i];
        }
        //合并BC 到A
        for(i = 0; i<m+n; i++){
            //bigger one place in A
            if(n==0)  break;
            if(m!=0 && a!=m &&(b==n || C[a]<B[b])){
                A[i] = C[a];
                a++;
            }
            else{
                A[i] = B[b];
                b++;
            }
        }
    }
}

代码反思:看了别人的解,都没有像我这种不动脑做题选手!大家都是从后往前处理,不需要另开空间。
这位 JacobGo! 大神又一次让我醍醐灌顶,我决定要追随大神的步伐,努力上进

/*
     * 最优解:从后往前处理,不需要开辟额外空间
     * Runtime: 0 ms.Your runtime beats 45.38 % of java submissions.
     */
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        int i = m - 1, j = n - 1, index = m + n - 1;
        while (i >= 0 && j >= 0)
            nums1[index--] = nums1[i] > nums2[j] ? nums1[i--] : nums2[j--];
        while (j >= 0)
            nums1[index--] = nums2[j--];
    }