Categories
不学无术

LeetCode 15. 3Sum

原题:https://leetcode.com/problems/3sum/
思路:
3Sum的问题其实是2Sum问题的晋级版,可我当年投机取巧用hashmap过了2sum问题(有兴趣可以搜下我的解法^-^),导致这个3sum卡壳了很久。所以经验教训是,不管自己AC了没有,要看看别人怎么做的,歪门邪道不一定次次通用。
因此先来解决下2sum的问题,也就是给定一个数组,找到两个元素a[i],a[j]使得a[i]+a[j]=X的问题,简单点,假设X=0;这个问题可以用两个指针来解决,当然前提是要先把数组排序好,时间复杂度是O(nlogn). 排序后,假设两个指针一开始指向数组的头、末元素(i=0, j=size-1),然后就有三种可能性:

  • 刚好碰巧这两个值的和就是0 ,那么下一步i,j就要各自朝向内部移动一格(i++, j–)。这里不会产生分支情况,即不可能存在a[i]+a[j] == a[i+1]+a[j] 或==a[i]+a[j-1],除非前项、后项的值是同一个元素;
  • 如果两个值的和<0了,那说明负能量太大了,因此i明显太小了,要把i向右移动让它变大才有可能,因此i++;
  • 与上一条同理,j–;

所以,2sum问题在排序后,内部查找的时间复杂度是线性复杂度O(n).
那么3sum问题呢,可以先简化一下,假设不用考虑数组内有重复元素的情况,那么可以把3Sum问题这样简化,固定一个位置的元素a[pos],在数组内寻找另外两个元素,其值a[i]+a[j]==-a[pos],这就变成2Sum问题了,其中之前提到的X=-a[pos]。整个算法需要先排序一遍,时间复杂度O(nlogn),然后需要遍历每个pos再在循环里做2Sum问题,复杂度为O(n^2),所以两个连在一起,复杂度在O(n^2)范围内(暴力解法复杂度为O(n^3))。
代码(记得处理重复问题)

bool sortFunc (int i,int j) { return (i<j); }
class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        sort(nums.begin(), nums.end(), sortFunc);
        vector<vector<int>> results;
        for(int pos=0; pos<nums.size(); pos++){
            threeSumSub(nums, pos, results);
            while(pos+1 < nums.size() && nums[pos+1] == nums[pos])
                pos++;
        };
        return results;
    }
    void threeSumSub(vector<int>& nums, int pos, vector<vector<int>> &results) {
        //Find two elements which add up equals -nums[pos]
        int i = pos+1;
        int j = nums.size() - 1;
        int val;
        while(i < j) {
            val = nums[pos] + nums[i] + nums[j];
            if(val == 0){
                vector<int> solution;
                solution.push_back(nums[pos]);
                solution.push_back(nums[i]);
                solution.push_back(nums[j]);
                results.push_back(solution);
                while(nums[i+1] == nums[i] && i < j) i++;
                while(nums[j-1] == nums[j] && i < j) j--;
                i++;
                j--;
            } else if (val < 0) {
                //Too big, move right
                while(nums[i+1] == nums[i] && i < j) i++;
                i++;
            } else {
                while(nums[j-1] == nums[j] && i < j) j--;
                j--;
            }
        }
    }
};

 
 

Categories
不学无术

LeetCode 11. Container With Most Water

原题:https://leetcode.com/problems/container-with-most-water/
思路:
最简单的方法当然是嵌套两层的循环i,j,时间复杂度是O(n^2),但是撸了一发超时了,说明还有更简单的方法。
可以这么理解,要让长方形的面积最大,可以先从底边长度最大开始试,假设左端高度是h_i,右端高度是h_j,那么面积就是(j-i)*min(h_i, h_j).
此时可以移动左边或右边的柱子将底边长度缩小一点,但是有个问题,假设当前的瓶颈在左侧i这里,如果保持i的位置不变移动右侧,最好情况也只会把面积缩小,因为底边长度缩水了,高最高也就是i的高度,所以其实若要缩小宽度,只能把瓶颈去除掉,只能移动当前的瓶颈位置i,这样如果碰到个更大的i’,高度会上升至min(h_i’,h_j),肯定比i要大了,这样才有可能增加面积。由于这种方法只用扫一遍数组,所以时间复杂度是O(n)
代码:

class Solution {
public:
    int maxArea(vector<int>& height) {
        int i = 0;
        int j = height.size() - 1;
        int area;
        int maxArea = 0;
        while(i != j){
            if(height[i] < height[j]){
                area = height[i] * (j - i);
                i ++; //Bottle neck at i, move forward
            } else {
                area = height[j] * (j - i);
                j --; //Bottle neck at j, move backward
            }
            if (area > maxArea)
                maxArea = area;
        }
        return maxArea;
    }
};

 

Categories
不学无术

(转载)从最大似然到EM算法浅解

感觉是我看到解释得最容易理解的一个版本
http://blog.csdn.net/zouxy09/article/details/8537620

Categories
不学无术

LeetCode 217. Contains Duplicate

题目:https://leetcode.com/problems/contains-duplicate/
思路:弄个哈希表啊或者现成的用哈希(或者XX树)的容器就行了
代码:

class Solution {
public:
    bool containsDuplicate(vector<int>& nums) {
        set<int> numSet;
        int numSize = nums.size();
        for (int i = 0; i<numSize; i++) {
            if (numSet.count(nums[i]) == 0)
                numSet.insert(nums[i]);
            else
                return true;
        }
        return false;
    }
};

 

Categories
不学无术

LeetCode 242. Valid Anagram

原题:https://leetcode.com/problems/valid-anagram/
思路:Anagram就是指两个单词(一说句子也行)里面字符的种类、数量一样,但是能拼出不同的词。所以思路就是统计里面的字符频率就行。
代码:

class Solution {
public:
    bool isAnagram(string s, string t) {
        if(s.size() != t.size())
            return false;
        int stable[26] = {0};
        int ttable[26] = {0};
        for(int i=0; i<s.size(); i++){
            stable[s[i] - 'a'] ++;
            ttable[t[i] - 'a'] ++;
        }
        for(int i=0; i<26; i++){
            if (stable[i] != ttable[i])
                return false;
        }
        return true;
    }
};

 

Categories
不学无术

LeetCode 283. Move Zeroes

题目:https://leetcode.com/problems/move-zeroes/
思路:
说是把0 挪到后面去,其实等同于把非0值往前挪。挪到什么地方呢?就得找个东西记录可以往前挪的“坑”。而这个“坑”有两种情况:
1.原来的值就是0的;
2.如果把当前的非0值往前面挪过了,那当前的位置也就空缺;
从一开始记录一下找到的0的个数,把非0值挪完了以后,最后几位写成0就行。
这一切的时间复杂度为O(n)
代码(用到了队列来存储坑的位置):

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        int size = nums.size();
        int zeroCount = 0;
        queue<int> availIndices;
        for(int i=0; i < size; i++){
            if(nums[i] == 0){
                zeroCount ++;
                availIndices.push(i);
            } else {
                if(!availIndices.empty()) {
                    //Move forward
                    int index =availIndices.front();
                    availIndices.pop();
                    nums[index] = nums[i];
                    //Mark i as empty
                    availIndices.push(i);
                }
            }
        }
        for(int i = size - zeroCount; i < size; i++) {
            nums[i] = 0;
        }
    }
};

 

Categories
不学无术

LeetCode #100. Same Tree

题目:https://leetcode.com/problems/same-tree/
分析:注意判断NULL情况,用递归做
代码:(秒AC有木有!)

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    bool isSameTree(TreeNode* p, TreeNode* q) {
        if(p == NULL && q == NULL)
            return true;
        if(p == NULL || q == NULL)
            return false;
        if(p->val != q->val)
            return false;
        return isSameTree(p->left, q->left) && isSameTree(p->right, q->right);
    }
};

 

Categories
不学无术

LeetCode #226. Invert Binary Tree

题目:https://leetcode.com/problems/invert-binary-tree/
思路:这TM还要思路?
代码:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        if(root == NULL)
            return root;
        TreeNode *tmpNode = root->right;
        root->right = root->left;
        root->left = tmpNode;
        if(root->left != NULL)
            invertTree(root->left);
        if(root->right != NULL)
            invertTree(root->right);
        return root;
    }
};

 

Categories
不学无术

LeetCode #4. Median of Two Sorted Arrays

题目链接https://leetcode.com/problems/median-of-two-sorted-arrays/
参考文献

思路:
实在是脑子转不过来,看了一下解法,然后就把自己的理解讲一下吧。
有两个规律需要说下:
1.在某个数列头尾删去相同数量的元素,其中位数是不变的。比如[1,2,3,4]删去1和4.
2.如果两个数列的中位数是m1, m2,那么合并后的数列的中位数的值肯定在[m1,m2]范围内。
既然题目是要Log级别的复杂度,就要想到要用折半法把问题的规模降低…举个例子,假设有[1,2,4,6,6,8]和[1,5,7,8,10,25,36]两个数列,很容易算出他们的(前序 )中位数分别是4和8,而他们合并后的中位数的值在[4,8]之间。
然后可以删去无用的值,[1,2]与[10,25,36]. 但由于规律1的限制,如果后面删了3个的话,合并后删减的数列会发生变化(本例中奇偶性都变了),我们只能删去min(2,3)=2个元素…这种删值从理论上讲就是折半了,然后搞一波递归就行了。
最最最基本的思路如下:

两个有序数列Ary1, Ary2(假设升序排列)的中位数分别为m1,m2
if m1 == m2:
    LUCKY!!!
elif m1 < m2:
    取(Ary1的后半段,Ary2的前半段)再算
else
    取(Ary1的前半段,Ary2的后半段)再算

由于题设里面俩数组是不等长的,所以里对其中某个数组n<=2时处理方法需要注意…
代码:

int max(int x, int y) {
    return x>y ? x : y;
}
int min(int x, int y) {
    return x<y ? x : y;
}
double getMedian(int* nums, int size) {
	if (size == 0)
		return -1;
	if (size % 2 == 0) {
		//Even number
		return (nums[size / 2] + nums[size / 2 - 1]) / 2.0;
	}
	else {
		return nums[(size - 1) / 2];
	}
}
double findMedianMerge(int* nums1, int nums1Size, int* nums2, int nums2Size) {
	int count = 0;
	int n1Count = 0;
	int totalSize = nums1Size + nums2Size;
	int t1, t2;
	if (totalSize % 2 == 0) {
		t1 = totalSize / 2;
		t2 = t1 + 1;
	}
	else {
		t1 = -1;
		t2 = totalSize / 2 + 1;
	}
	double sum = 0;
	int curVal;
	while (n1Count < nums1Size) {
		if (*nums1 < *nums2) {
			n1Count++;
			curVal = *nums1;
			nums1++;
		}
		else {
			curVal = *nums2;
			nums2++;
		}
		count++;
		if (count == t1 || count == t2) {
			sum += curVal;
		}
		else if (count >= t2) {
			break;
		}
	}
	if (count < t1) {
		//Not finished
		nums2 += (t1 - count - 1);
		count = t1;
		sum += *nums2;
		nums2++;
	}
	if (count < t2) {
		nums2 += (t2 - count - 1);
		sum += *nums2;
	}
	if (t1 == -1)
		return sum;
	else
		return sum / 2.0;
}
double findMedianSortedArrays(int* nums1, int nums1Size, int* nums2, int nums2Size) {
	int totalSize = nums1Size + nums2Size;
	if (nums1Size > nums2Size) {
		//Keep ary1 shorter than ary2
		int* temp = nums1;
		int t = nums1Size;
		nums1 = nums2;
		nums1Size = nums2Size;
		nums2 = temp;
		nums2Size = t;
	}
	if (nums1Size == 0) {
		return getMedian(nums2, nums2Size);
	} else if (nums1Size == 1) {
		if (nums2Size == 1) {
			return (nums1[0] + nums2[0]) / 2.0;
		}
		else {
			return findMedianMerge(nums1, nums1Size, nums2, nums2Size);
		}
	}
	else if (nums1Size == 2) {
		if (nums2Size == 2) {
			return (max(nums1[0], nums2[0]) + min(nums1[1], nums2[1])) / 2.0;
		}
		else {
			return findMedianMerge(nums1, nums1Size, nums2, nums2Size);
		}
	}
	//Slice
	double m1 = getMedian(nums1, nums1Size);
	double m2 = getMedian(nums2, nums2Size);
	if (m1 == m2) {
		//Got lucky
		return m1;
	}
	else if (m1 < m2) {
		//Slice L-nums1 and R-nums2
		int cut = (nums1Size - 1) / 2;
		return findMedianSortedArrays(nums1 + cut, nums1Size - cut, nums2, nums2Size - cut);
	}
	else {
		//Slice R-nums1 and L-nums2
		int cut = (nums1Size - 1) / 2;
		return findMedianSortedArrays(nums1, nums1Size - cut, nums2 + cut, nums2Size - cut);
	}
}

 

Categories
不学无术

LeetCode #237. Delete Node in a Linked List

题目:https://leetcode.com/problems/delete-node-in-a-linked-list/
思路:
因为只给要删去节点的指针,所以就要想点歪办法。
假设我们有节点1,2,3,【4】,5,6,要删去4这个点,那其实只要把后序节点5的值复制给4,然后我们可以把后面原来那个5删掉,把原有的4节点链到6就行了~大致意思就是,通过往前复制一个值,构造出一个新的“4”的前序节点。

使用前:1,2,3,[4],5,6
拷贝值:1,2,3,5,[5],6
删去多于节点(将新的5节点链接到拷贝的5右边的6,删去老的5节点):1,2,3,5,6

代码:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    void deleteNode(ListNode* node) {
        ListNode* nextNode = node->next;
        node->val = nextNode->val; //Copy value
        ListNode* thirdNode = nextNode->next;
        node->next = thirdNode;
        delete nextNode;
    }
};