[2016/07/05] LeetCode / Java - Day 12 -

328. Odd Even Linked List

Given a singly linked list, group all odd nodes together followed by the even nodes. Please note here we are talking about the node number and not the value in the nodes.

You should try to do it in place. The program should run in O(1) space complexity and O(nodes) time complexity.

Example:
Given 1->2->3->4->5->NULL,
return 1->3->5->2->4->NULL.

Note:
The relative order inside both the even and odd groups should remain as it was in the input. 
The first node is considered odd, the second node even and so on ...

思路:这个链表,每次做的时候我都有种心中呼啸的感觉。。。呃。代码还是很清晰明了的,虽然我做了很久。果然回家是刷题的大敌!!

是这样的,分别用rootO,rootE,tailO,tailE记录奇数头,偶数头,奇数尾和偶数尾,并且在遍历的过程中实时更新它们。最后处理完了以后,把奇数尾和偶数头连起来,偶数尾指向null即可~

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
public class Solution {
    public ListNode oddEvenList(ListNode head) {
    	ListNode p = head;
    	if(head == null || head.next == null) return head;
    	ListNode rootE = head.next, tailE = head.next, tailO = head;
    	
    	boolean pO = true;
    	while(p.next!=null){
    		if(pO){
    			if(p.next.next==null)
    			{	
    				tailO = p;
    				tailE = p.next;
    				break;    		
    			}
    			tailO = tailO.next.next;
    		}else{
	    			if(p.next.next == null)
	    			{
	    				tailO = p.next;
	    				tailE = p;
	    				break;
	    			}    			
	    			tailE = tailE.next.next;
    		}
    		ListNode q = p;
    		p = p.next;
    		q.next = q.next.next;
    		pO = !pO;
    	}	
    	
    	tailO.next = rootE;
    	tailE.next = null;
    	
		return head;           
    }
}

230. Kth Smallest Element in a BST

Given a binary search tree, write a function kthSmallest to find the kth smallest element in it.

Note: 
You may assume k is always valid, 1 ≤ k ≤ BST's total elements.

Follow up:
What if the BST is modified (insert/delete operations) often and you need to find the kth smallest frequently? How would you optimize the kthSmallest routine?

思路:一开始我真的忘了,原来二叉搜索树和二叉树的区别。我当做是二叉树,匪夷所思的想……怎么可能用O(Height Of Tree)???是我太蠢了??后来我终于想到二叉搜索树。。原来是已经排好一部分序的。。。呃=-=

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
int count = 0;
int result = Integer.MIN_VALUE;

public int kthSmallest(TreeNode root, int k) {
    traverse(root, k);
    return result;
}

public void traverse(TreeNode root, int k) {
    if(root == null) return;
    traverse(root.left, k);
    count ++;
    if(count == k) result = root.val;
    traverse(root.right, k);       
}
}

312. Burst Balloons

Given n balloons, indexed from 0 to n-1. Each balloon is painted with a number on it represented by array nums. You are asked to burst all the balloons. If the you burst balloon i you will get nums[left] * nums[i] * nums[right] coins. Here left and right are adjacent indices of i. After the burst, the left and right then becomes adjacent.

Find the maximum coins you can collect by bursting the balloons wisely.

Note: 
(1) You may imagine nums[-1] = nums[n] = 1. They are not real therefore you can not burst them.
(2) 0 ≤ n ≤ 500, 0 ≤ nums[i] ≤ 100

Example:

Given [3, 1, 5, 8]

Return 167

    nums = [3,1,5,8] --> [3,5,8] -->   [3,8]   -->  [8]  --> []
   coins =  3*1*5      +  3*5*8    +  1*3*8      + 1*8*1   = 167

Be Naive First

When I first get this problem, it is far from dynamic programming to me. I started with the most naive idea the backtracking.

一开始看到这个问题,我没想到用动态规划。我用回溯法开始思考最简单的思路。

We have n balloons to burst, which mean we have n steps in the game. In the i th step we have n-i balloons to burst, i = 0~n-1. Therefore we are looking at an algorithm of O(n!). Well, it is slow, probably works for n < 12 only.

我们要炸n个气球,也就是说我们要走n步。在第i步,我们有n-i个气球要炸,i=0~n-1。所以我们这个算法复杂度是O(n!),当然很慢,估计只能在n<12的情况下AC。

Of course this is not the point to implement it. We need to identify the redundant works we did in it and try to optimize.

当然,这不是实现它的要点。我们需要找出我们重复做的工作,并且尝试去优化它。

Well, we can find that for any balloons left the maxCoins does not depends on the balloons already bursted. This indicate that we can use memorization (top down) or dynamic programming (bottom up) for all the cases from small numbers of balloon until n balloons. How many cases are there? For k balloons there are C(n, k) cases and for each case it need to scan the k balloons to compare. The sum is quite big still. It is better than O(n!) but worse than O(2^n).

我们发现,炸哪些气球能获得最大收益,和已经炸了的气球无关。也就是说,我们可以用memorization(自顶而下)或动态规划(自底而上)的方法,从处理小数量的气球开始,直到n个气球处理完。有多少种情况呢?对于k个气球,有C(n,k)种,每种情况,需要遍历k个气球去比较。这个计算量很大,虽然比O(n!)好点,但是比O(2^n)还大。

Better idea

We then think can we apply the divide and conquer technique? After all there seems to be many self similar sub problems from the previous analysis.

接下来,我想,我们可以用分而治之策略么?毕竟通过之前的分析,看上去有很多相似的子问题。

Well, the nature way to divide the problem is burst one balloon and separate the balloons into 2 sub sections one on the left and one one the right. However, in this problem the left and right become adjacent and have effects on the maxCoins in the future.

至于如何分这个问题,自然而然的想法是,炸一个气球,然后把剩下的气球分成左边的和右边的。但是在这个问题中,左右是相邻的,而且会影响到最终结果。

Then another interesting idea come up. Which is quite often seen in dp problem analysis. That is reverse thinking. Like I said the coins you get for a balloon does not depend on the balloons already burst. Therefore
instead of divide the problem by the first balloon to burst, we divide the problem by the last balloon to burst.

那么我有了另一个有趣的想法,这个想法在动态规划问题中很常见,即倒过来思考。我之前说过,你炸一个气球获得的收益和已经炸了的气球无关。所以,我们不是通过炸第一个气球划分这个问题,而是通过炸最后一个气球划分。

Why is that? Because only the first and last balloons we are sure of their adjacent balloons before hand!

For the first we have nums[i-1]*nums[i]*nums[i+1] for the last we have nums[-1]*nums[i]*nums[n].

为啥呢?因为只有第一个和最后一个气球我们是确定他们的相邻气球的。第一个是nums[i-1]*nums[i]*nums[i+1]最后一个是nums[i]

OK. Think about n balloons if i is the last one to burst, what now?

好,如果i是最后一个要炸的气球,一共有n个气球,然后呢?

We can see that the balloons is again separated into 2 sections. But this time since the balloon i is the last balloon of all to burst, the left and right section now has well defined boundary and do not affect each other! Therefore we can do either recursive method with memoization or dp.

我们可以看出,气球又被分为两组。但这次因为i是最后一个要炸的气球,所以左部分和右部分有明确定义的边界,不会相互影响。所以我们可以用memorization或dp来递归。

Final

Here comes the final solutions. Note that we put 2 balloons with 1 as boundaries and also burst all the zero balloons in the first round since they won't give any coins.
The algorithm runs in O(n^3) which can be easily seen from the 3 loops in dp solution.

接下来给出最后的解法。注意,我们把两个气球看作一个整体作为边界,而且把所有为0的气球第一轮就炸光,因为它们没分拿。

算法复杂度是O(n^3),从dp解法中可以看出是3个循环。

public int maxCoins(int[] iNums) {
<span style="white-space:pre">	</span>//初始化气球数组
    int[] nums = new int[iNums.length + 2];
    int n = 1;
    for (int x : iNums) if (x > 0) nums[n++] = x;
    nums[0] = nums[n++] = 1;

<span style="white-space:pre">	</span>//dp数组存储的是,还剩下 n(第一个n) 步 已经炸了 n+1(第二个n) 个气球的收益
    int[][] dp = new int[n][n];
    for (int k = 2; k < n; ++k)
        for (int left = 0; left < n - k; ++left){
            int right = left + k;
            for (int i = left + 1; i < right; ++i)//比较是原值比较大,还是叠加值比较大
                dp[left][right] = Math.max(dp[left][right], 
                nums[left] * nums[i] * nums[right] + dp[left][i] + dp[i][right]);
        }

    return dp[0][n - 1];
}
// 17 ms

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值