刷题笔记:29th January

/**************************Dynamic Programming**************************/


53. Maximum Subarray


Find the contiguous subarray within an array (containing at least one number) which has the largest sum.


For example, given the array [-2,1,-3,4,-1,2,1,-5,4],
the contiguous subarray [4,-1,2,1] has the largest sum = 6.


自己的方法:


public class Solution {
    public int maxSubArray(int[] nums) {
        //extreme case
        if(nums == null || nums.length == 0) {
            return 0;
        }
        
        /*define the sumArray*/
        int[] sumArray = new int[nums.length];
        sumArray[0] = nums[0]; 
        
        /*define the biggestSum*/
        int[] biggestSum = new int[sumArray.length];
        biggestSum[0] = sumArray[0];
        
        for(int index = 1; index < sumArray.length; index++) {
            sumArray[index] = nums[index] + sumArray[index - 1];
        }
        
        for(int index = 1; index < biggestSum.length; index++) {
            int tmpBiggest = sumArray[index];
            for(int t = index - 1; t > -1; t--){
                if(sumArray[index] - sumArray[t] > tmpBiggest){//错误1
                    tmpBiggest = sumArray[index] - sumArray[t];
                }
            }
            biggestSum[index] = tmpBiggest;
        }
        
        Arrays.sort(biggestSum);
        return biggestSum[biggestSum.length-1];
    }
}

做错的地方:把大于号的右边写了sumArray[index],其实应该和目前最大的结果比。


但这方法到了最后存在time exceed 的问题,证明还有更加快的方法。网上的参考答案:


这一题应该把sub problem定义成到某个index为止最大的sub array sum。然后求index = length - 1的时候的值。注意,你想知道到当前长度为止的子数组的最大和,只需要知道前一个的子数组最大和,再考虑加上当前的值。是加上前一个比较大还是只有当前值比较大?所以,新的答案应该是:


public class Solution {
    public int maxSubArray(int[] nums) {
        if (nums == null || nums.length == 0){
            return 0;
        }
        
        int[] maxSumArray = new int[nums.length];
        maxSumArray[0] = nums[0];
        
        for(int index = 1; index < maxSumArray.length; index++){
            maxSumArray[index] = nums[index] + (maxSumArray[index - 1] > 0 ? maxSumArray[index - 1] : 0);
        }
        
        Arrays.sort(maxSumArray);
        return maxSumArray[maxSumArray.length - 1];
    }
}

反思:动态规划的关键是寻找缩小的子问题。通常,拿到题目的第一个思考方向应该是把原来的问题照样变小。


121. Best Time to Buy and Sell Stock(*)


Say you have an array for which the ith element is the price of a given stock on day i.


If you were only permitted to complete at most one transaction (ie, buy one and sell one share of the stock), design an algorithm to find the maximum profit.


Example 1:
Input: [7, 1, 5, 3, 6, 4]
Output: 5


max. difference = 6-1 = 5 (not 7-1 = 6, as selling price needs to be larger than buying price)
Example 2:
Input: [7, 6, 4, 3, 1]
Output: 0


In this case, no transaction is done, i.e. max profit = 0.


直接的方法:


public class Solution {
    public int maxProfit(int[] prices) {
        if(prices == null || prices.length == 0) {
            return 0;
        }
        
        //the profit array
        int[] profitArray = new int[prices.length];
        int tmpMax = 0;
        int tmpDiff = 0;
        
        for(int i = 0; i < profitArray.length; i++) {
            tmpMax = 0;
            tmpDiff = 0;
            for(int j = i + 1; j < profitArray.length; j++) {
                tmpDiff = prices[j] - prices[i];
                if (tmpDiff > tmpMax) {
                    tmpMax = tmpDiff;
                }
            }
            profitArray[i] = tmpMax;
        }
        
        Arrays.sort(profitArray);
        if (profitArray[profitArray.length - 1] < 0) {
            return 0;
        }
        
        return profitArray[profitArray.length - 1];
    }
}


然而,上述方法存在Time exceed limit的问题。考虑有可能是因为快速排序的问题。直接使用变量同样遇到time exceed的问题。


public class Solution {
    public int maxProfit(int[] prices) {
        if(prices == null || prices.length == 0) {
            return 0;
        }
        
        //the profit variables
        int finalMax = 0;
        int tmpMax = 0;
        int tmpDiff = 0;
        
        for(int i = 0; i < prices.length; i++) {
            tmpMax = 0;
            tmpDiff = 0;
            for(int j = i + 1; j < prices.length; j++) {
                tmpDiff = prices[j] - prices[i];
                if (tmpDiff > tmpMax) {
                    tmpMax = tmpDiff;
                }
            }
            finalMax = Math.max(finalMax, tmpMax);
        }
        
        return finalMax;
    }
}

那么参考答案中提到了: Kadane's Algorithm。


The logic to solve this problem is same as "max subarray problem" using Kadane's Algorithm. Since no body has mentioned this so far, I thought it's a good thing for everybody to know.


All the straight forward solution should work, but if the interviewer twists the question slightly by giving the difference array of prices, Ex: for {1, 7, 4, 11}, if he gives {0, 6, -3, 7}, you might end up being confused.


Here, the logic is to calculate the difference (maxCur += prices[i] - prices[i-1]) of the original array, and find a contiguous subarray giving maximum profit. If the difference falls below 0, reset it to zero.


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

/**************************DFS 寻找所有组合(子集)的题目**************************/


78. Subsets (**)


Given a set of distinct integers, nums, return all possible subsets.


Note: The solution set must not contain duplicate subsets.


For example,
If nums = [1,2,3], a solution is:


[
  [3],
  [1],
  [2],
  [1,2,3],
  [1,3],
  [2,3],
  [1,2],
  []
]


public class Solution {
    private void findSubset(List<List<Integer>> ans, List<Integer> tmp, int[] nums, int start){
        ans.add(new ArrayList<Integer>(tmp));
        for(int i = start; i < nums.length; i++){
            tmp.add(nums[i]);
            findSubset(ans, tmp, nums, i+1);
            tmp.remove(tmp.size() - 1);
        }
    }
    
    public List<List<Integer>> subsets(int[] nums) {
        //extreme cases
        if (nums == null || nums.length == 0){
            return null;
        }
        List<List<Integer>> answer = new ArrayList<List<Integer>>();
        List<Integer> tmp = new ArrayList<Integer>();
        findSubset(answer, tmp, nums, 0);
        return answer;
    }
}


参考答案,非递归版本(使用非位运算):


// Non Recursion
class Solution {
    /**
     * @param S: A set of numbers.
     * @return: A list of lists. All valid subsets.
     */
    public ArrayList<ArrayList<Integer>> subsets(int[] nums) {
        ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();
        int n = nums.length;
        Arrays.sort(nums);
        
        // 1 << n is 2^n
        // each subset equals to an binary integer between 0 .. 2^n - 1
        // 0 -> 000 -> []
        // 1 -> 001 -> [1]
        // 2 -> 010 -> [2]
        // ..
        // 7 -> 111 -> [1,2,3]
        for (int i = 0; i < (1 << n); i++) {
            ArrayList<Integer> subset = new ArrayList<Integer>();
            for (int j = 0; j < n; j++) {
                // check whether the jth digit in i's binary representation is 1
                if ((i & (1 << j)) != 0) {
                    subset.add(nums[j]);
                }
            }
            result.add(subset);
        }
        
        return result;
    }
}


90. Subsets II


Given a collection of integers that might contain duplicates, nums, return all possible subsets.


Note: The solution set must not contain duplicate subsets.


For example,
If nums = [1,2,2], a solution is:


[
  [2],
  [1],
  [1,2,2],
  [2,2],
  [1,2],
  []
]


这道题与上一道题的不同在于要考虑重复元素。所以,只要考虑到了去重。其基本步骤和上面的题目没什么区别。这道题自己做的时候,去重去错了。自己做的版本是:


if (i < nums.length -1) {
                if (nums[i] == nums[i + 1]){
                    continue;
                }
            }

这样会导致输出的结果少了包含两个重复元素的组合。为什么会这样呢?以[1,2,2]为例,如果用上述的去重方法,会输出:


【】、【1】、【1,2】、【2】。这是因为remove函数在循环体内曾经remove空了tmp,这是不应该出现的。而如果选择正确的去重方法,是通过i > start这个去保证至少有一个包含重复元素的组合。


所以,正常的应该是这样做:


public class Solution {
    private void findSubset(List<List<Integer>> ans, List<Integer> tmp, int[] nums, int start) {
        ans.add(new ArrayList<Integer>(tmp));
        
        for(int i = start; i < nums.length; i++) {
            if (i > start) {
                if (nums[i] == nums[i - 1]){
                    continue;
                }
            }
            
            tmp.add(nums[i]);
            findSubset(ans, tmp, nums, i + 1);
            tmp.remove(tmp.size() - 1);
        }
    }
    
    
    public List<List<Integer>> subsetsWithDup(int[] nums) {
        if (nums == null || nums.length == 0) {
            return null;
        }
        // let the duplicates beside each other
        Arrays.sort(nums);
        
        List<List<Integer>> answer = new ArrayList<List<Integer>>();
        List<Integer> tmp = new ArrayList<Integer>();
        
        findSubset(answer,tmp,nums,0);
        return answer;
    }
}

在循环内,输出的结果是:【】,【1】,【1,2】,【1,2,2】,【2】,【2,2】

46. Permutations


Given a collection of distinct numbers, return all possible permutations.


For example,
[1,2,3] have the following permutations:
[
  [1,2,3],
  [1,3,2],
  [2,1,3],
  [2,3,1],
  [3,1,2],
  [3,2,1]
]


这道题和上述的找子集的题类似。唯一不同的是,找子集的题通过设置start,确保了最长的全集只会被收集一次到answer里。但是这道题的目的是相反的,仅仅只有当tmp的长度和nums的长度相等的时候,才能把tmp收集到答案里。所有,这里不需要设计start. 


那么每一次遍历需要从哪里开始呢?由于需要穷举出所有的组合,未必是从当前元素的后一个开始。所以,每一次都需要从开头开始。同时,由于题目设计说是没有duplicate的集合,所以,只要每次循环加个是否包含这个元素的判断,就能去掉一下含有重复元素的组合。


public class Solution {
    private void findPermutations(List<List<Integer>> ans,List<Integer> tmp,int[] nums) {
        if (tmp.size() == nums.length) {
            ans.add(new ArrayList<Integer>(tmp));
        }else{
            for (int i = 0;i < nums.length;i++) {
                if (tmp.contains(nums[i])) {
                    continue;
                }
                tmp.add(nums[i]);
                findPermutations(ans,tmp,nums);
                tmp.remove(tmp.size() - 1);
            }
        }
    }
    
    public List<List<Integer>> permute(int[] nums) {
        if (nums == null || nums.length == 0) {
            return null;
        }
        
        List<List<Integer>> answer = new ArrayList<List<Integer>>();
        List<Integer> tmp = new ArrayList<Integer>();
        
        findPermutations(answer,tmp,nums);
        
        return answer;
    }
}

47. Permutations II

Given a collection of numbers that might contain duplicates, return all possible unique permutations.

For example,
[1,1,2] have the following unique permutations:
[
  [1,1,2],
  [1,2,1],
  [2,1,1]
]


这道题的难点和SubsetII一样,如何让重复的元素排列只出现一次。然而,Subset当中给了我们思路,利用 i>0 && nums[i] == nums[i-1]去判断。在这里需要注意的是,要想办法实现上题中用contains来防止同一个元素出现重复的现象。所以,要在递归当中,传入一个boolean[] 数组,来表示nums对应坐标的元素是否used。而且,这里的是否used还有第二个作用:确保两个duplicates只会以一个顺序出现一次。不会再调转顺序又算一个组合。因为判断时,如果used[i-1] == false的话,直接continue。


代码如下:


public class Solution {
    private void findPermutation(List<List<Integer>> ans,List<Integer> tmp,int[] nums,boolean[] used){
        if (tmp.size() == nums.length){
            ans.add(new ArrayList<Integer>(tmp));
        }else{
            for (int i = 0; i < nums.length; i++){
                if (used[i] == true || (i > 0 && nums[i] == nums[i-1] && used[i - 1] == false)){
                    continue;
                }
                used[i] = true;
                tmp.add(nums[i]);
                findPermutation(ans,tmp,nums,used);
                used[i] = false;
                tmp.remove(tmp.size() - 1);
            }
        }
    }
    
    public List<List<Integer>> permuteUnique(int[] nums) {
        if (nums == null || nums.length == 0) {
            return null;
        }
        
        List<List<Integer>> answer = new ArrayList<List<Integer>>();
        List<Integer> tmp = new ArrayList<Integer>();
        boolean[] used = new boolean[nums.length];
        
        Arrays.sort(nums);
        for(int index = 0; index < used.length; index++) {
            used[index] = false;
        }
        
        findPermutation(answer,tmp,nums,used);
        return answer;
    }
}

39. Combination Sum


Given a set of candidate numbers (C) (without duplicates) and a target number (T), find all unique combinations in C where the candidate numbers sums to T.


The same repeated number may be chosen from C unlimited number of times.


Note:
All numbers (including target) will be positive integers.
The solution set must not contain duplicate combinations.
For example, given candidate set [2, 3, 6, 7] and target 7, 
A solution set is: 
[
  [7],
  [2, 2, 3]
]


这道题,主要需要注意的点在于题目说明了所有的数字都是正数,所以,当算出来目前加起来的数已经大于target,就要及时终止,否则就stack overflow了。另外,如何避免重复?就是每次都从某个index的数开始填充tmp,每次往tmp加数的时候只考虑该index和index之后的,之前的不考虑。通过设置start来确保。


代码如下:


public class Solution {
    private int sumUp(List<Integer> input) {
        int sum = 0;
        for(int temp : input){
            sum += temp;
        }
        return sum;
    }
    
    private void findCombination(List<List<Integer>> answer,List<Integer> tmp,int[] nums,int target,int start){
        int currentRes = sumUp(tmp);
        if (currentRes == target) {
            answer.add(new ArrayList<Integer>(tmp));
        }else if(currentRes < target){//需要注意的点,及时止损
            for(int i = start; i < nums.length; i++) {
                tmp.add(nums[i]);
                findCombination(answer,tmp,nums,target,i);
                tmp.remove(tmp.size() - 1);
            }
        }else{
            return;
        }
    }
    
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        if(candidates == null || candidates.length == 0) {
            return null;
        }
        
        List<List<Integer>> answer = new ArrayList<List<Integer>>();
        List<Integer> tmp = new ArrayList<Integer>();
        
        findCombination(answer,tmp,candidates,target,0);
        return answer;
    }
}


40. Combination Sum II


Given a collection of candidate numbers (C) and a target number (T), find all unique combinations in C where the candidate numbers sums to T.


Each number in C may only be used once in the combination.


Note:
All numbers (including target) will be positive integers.
The solution set must not contain duplicate combinations.
For example, given candidate set [10, 1, 2, 7, 6, 1, 5] and target 8, 
A solution set is: 
[
  [1, 7],
  [1, 2, 5],
  [2, 6],
  [1, 1, 6]
]


这道题和上道题的代码只需要改一个点就好了,不能重复嘛,那就从i+1开始找就好啦。


还要就是注意,这道题的数字是有重复的。所以,还要先sort,接着考虑去重。


添加以下两部分的代码就好:


1) 

        List<List<Integer>> answer = new ArrayList<List<Integer>>();
        List<Integer> tmp = new ArrayList<Integer>();
        Arrays.sort(candidates);
        
        findCombination(answer,tmp,candidates,target,0);
        return answer;

2) 

    private void findCombination(List<List<Integer>> answer,List<Integer> tmp,int[] nums,int target,int start){
        int currentRes = sumUp(tmp);
        if (currentRes == target) {
            answer.add(new ArrayList<Integer>(tmp));
        }else if(currentRes < target){
            for(int i = start; i < nums.length; i++) {
                if (i > start && nums[i] == nums[i-1]) {
                    continue;   
                }
                tmp.add(nums[i]);
                findCombination(answer,tmp,nums,target,i+1);
                tmp.remove(tmp.size() - 1);
            }
        }else{
            return;
        }
    }

131. Palindrome Partitioning


Given a string s, partition s such that every substring of the partition is a palindrome.


Return all possible palindrome partitioning of s.


For example, given s = "aab",
Return


[
  ["aa","b"],
  ["a","a","b"]
]


这道题和上述题目原理大同小异。直接上代码:


public class Solution {
    public List<List<String>> partition(String s) {
        List<List<String>> list = new ArrayList<>();
        backtrack(list, new ArrayList<>(), s, 0);
        return list;
    }

    private void backtrack(List<List<String>> list, List<String> tempList, String s, int start){
       if(start == s.length())
          list.add(new ArrayList<>(tempList));
       else{
          for(int i = start; i < s.length(); i++){
             if(isPalindrome(s, start, i)){
                tempList.add(s.substring(start, i + 1));
                backtrack(list, tempList, s, i + 1);
                tempList.remove(tempList.size() - 1);
             }
          }
       }
    }

    private boolean isPalindrome(String s, int low, int high){
       while(low < high) {
          if(s.charAt(low++) != s.charAt(high--)) {
              return false;
          }
       }          
       return true;
    } 
}



Prim算法生成最小生成树Java代码 [原理链接:http://blog.csdn.net/firehotest/article/details/53503624]:


package graph2.firehotest.net.csdn.blog;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.HashSet;
import java.util.HashMap;
import java.util.Comparator;

class Node{
	public char label;
	public int weight;
	public List<Node> neighbors;
	public Node(char x, int w) { 
		label = x; 
		weight = w;
		neighbors = new ArrayList<Node>(); 
	}
}

class NodeComparator implements Comparator<Node> {
	@Override
	public int compare(Node n1, Node n2){
		if(n1.weight > n2.weight) {
			return 1;
		}else if(n1.weight < n2.weight) {
			return -1;
		}else {
			return 0;
		}
	}
}

class Edge {
	public char letterOne, letterTwo;
	
	public Edge(char inputOne, char inputTwo) {
		this.letterOne = inputOne;
		this.letterTwo = inputTwo;
	}
}

public class MST {
	public static void main(String[] args) {
		/** construct the nodes*/
		Node nodeA = new Node('a',Integer.MAX_VALUE);
		Node nodeB = new Node('b',Integer.MAX_VALUE);
		Node nodeC = new Node('c',Integer.MAX_VALUE);
		Node nodeD = new Node('d',Integer.MAX_VALUE);
		Node nodeE = new Node('e',Integer.MAX_VALUE);
		Node nodeF = new Node('f',Integer.MAX_VALUE);
		
		/* Construct Nodes neighbors*/
		nodeA.neighbors.add(new Node('f',2));
		nodeA.neighbors.add(new Node('b',4));
		
		nodeB.neighbors.add(new Node('a',4));
		nodeB.neighbors.add(new Node('f',3));
		nodeB.neighbors.add(new Node('c',6));
		
		nodeC.neighbors.add(new Node('b',6));
		nodeC.neighbors.add(new Node('f',1));
		nodeC.neighbors.add(new Node('d',3));
		
		nodeD.neighbors.add(new Node('c',3));
		nodeD.neighbors.add(new Node('e',2));
		
		nodeE.neighbors.add(new Node('d',2));
		nodeE.neighbors.add(new Node('f',4));
		
		nodeF.neighbors.add(new Node('a',2));
		nodeF.neighbors.add(new Node('b',3));
		nodeF.neighbors.add(new Node('c',1));
		nodeF.neighbors.add(new Node('e',4));
		
		HashSet<Node> allSet = new HashSet<Node>();
		allSet.add(nodeA);
		allSet.add(nodeB);
		allSet.add(nodeC);
		allSet.add(nodeD);
		allSet.add(nodeE);
		allSet.add(nodeF);
		
		HashSet<Character> allLetterSet = new HashSet<Character>();
		allLetterSet.add(nodeA.label);
		allLetterSet.add(nodeB.label);
		allLetterSet.add(nodeC.label);
		allLetterSet.add(nodeD.label);
		allLetterSet.add(nodeE.label);
		allLetterSet.add(nodeF.label);
		
		HashMap<Character,Integer> key = new HashMap<Character,Integer>();
		key.put(nodeA.label, nodeA.weight);
		key.put(nodeB.label, nodeB.weight);
		key.put(nodeC.label, nodeC.weight);
		key.put(nodeD.label, nodeD.weight);
		key.put(nodeE.label, nodeE.weight);
		key.put(nodeF.label, nodeF.weight);
		
		HashMap<Character, Node> charToNode = new HashMap<Character, Node>();
		charToNode.put('a', nodeA);
		charToNode.put('b', nodeB);
		charToNode.put('c', nodeC);
		charToNode.put('d', nodeD);
		charToNode.put('e', nodeE);
		charToNode.put('f', nodeF);
		
		nodeA.weight = 0;
		
		NodeComparator nodeComparator = new NodeComparator();
		HashMap<Character,Character> parent = new HashMap<Character,Character>();
		ArrayList<Edge> answer = new ArrayList<Edge>();
		
		for(Node tmp : allSet) {
			parent.put(tmp.label, null);
		}
		
		while(!(allSet.isEmpty())) {
			Node tmp = Collections.min(allSet,nodeComparator);
			allSet.remove(tmp);
			allLetterSet.remove(tmp.label);
			
			if (parent.get(tmp.label) != null) {
				answer.add(new Edge(tmp.label, parent.get(tmp.label)));
			}
			for(Node temp : tmp.neighbors){
				if(allLetterSet.contains(temp.label) && temp.weight < key.get(temp.label)){
					parent.put(temp.label, tmp.label);
					key.put(temp.label, temp.weight);
					charToNode.get(temp.label).weight = temp.weight;
				}
			}
		}
		
		for(Edge tempEdge : answer){
			System.out.println("(" + tempEdge.letterOne + "," +
								tempEdge.letterTwo + ")");
		}
	}
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值