Categories
不学无术

LeetCode 142. Linked List Cycle II

Given a linked list, return the node where the cycle begins. If there is no cycle, return null.
Note: Do not modify the linked list.
Follow up:
Can you solve it without using extra space?

思路

其实还有更简单的方法,在《Cracking the Coding Interview》上有提到。通过数学计算可以推导出来,用快行指针的方法判断出环后,可以不用计算环大小,把快指针变慢然后放到链头上,两个指针继续走到相碰就剩入口了。
分为三步:判断环,计算环的大小,找到入口
判断环就是老一套,用2倍速指针就行,这个之前的题目里头有;
计算环的大小k,环的大小么,刚才相碰的地方找个指针绕圈圈走。记个数,再碰到相遇地点就是环大小了;
找到入口后,我们有两个普通速度的指针pA,pB,让其中一个pA先往前走k步,然后两个一起开始挪动,可以知道,如果pA与pB又相遇了,那相遇点就是环的入口了。此时pA比pB刚好多走了一整圈。

代码

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* meetingNode(ListNode* head){
        //Find the meeting node
        if(head == NULL)
            return NULL;
        ListNode* slow = head->next;
        if(slow == NULL)
            return NULL;
        ListNode* fast = slow->next;
        while(fast != NULL && slow != NULL){
            if (slow == fast)
                return slow;
            slow = slow->next;
            fast = fast->next;
            if(fast != NULL)
                fast = fast->next;
        }
        return NULL;
    }
    ListNode *detectCycle(ListNode *head) {
        ListNode* meet = meetingNode(head);
        if(meet == NULL)
            return NULL;
        //Get loop size
        ListNode* temp = meet->next;
        int count = 1;
        while(temp != meet){
            count++;
            temp = temp->next;
        }
        //Pick one pointer at start, move `count` steps forward
        temp = head;
        meet = head;
        while(count > 0){
            count--;
            meet = meet->next;
        }
        //Find the cycle start
        while(temp != meet){
            temp = temp->next;
            meet = meet->next;
        }
        return meet;
    }
};

 

Categories
不学无术

LeetCode 287. Find the Duplicate Number | 智商被碾压!

看了LeetCode上牛人的解答,感觉我的智商被碾压了~

原题

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.
给定一个包含n+1个整数的数组nums,该数组中的每个整数都在[1,n]的范围内,假设数组中只有一个数是重复的,找到那个重复的数。
Note|提示:

  1. You must not modify the array (assume the array is read only).|    不能改动数组内容
  2. You must use only constant, O(1) extra space.|    在O(1)的空间复杂度内完成
  3. Your runtime complexity should be less than O(n2).|    时间复杂度不超过O(n^2)
  4. There is only one duplicate number in the array, but it could be repeated more than once.|    只有一个重复数字,但是可能重复超过一次

思路1:O(nlogn)时间复杂度

英文版解释:https://leetcode.com/discuss/60830/solutions-explanation-space-without-changing-input-array
这个解法涉及到鸽巢原理。
假设有n=10的一个数组(大小是n+1),那我目前的搜索范围是[1,10],先找中间的数mid=5.现在我们遍历数组的所有元素并统计<=5的元素个数,记作n_lteq5好了,那么有:

  • 如果n_lteq5>5,那就有6个数字占了本来只有5个的坑位,那目标数字肯定是在[1,5]的范围内了;
  • 如果n_lteq5<=5,那前面的元素都挺守规矩的,得看看[6,10]里头哪个数字在作怪;

这样每一次判断,问题的规模都会缩小一半,一共n+1个数字,时间复杂度是O(nlogn)
感觉碰到了很多二分思想的题目啊,关键点就是要找到一个什么划分方式把问题的规模缩小掉,而在这道题里头用到的就是鸽巢原理。

思路1代码

class Solution {
public:
    int countLtEq(const vector<int>& nums, int n){
        int count = 0;
        for(int i=0; i<nums.size(); i++){
            if(nums[i] <= n)
                count ++;
        }
        return count;
    }
    int findDuplicate(vector<int>& nums) {
        int n = nums.size() - 1;
        int min = 1, max = n;
        int mid;
        int ltEqCnt;
        while(min < max){
            mid = (min + max)/2;
            ltEqCnt = countLtEq(nums, mid);
            if(ltEqCnt <= mid){
                //[min, mid]是正常的,找另一边
                min = mid+1;
            } else {
                max = mid;
            }
        }
        return min; // min == max
    }
};

 

插曲:如果可以改变数组元素呢?

感觉在剑指Offer上遇到过类似的题目。如果可以改变数组元素的话,可以按照下面的思路进行,时间复杂度是O(n)。基本思想是,一个萝卜一个坑,把搜索到的数放到自己应该在的位置上,比如1应该放在第一个位置上,如果后面再找到一个1,我们就会发现1这个位置的坑被前一个1占了,那就冲突了。
方便起见,数组的位置按1,2,3,…描述(而非0,1,2…)。假设有数组[5,4,2,3,1,5,6] ,当前工作的位置pos=1

  • 先看第pos=1个数字5,在设想的排好序的序列里,5应该就在第5个位置上,因此我们把5和第5个位置上现有的数字1进行调换,结果就变成了[1,4,2,3,5,5,6] ;
  • 看换回来的数字1,由于已经在1号位上,这次处理就结束了,往前挪一下,pos=2,看下一个数字;
  • pos=2的数字是4,本来应该在4号位上,因此把4和4号位上的 3调换,数组变成[1,3,2,4,5,5,6] ;
  • 这时候事情没完,因为换回来的3不在应该有的3号位上,因此把3和3号位上占着的2调换,数组变成[1,2,3,4,5,5,6] ;
  • 换回的数字2是正确的位置,因此pos++,变成pos=3,类似地检查pos=4, 5发现都符合要求;
  • pos=6时,此时位置上的元素是5,于是我们想把这个5归为到5号位,但是检查5号位的时候我们发现,上面已经有一个5了,这就说明5是重复数值了,搞定

可能有问,那如果原来的数组是[5,4,2,3,1,6,5] 呢?很显然,如果前面找到pos=6的时候都没发生冲突,那罪魁祸首肯定在最后个元素上面了。
这种方法只要遍历一次数组并做相应替换就行,除了会改变数组内容外,其他条件都满足,可惜啊。

思路2:O(n)时间复杂度

大神,大家快去膜拜:http://keithschwarz.com/interesting/code/?dir=find-duplicate
据说越是经典的代码行数越是写的简单,然而你并不能看懂…
似乎是把数组中的值看做单链表的next指针的值的,还在理解中~这个算法理论上是正确的。
 

Categories
不学无术

LeetCode 330. Patching Array

题目

Given a sorted positive integer array nums and an integer n, add/patch elements to the array such that any number in range [1, n] inclusive can be formed by the sum of some elements in the array. Return the minimum number of patches required.
Example 1:
nums = [1, 3], n = 6
Return 1.
Combinations of nums are [1], [3], [1,3], which form possible sums of: 1, 3, 4.
Now if we add/patch 2 to nums, the combinations are: [1], [2], [3], [1,3], [2,3], [1,2,3].
Possible sums are 1, 2, 3, 4, 5, 6, which now covers the range [1, 6].
So we only need 1 patch.
Example 2:
nums = [1, 5, 10], n = 20
Return 2.
The two patches can be [2, 4].
Example 3:
nums = [1, 2, 2], n = 5
Return 0.

思路

说实话这道题没有想出来,还以为是个排列组合的问题,后来看了解释才恍然大悟。网上版本有很多种,我看的官网discussion里头的(这里)。
思路就是贪心法,假设目标是覆盖[0,n],当前我们的组合能够覆盖[0,miss)的数字(注意这里的miss是目前无法到达的目标,我们得用贪心的思想让这个目标可达),并且目前正在处理有序数组中num[i]的数,那么有两种情况需要考虑:

  • 如果num[i]比miss来的小,说明总有一个之前有的数,加上了num[i]以后能够到达miss完成任务。因为我们比较贪心,想让可覆盖的数越多越好,所以把num[i]带上,得到最大可以覆盖的范围[0,miss+num[i]);
  • 如果num[i]比miss来的大,那么显然把num[i]加进去是无法得到miss的。比如:目前有0,1,2,…,9可以覆盖(即miss=10),来了个num[i]=12的,显然加入12并不能解决覆盖10的问题(试想一下0+12都比10来的大)。所以最经济实惠的办法就是把miss自己加进去,这样还能把覆盖范围拓展到[0,miss*2);

代码

class Solution {
public:
    int minPatches(vector<int>& nums, int n) {
        long known_sum = 1, count = 0, i = 0;
        while(known_sum <= n){
            if(i < nums.size() && nums[i]<=known_sum){
                //If we have a choice to expand the range
                known_sum += nums[i++]; //The best choice is to add nums[i] in, which can reach up to (known+nums[i])
            } else {
                //Or we have to add in one item to make the best range, which means to double the range to 2*known_sum
                known_sum += known_sum;
                count++;
            }
        }
        return count;
    }
};

 

Categories
不学无术

LeetCode 36. Valid Sudoku

Determine if a Sudoku is valid, according to: Sudoku Puzzles – The Rules.
The Sudoku board could be partially filled, where empty cells are filled with the character '.'.

A partially filled sudoku which is valid.
Note:
A valid Sudoku board (partially filled) is not necessarily solvable. Only the filled cells need to be validated.

思路

行搜索、列搜索、sub-box搜索,根据规则来就行了

代码

class Solution {
public:
    bool isValidSudoku(vector<vector<char>>& board) {
        set<char> workingSet;
        // Row
        char c;
        for(int row = 0; row<9; row++){
            workingSet.clear();
            for(int col=0; col<9; col++){
                c = board[row][col];
                if(c == '.')
                    continue;
                if(workingSet.count(c) > 0)
                    return false;
                workingSet.insert(c);
            }
        }
        // Col
        for(int col = 0; col<9; col++){
            workingSet.clear();
            for(int row=0; row<9; row++){
                c = board[row][col];
                if(c == '.')
                    continue;
                if(workingSet.count(c) > 0)
                    return false;
                workingSet.insert(c);
            }
        }
        // Sub-box
        for(int lPos=0; lPos < 9; lPos+=3){
            for(int tPos=0; tPos<9; tPos+=3){
                workingSet.clear();
                for(int i=0; i<3; i++){
                    for(int j=0; j<3; j++){
                        c = board[lPos+i][tPos+j];
                        if(c == '.')
                            continue;
                        if(workingSet.count(c) > 0)
                            return false;
                        workingSet.insert(c);
                    }
                }
            }
        }
        return true;
    }
};

 

Categories
不学无术

LeetCode 264. Ugly Number II

Write a program to find the n-th ugly number.
Ugly numbers are positive numbers whose prime factors only include 2, 3, 5. For example, 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 is the sequence of the first 10 ugly numbers.
Note that 1 is typically treated as an ugly number.

思路

《剑指Offer》上的原题34,所以就说个大概吧。
下一个丑数是之前的丑数(最初是1)乘以2、3或者5得到的,所以下一个丑数就是之前的某个数乘出来的。这样的找法比从1到n一个个判断是不是丑数高效很多,不然每个数都要去除个2、3、5判断一下。
由于要管次序问题,所以安排三个指针,分别记录乘以2、3、5后不超过当前最大丑数的位置,假设是p2, p3, p5,下一次找下个丑数的时候,从(p2)2、(p3)3、(p5)5里头找到个最小的,加在数组最后面,然后更新三个指针的值,如此往复……

代码

class Solution {
public:
    int nthUglyNumber(int n) {
        if(n <= 0)
            return 0;
        int* uglyAry = new int[n];
        uglyAry[0] = 1;
        int nextPos = 1;
        int *pNextUgly2 = uglyAry;
        int *pNextUgly3 = uglyAry;
        int *pNextUgly5 = uglyAry;
        while(nextPos < n){
            int nextUgly = min3(*pNextUgly2 * 2, *pNextUgly3 * 3, *pNextUgly5 * 5);
            uglyAry[nextPos] = nextUgly;
            //Move forward to find next possible candidates
            while(*pNextUgly2 * 2 <= nextUgly)
                pNextUgly2++;
            while(*pNextUgly3 * 3 <= nextUgly)
                pNextUgly3++;
            while(*pNextUgly5 * 5 <= nextUgly)
                pNextUgly5++;
            nextPos++;
        }
        int ugly = uglyAry[n-1];
        delete []uglyAry;
        return ugly;
    }
    int min3(int n1, int n2, int n3){
        int min = n1<n2?n1:n2;
        min = min<n3?min:n3;
        return min;
    }
};

 
 

Categories
不学无术

LeetCode 257. Binary Tree Paths

Given a binary tree, return all root-to-leaf paths.
For example, given the following binary tree:

   1
 /   \
2     3
 \
  5

All root-to-leaf paths are:

["1->2->5", "1->3"]

代码

/**
 * 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:
    vector<string> binaryTreePaths(TreeNode* root) {
        vector<string> results;
        vector<int> path;
        binaryTreePaths(root, path, results);
        return results;
    }
    void binaryTreePaths(TreeNode* root, vector<int> path, vector<string>& results){
        if(root == NULL){
            //Error
            return;
        }
        path.push_back(root->val);
        if(root->left == NULL && root->right == NULL) {
            //Leaf
            string result;
            int pSize = path.size();
            for(int i=0; i<pSize; i++){
                result += to_string(path[i]);
                if(i != pSize - 1)
                    result += "->";
            }
            results.push_back(result);
        } else {
            if(root->left != NULL)
                binaryTreePaths(root->left, path, results);
            if(root->right != NULL)
                binaryTreePaths(root->right, path, results);
        }
    }
};

 
 

Categories
不学无术

LeetCode 169. Majority Element My Submissions Question

Given an array of size n, find the majority element. The majority element is the element that appears more than ⌊ n/2 ⌋ times.
You may assume that the array is non-empty and the majority element always exist in the array.

思路

多数投票嘛,这个题目当年NB哄哄的数据库老师在课堂上提到过,至今印象深刻,跟我们说选班长的时候画正字就是在浪费空间(当然要保证半数通过的前提)。
假设当前候选人A唱了三票,那么小黑板上A的“正”字记了三笔;这时候有了B的一票,由于我们只要统计排名第一的人,此时可以把A的正字划去一票,而不是重新记一个“B,一票”。因为照这样计算,如果B再唱了两票,那两个人的票刚好抵消,小黑板被擦干净,这时候不管是谁得了一票就写在小黑板上重新计数啦

思路2

设想一下排序后的数列,如果有个元素占了超过半数的位置,那么它肯定也是这个数列的中位数。参考快速排序的思想,可以在O(n)的复杂度找到这个中位数,它也就是要得到的结果。(参考数组第k大数的那个递归方法

代码(思路1)

class Solution {
public:
    int majorityElement(vector<int>& nums) {
        int majNum = -1;
        int majCount = 0;
        int numSz = nums.size();
        for(int i=0; i<numSz; i++){
            if(majNum != nums[i]){
                if(majCount == 0){
                    majNum = nums[i];
                    majCount = 1;
                } else {
                    majCount--;
                }
            } else {
                majCount++;
            }
        }
        return majNum;
    }
};

 

Categories
不学无术

LeetCode 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?

思路:

二叉搜索树的中序遍历(LNR)就是按照从小到大顺序排的,所以遍历到N节点的时候就记个数,计数到k的时候就返回了。

代码:

/**
 * 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:
    int kthSmallest(TreeNode* root, int k) {
        if(root == NULL)
            return -1;
        int retVal = -1;
        int count = 0;
        kthSmallest1(root, k, count, retVal);
        return retVal;
    }
    void kthSmallest1(TreeNode* root, int k, int& count, int& retVal){
        if(count >= k)
            return;
        if(root->left != NULL)
            kthSmallest1(root->left, k, count, retVal);
        count ++;
        if(count == k){
            retVal = root->val;
            return;
        }
        if(root->right != NULL)
            kthSmallest1(root->right, k, count, retVal);
    }
};

 

Categories
不学无术

LeetCode 26. Remove Duplicates from Sorted Array

Given a sorted array, remove the duplicates in place such that each element appear only once and return the new length.
Do not allocate extra space for another array, you must do this in place with constant memory.
For example,
Given input array nums = [1,1,2],
Your function should return length = 2, with the first two elements of nums being 1 and 2 respectively. It doesn’t matter what you leave beyond the new length.

思路

倆指针~一个指向当前实际有效的位置,一个指向当前处理的位置
题外话:经历感冒鼻塞喉咙痛快一周,脑子终于是清楚了!

代码

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        int numSz = nums.size();
        if(numSz <= 1)
            return numSz;
        int i=0;
        int j=1;
        while(j < numSz){
            if(nums[j] == nums[i])
                j++;
            else {
                nums[++i] = nums[j];
                j++;
            }
        }
        return i+1;
    }
};

 

Categories
不学无术

LeetCode 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.

思路

这个是剑指Offer还是什么程序员面试经典或者是编程珠玑上的一道题,反正已经广为流传了。
O(n)复杂度的想法是,假定我有之前能得到的最大和子串的和sum[i-1]和当前值a[i],如何得到当前的最大和sum[i]呢?分两个情况:

  • 如果sum[i-1]+a[i]<a[i],比如上面那个数列的前两项-2+1 = -1 < 1,那意思就是前面那么好的加起来还不如从我这边重新开始来的好,则sum[1]就是a[i]
  • 否则,更新sum[i]=sum[i-1]+a[i]

概括成一句话就是,既然之前的人加在一起都不如我,那就我带头上吧!

代码

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        if(nums.empty())
            return -1;
        int max = nums[0];
        for(int i = 1; i<nums.size(); i++){
            if(nums[i-1] + nums[i] > nums[i]){
                nums[i] += nums[i-1];
            }
            if(nums[i] > max)
                max = nums[i];
        }
        return max;
    }
};