• LeetCode----Array


    Remove Duplicates from Sorted Array
    思路:两个指针,头指针在0,尾指针从1开始寻找,找到第一个不等于头指针值的数,覆盖掉头指针后面那个数,然后尾指针往后移。

    public int removeDuplicates(int[] nums) {
        if(nums.length <= 1) return nums.length;
        int i = 0;
        for(int j = 1; j < nums.length; j++){
            if(nums[i] != nums[j]){
                nums[++i] = nums[j];
            }
        }
        return i + 1;
    }
    
    这种解法具有普遍性,具体看下一题
    public int removeDuplicates(int[] nums) {
        if(nums == null) return 0;
        int i = 0;
        int j = 0;
        while(j < nums.length){
            if(i < 1 || nums[i - 1] != nums[j]){
                nums[i] = nums[j];
                i++;
            }
            j++;
        }
        return i;
    }
    

    Remove Duplicates from Sorted ArrayII
    思路:同样使用两个指针,都从1开始,同时加上一个标志位,用于区分两个数重复还是多余两个数重复,可分为三种情况
    当前数和前一个数不等,则尾指针覆盖掉头指针,两个指针都后移,标志位置为1;
    当前数和前一个数相等且标志位为1,则尾指针覆盖掉头指针,两个指针都后移,标志位置为0;
    当前数和前一个数相等且标志位为0,则尾指针后移

    public int removeDuplicates(int[] nums) {
        if(nums.length == 0) return 0;
        int count = 1;
        boolean flag = true;
        for(int i = 1; i < nums.length; i++){
            if(nums[i] == nums[i - 1] && flag){
                nums[count] = nums[i];
                count++;
                flag = false;
            }
            else if(nums[i] == nums[i - 1] && !flag){
                continue;
            }
            else{
                nums[count] = nums[i];
                count++;
                flag = true;
            }
        }
        return count;
    }
    
    public int removeDuplicates(int[] nums) {
        if(nums == null) return 0;
        int i = 0;
        int j = 0;
        while(j < nums.length){
            if(i < 2 || nums[j] != nums[i - 2]){
                nums[i] = nums[j];
                i++;
            }
            j++;
        }
        return i;
    }
    

    Best Time to Buy and Sell Stock
    思路:dp[i]为第i天的最大利润,则dp[i] = max(dp[i - 1],prices[i] - min_Buy)

    public int maxProfit(int[] prices) {
        if(prices.length == 0) return 0;
        int profit = 0;
        int min_buy = prices[0];
        for(int i = 1; i < prices.length; i++){
            min_buy = Math.min(min_buy,prices[i]);
            profit = Math.max(profit,(prices[i] - min_buy));
        }
        return profit;
    }
    

    Best Time to Buy and Sell StockII
    思路:比较后一天和前一天的价格,只要后一天的贵,则选择买进

    public int maxProfit(int[] prices) {
        if(prices.length <= 1) return 0;
        int res = 0;
        
        for(int i = 1; i < prices.length; i++){
            if(prices[i] > prices[i - 1]){
                res += (prices[i] - prices[i - 1]);
            }
        }
        return res;
    }
    

    Best Time to Buy and Sell StockIII
    思路:dp[i]为在第i天前进行一次交易,第i天后进行一次交易得的总利润,即dp[i] = preProfit[i] + postProfit[i],preProfit[i]和postProfit[i]的算法和题1一样

    public int maxProfit(int[] prices) {
        int n = prices.length;
        if(n < 2) return 0;
        int[] preProfit = new int[n];
        int[] postProfit = new int[n];
        
        //计算第i天之前交易一次获得的最大利益
        int curMin = prices[0];
        preProfit[0] = 0;
        for(int i = 1; i < n; i++){
            curMin = Math.min(curMin,prices[i]);
            preProfit[i] = Math.max(preProfit[i - 1],prices[i] - curMin);
        }
        
        //计算第i天之后交易一次获得的最大利益
        int curMax = prices[n - 1];
        postProfit[n - 1] = 0;
        for(int i = n - 2; i >= 0; i--){
            curMax = Math.max(prices[i],curMax);
            postProfit[i] = Math.max(postProfit[i + 1],curMax - prices[i]);
        }
        
        int max = Integer.MIN_VALUE;
        for(int i = 0; i < n; i++){
            max = Math.max(max,preProfit[i] + postProfit[i]);
        }
        return max;
    }
    

    Game of Life
    思路:将题目中四个条件标记为四个状态即:
    活细胞周围有少于两个活细胞,则死亡 状态2
    活细胞周围有超过3个活细胞,则死亡 状态3
    活细胞周围有两个或三个活细胞,则继续存活 状态4
    死细胞周围有三个活细胞,则存活 状态5
    判断细胞周围的活细胞数目,即周围标记为1 2 3 4的都是活细胞,标记完以后,根据状态判断细胞是存活还是死亡,即状态4 5存活

    public void gameOfLife(int[][] board) {
        int m = board.length;
        if(m == 0) return;
        int n = board[0].length;
    
        for(int i = 0; i < m; i++){
            for(int j = 0; j < n; j++){
                if(board[i][j] == 1){
                    if(getNum(i,j,board) < 2) board[i][j] = 3;
                    else if(getNum(i,j,board) > 3) board[i][j] = 2;
                    else board[i][j] = 4;
                }
                else if(board[i][j] == 0){
                    if(getNum(i,j,board) == 3) board[i][j] = 5;
                }
            }
        }
        
        for(int i = 0; i < m; i++){
            for(int j = 0; j < n; j++){
                if(board[i][j] == 4 || board[i][j] == 5) board[i][j] = 1;
                else board[i][j] = 0;
            }
        }
        return;
    }    
    public int getNum(int i,int j,int[][] board){
        int m = board.length;
        int n = board[0].length;
        int count = 0;
        for(int p = i - 1; p < i + 2; p++){
            for(int q = j - 1; q < j + 2; q++){
                if(p >= 0 && p < m && q >= 0 && q < n){
                    if(p == i && q == j) continue;
                    else{
                        if(board[p][q] == 1 || board[p][q] == 2 || board[p][q] == 3 || board[p][q] == 4){
                            count++;
                        }
                    }
                }
            }
        }
        return count;
    }    
    

    Subsets
    思路:回溯,临界条件:element.length <= nums.length

    public List<List<Integer>> subsets(int[] nums) {
        List<List<Integer>> result = new ArrayList<List<Integer>>();
        List<Integer> element = new ArrayList<Integer>();
        int k = nums.length;
        if(k == 0) return result;
        result.add(element);
        
        for(int i = 1; i <= k; i++){
            getResult(nums,result,element,0,i);
        }
        return result;
    }
    
    void getResult(int[] nums,List<List<Integer>> result,List<Integer> element,int start,int k){
        if(element.size() == k){
            result.add(new ArrayList<Integer>(element));
            return;
        }
        
        for(int i = start;i < nums.length; i++){
            element.add(nums[i]);
            getResult(nums,result,element,i+1,k);
            element.remove(element.size() - 1);
        }
    }
    
    public List<List<Integer>> subsets(int[] nums) {
        List<List<Integer>> res = new ArrayList<List<Integer>>();
        List<Integer> ans = new ArrayList<Integer>();
        getResult(nums,res,ans,0);
        return res;
    }
    
    public void getResult(int[] nums,List<List<Integer>> res,List<Integer> ans,int start){
        res.add(new ArrayList<Integer>(ans));
        
        for(int i = start; i < nums.length; i++){
            ans.add(nums[i]);
            getResult(nums,res,ans,i + 1);
            ans.remove(ans.size() - 1);
        }
    }
    

    SubsetsII
    思路:和Subsets一样,但是需要去掉重复解决方案,即增加排序和过滤即可

    public List<List<Integer>> subsetsWithDup(int[] nums) {
        List<List<Integer>> result = new ArrayList<List<Integer>>();
        List<Integer> element = new ArrayList<Integer>();
        int k = nums.length;
        if(k == 0) return result;
        result.add(element);
        //排序很重要
        Arrays.sort(nums);
        
        for(int i = 1; i <= k; i++){
            getResult(nums,result,element,0,i);
        }
        return result;
    }
    
    void getResult(int[] nums,List<List<Integer>> result,List<Integer> element,int start,int k){
        if(element.size() == k){
            result.add(new ArrayList<Integer>(element));
            return;
        }
        
        for(int i = start;i < nums.length; i++){
            //将重复的解决方案过滤掉
            if(i != start && nums[i] == nums[i - 1]) continue;
            else{
                element.add(nums[i]);
                getResult(nums,result,element,i+1,k);
                element.remove(element.size() - 1);
            }
        }
    }
    

    Word Search
    思路:回溯法,构造一个访问数组,访问过标记为true,未访问标记为false,若朝四个方向查找均未找到,则不存在

    public boolean exist(char[][] board, String word) {
        if(word.length() == 0) return false;
        int m = board.length;
        if(m == 0) return false;
        int n = board[0].length;
        
        boolean[][] visited = new boolean[m][n];
        for(int i = 0; i < m; i++){
            for(int j = 0; j < n; j++){
                visited[i][j] = false;
            }
        }
        
        for(int i = 0; i < m; i++){
            for(int j = 0; j < n; j++){
                if(board[i][j] == word.charAt(0)){
                    visited[i][j] = true;
                    if(word.length() == 1 || search(board,word.substring(1),i,j,visited)) return true;
                    visited[i][j] = false;
                }
            }
        }
        return false;
    }
    
    public boolean search(char[][] board,String word,int i,int j,boolean[][] visited){
        int[][] direction = new int[4][2];
        direction[0][0] = direction[1][0] = direction[2][1] = direction[3][1] = 0;
        direction[0][1] = direction[2][0] = 1;
        direction[1][1] = direction[3][0] = -1;
        for(int p = 0; p < 4; p++){
            int ii = i + direction[p][0];
            int jj = j + direction[p][1];
            if(ii >= 0 && ii < board.length && jj >= 0 && jj < board[0].length && !visited[ii][jj] && board[ii][jj] == word.charAt(0)){
                visited[ii][jj] = true;
                if(word.length() == 1 || search(board,word.substring(1),ii,jj,visited)) return true;
                visited[ii][jj] = false;
            }
        }
        return false;
    }
    

    Spiral Matrix
    思路:记录待处理矩阵左上角和右下角的坐标,每次处理一圈,直到处理完

    public List<Integer> spiralOrder(int[][] matrix) {
        List<Integer> list = new ArrayList<Integer>();
        int row = matrix.length;
        if(row == 0) return list;
        int col = matrix[0].length;
    
        int x = 0;
        int y = 0;
        while(row >= 1 && col >= 1){
            int m = x + row - 1;
            int n = y + col - 1;
            for(int i = y; i <= n; i++){
                list.add(matrix[x][i]);
            }
            
            for(int i = x + 1; i <= m - 1; i++){
                list.add(matrix[i][n]);
            }
            
            if(row > 1){
                for(int i = n; i >= y; i--){
                    list.add(matrix[m][i]);
                }
            }
            
            if(col > 1){
                for(int i = m - 1; i > x; i--){
                    list.add(matrix[i][y]);
                }
            }
            row -= 2;
            col -= 2;
            x++;
            y++;
        }
        return list;
    }
    

    Maximum Subarray
    思路:动态规划,判断前面数的加和是否大于0,sum = (sum < 0) ? nums[i] : (nums[i] + sum);然后每次更新max:max = Math.max(max,sum);

    public int maxSubArray(int[] nums) {
        if(nums.length == 0) return -1;
        if(nums.length == 1) return nums[0];
        int max = nums[0];
        int sum = nums[0];
        for(int i = 1; i < nums.length; i++){
            sum = (sum < 0) ? nums[i] : (nums[i] + sum);
            max = Math.max(max,sum);
        }
        return max;
    }
    

    Combination Sum
    思路:需要计算解决方案中所有数的和,即构造一个参数sum与target比较

    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<List<Integer>> res = new ArrayList<List<Integer>>();
        List<Integer> ans = new ArrayList<Integer>();
        Arrays.sort(candidates);
        getResult(res,ans,candidates,0,0,target);
        return res;
    }
    
    public void getResult(List<List<Integer>> res,List<Integer> ans,int[] candidates,int start,int sum,int target){
        if(sum == target){
            res.add(new ArrayList<Integer>(ans));
            return;
        }
        else if(sum > target) return;
        else{
            for(int i = start; i < candidates.length; i++){
                sum += candidates[i];
                ans.add(candidates[i]);
                getResult(res,ans,candidates,i,sum,target);
                ans.remove(ans.size() - 1);
                sum -= candidates[i];
            }
        }
        return;
    } 
    

    Combination SumII(需要去除重复解决方案)和Combination SumIII(需要判断解决方案数组长度)思路同Combination Sum

    Find the Duplicate Number
    Given an array nums containing n + 1 integers where each integer is between 1 and n (inclusive), prove that at least one duplicate number must exist. Assume that there is only one duplicate number, find the duplicate one.

    Note:
    You must not modify the array (assume the array is read only).
    You must use only constant, O(1) extra space.
    Your runtime complexity should be less than O(n2).
    There is only one duplicate number in the array, but it could be repeated more than once.
    思路:本题可以先排序然后遍历得出结果,但是题目要求不能改变原数组,因此不能排序,则采用二分查找的变种算法:我们在区别[1, n]中搜索,首先求出中点mid,然后遍历整个数组,统计所有小于等于mid的数的个数,如果个数小于mid,则说明重复值在[mid+1, n]之间,反之,重复值应在[1, mid-1]之间,然后依次类推,直到搜索完成,此时的low就是我们要求的重复值

    public int findDuplicate(int[] nums) {
        int m = 0;
        int n = nums.length - 1;
        while(m <= n){
            int mid = m + (n - m) / 2;
            int cnt = 0;
            for(int i = 0; i < nums.length; i++){
                if(nums[i] <= mid) cnt++;
            }
            if(cnt > mid) n = mid - 1;
            else m = mid + 1;
        }
        return m;
    }
    

    Median of Two Sorted Arrays
    思路:用快速排序的方法找第k大的数,可是题目要求时间复杂度为O(log(m + n)),于是只能在两个有序数组中使用二分查找

    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int m = nums1.length;
        int n = nums2.length;
        return (findKNum(nums1,nums2,(m + n + 1) / 2) + findKNum(nums1,nums2,(m + n + 2) / 2)) / 2.0;
    }
    
    public int findKNum(int[] nums1,int[] nums2,int k){
        int m = nums1.length;
        int n = nums2.length;
        if(m > n) return findKNum(nums2,nums1,k);
        if(m == 0) return nums2[k - 1];
        if(k == 1) return Math.min(nums1[0],nums2[0]);
        int i = Math.min(m,k / 2);
        int j = Math.min(n,k / 2);
        if(nums1[i - 1] < nums2[j - 1]) return findKNum(Arrays.copyOfRange(nums1,i,m),nums2,k - i);
        else return findKNum(nums1,Arrays.copyOfRange(nums2,j,n),k - j);
    }
    
    典型的归并排序
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int m = nums1.length;
        int n = nums2.length;
        int[] res = new int[m + n];
        merge(nums1,nums2,res);
        if((m + n) % 2 == 1) return (double)res[(m + n - 1) / 2];
        else return ((double)res[(m + n - 1) / 2] + res[(m + n) / 2]) / 2;
    }
    
    public void merge(int[] nums1,int[] nums2,int[] res){
        int m = nums1.length;
        int n = nums2.length;
        int left = 0;
        int right = 0;
        int i = 0;
        while(left < m && right < n){
            if(nums1[left] < nums2[right]){
                res[i++] = nums1[left++];
            }
            else{
                res[i++] = nums2[right++];
            }
        }
        while(left < m){
            res[i++] = nums1[left++];
        }
        while(right < n){
            res[i++] = nums2[right++];
        }
    }
    

    Trapping Rain Water
    思路:找到左边最高水柱和右边最高水柱,两水柱低的那个减去自身水柱就是积攒的面积

    public int trap(int[] height) {
        int n = height.length;
        if(n == 0) return 0;
        int[] left = new int[n];
        int[] right = new int[n];
        
        //计算坐标i左边最高的水柱
        left[0] = height[0];
        for(int i = 1; i < n; i++){
            left[i] = Math.max(left[i - 1],height[i]);
        }
        
        //计算坐标i右边最高的水柱
        right[n - 1] = height[n - 1];
        for(int i = n - 2; i >= 0; i--){
            right[i] = Math.max(right[i + 1],height[i]);
        }
        
        int water = 0;
        for(int i = 0; i < n; i++){
            water += (Math.min(left[i],right[i]) - height[i]);
        }
        return water;
    }
    

    Jump Game II
    思路:动态规划,构造一个dp[n],dp[i]表示到达i所需要的最小跳数

    public int jump(int[] nums) {
        int n = nums.length;
        if(n == 0) return Integer.MAX_VALUE;
        //dp[i]表示到达索引i所需要的最小跳数
        int[] dp = new int[n];
        dp[0] = 0;
        for(int i = 1; i < n; i++){
            dp[i] = Integer.MAX_VALUE;
        }
        for(int i = 1; i < n; i++){
            for(int j = 0; j < i; j++){
                if(j + nums[j] >= i){
                    int temp = dp[j] + 1;
                    if(temp < dp[i]){
                        dp[i] = temp;
                        break;
                    }
                    //由于dp是一个递增序列,因此上面做法可以减少大量的计算
                    //dp[i] = (dp[i] < dp[j] + i) ? dp[i] : dp[j] + 1;
                }
            }
        }
        return dp[n - 1];
    }
    

    Longest Consecutive Sequence
    思路:遍历查找当前数升序和降序的个数之和,由于同一序列的个数一定相同,则找到的可以直接删掉以减少时间复杂度

    public int longestConsecutive(int[] nums) {
        HashSet<Integer> set = new HashSet<Integer>();
        for(int i = 0; i < nums.length; i++){
            set.add(nums[i]);
        }
        int sum = Integer.MIN_VALUE;
        
        for(int i = 0; i < nums.length; i++){
            //升序个数
            int upCnt = findNum(set,nums[i],true);
            //降序个数
            int downCnt = findNum(set,nums[i] - 1,false);
            sum = Math.max(sum,upCnt + downCnt);
        }
        return sum;
    }
    
    public int findNum(HashSet<Integer> set,int num,boolean flag){
        int cnt = 0;
        while(set.contains(num)){
            cnt++;
            set.remove(num);
            if(flag) num++;
            else num--;
        }
        return cnt;
    }
    

    414 排序以后找到第三大的数,考虑数组长度和有重复的数 时间复杂度:O(nlogn)
    396 先遍历计算出F(0)和sum,F(n + 1) = F(n) + sum - length * RotateNumber,寻找最大的F
    283 两个指针,后指针等于0则往后移,不等于0则覆盖前指针,两指针一起往后移,然后将数组后面补0
    217 HashSet 无序不重复
    219/169 HashMap key不重复
    189 将后面k个元素拿出来放到前面 判断k % length
    118/119 递推式:list.get(i).get(j) = list.get(i - 1).get(j - 1) + list.get(i - 1).get(j)
    88/75 快速排序
    26/27 思路同283
    268 排序后判断索引是否与值相等
    64 动态规划:grid[i][j] = min(grid[i][j] + grid[i - 1][j],grid[i][j] + grid[i][j - 1])
    162 遍历,考虑特殊情况
    153/154/81/35/33 遍历
    120 动态规划:triangle[i][j] = min(triangle[i][j] + triangle[i - 1][j],triangle[i][j] + triangle[i - 1][j - 1]),注意java中对list某元素赋值只能算完加法运算后然后set,只有数组才能+=
    268/41 排序后遍历
    74 从右上角开始查找,小就往下,大就往左
    73 首先设置两个标志位判断第一行和第一列是否有0,然后遍历整个矩阵将0都映射到第一行和第一列,最后将0的对应行和列都置为0,若第一行和第一列的标志位为true则将其整行或整列都置为0
    62 动态规划:res[i][j] = res[i][j - 1] + res[i - 1][j];
    55 动态规划:构造一个余力值数组,记录还有多少余力 canWalk[i] = Math.max(canWalk[i - 1],nums[i - 1]) - 1;
    167 Two Points
    11 Container With Most Water 动态规划:两个指针依次计算面积,长度小的指针往长度高的指针方向移,更新面积

    Plus One--->通过%10和/10记录当前数和进位比判断9更有效率
    3Sum Closest
    Product of Array Except Self
    Majority Element II
    Maximum Product Subarray
    Construct Binary Tree from Preorder and Inorder Traversal
    Unique Paths II
    Spiral Matrix II
    Summary Ranges
    Minimum Size Subarray Sum
    Search for a Range
    Next Permutation(首先从后往前找到第一个降序的索引i,然后找到后面比nums[i]大的最小的数,将它们调换位置,然后将后面的序列翻转)

    Rotate Image:将方针顺时针旋转90度:matrix[j][n - i - 1] = copy[i][j];

  • 相关阅读:
    python wsdl connection refused 111
    我要学算法
    linux 定时任务
    mysql语句
    Firefox配置Fiddler
    windows下安装spynner
    做一个完整的项目需要技能
    快速排序
    《实时控制软件设计》总结
    asp实现在微信jsdk分享从a页面跳转到b页面然后分享后点开又回a页面
  • 原文地址:https://www.cnblogs.com/LeonNew/p/6007990.html
Copyright © 2020-2023  润新知