leetcode 算法学习

1  Unique Paths II   带障碍物的路径计算

思路:dp[i][j] = 0 if grid[i][j] = 1 (障碍物)

        再按照无障碍物的逻辑进行计算

int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        int rows = obstacleGrid.size();
        if (rows == 0) {
            return 0;
        }
        int cols = obstacleGrid[0].size();
        vector<vector<int>> ways(rows, vector<int> (cols, 1));
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                if (obstacleGrid[i][j] == 1) {
                    ways[i][j] = 0;
                } else {
                    if (i == 0 && j == 0) {
                        ways[i][j] = 1;
                    } else if (i == 0) {
                        ways[i][j] =  ways[i][j - 1];
                    } else if (j == 0) {
                        ways[i][j] = ways[i - 1][j];
                    } else {
                        ways[i][j] = ways[i - 1][j] + ways[i][j - 1];
                    }
                }
            }
        }
        return ways[rows - 1][cols - 1];
    }
View Code

 

 2 矩阵的环形遍历

方法:(1)设置rowStart,rowEnd,colStart,colEnd四个变量来指示当前轮遍历的起始位置

     (2) 按照顺时针方向进行遍历,一轮遍历分成四次循环,每一次循环完成,终点或起点需要缩进一个单位(start + 1,end -1)

注意:当矩阵不是方阵时,需要在每一轮向左和向上遍历之前进行一次判断,否则会造成重复。

vector<int> spiralOrder(vector<vector<int>>& matrix) {
        if (matrix.size() == 0) {
            return {};
        }
        int rowStart = 0;
        int rowEnd = matrix.size() - 1;
        int colStart = 0;
        int colEnd = matrix[0].size() - 1;
        vector<int> order;
        while (rowStart <= rowEnd && colStart <= colEnd) {
            for (int j = colStart; j <= colEnd; j++) {
                order.push_back(matrix[rowStart][j]);
            }
            rowStart++;
            for (int i = rowStart; i <= rowEnd; i++) {
                order.push_back(matrix[i][colEnd]);
            }
            colEnd--;
            if (rowStart <= rowEnd) { //注意此处
                for (int j = colEnd; j >= colStart; j--) {
                    order.push_back(matrix[rowEnd][j]);
                }
                rowEnd--;
            }
            if (colStart <= colEnd) {//注意此处
                for (int i = rowEnd; i >= rowStart; i--) {
                    order.push_back(matrix[i][colStart]);
                }
                colStart++;
            }
        }
        return order;
    }
View Code

 3 中序和后序遍历序构造二叉树

思路:递归

注意:递归函数的写法和终止条件

TreeNode* helper(vector<int>& inorder, int inStart, int inEnd, vector<int>& postorder, int postStart, int postEnd) {
    if (inEnd < inStart || postEnd < postStart) {
        return NULL;
    }
    if (inEnd == inStart) {
        return new TreeNode(inorder[inEnd]);
    }
    int i = inEnd;
    while (inorder[i] != postorder[postEnd]) {
        i--;
    }
    TreeNode* root = new TreeNode(postorder[postEnd]);
    root->left = helper(inorder,inStart, i - 1, postorder, postStart, postStart + i - inStart - 1);
    root->right = helper(inorder, i + 1, inEnd, postorder, postStart + i - inStart, postEnd - 1);
    return root;
}
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
    if (inorder.size() == 0) {
        return NULL;
    }
    return helper(inorder, 0, inorder.size() - 1, postorder, 0, postorder.size() - 1);
}
View Code

 4 行和列都升序排列的矩阵查找第k小的数

思路:二分查找

int kthSmallest(vector<vector<int>>& matrix) {
    int n = matrix.size();
    int left = matrix[0][0];
    int right = matrix[n-1][n-1];
    while (left < right) {
        int midv = (left + right) / 2;
        
        int count = 0;  // number of elements no bigger than midv
        for (int i = 0; i < n; i++) {
            vector<int>& row = matrix[i];
            count += std::upper_bound(row.begin(), row.end(), midv) - row.begin();
        }
        
        if (count < k) {
            left = midv + 1;
        } else {
            right = midv;    
        }
    }
    return left;
}
View Code

 5 求1 到n中1出现的次数

思路:从各位到最高位,依次出现1的次数。如n = 3141592,计算到百位时,a = 3141592 / 100 = 31415, b = 3141592 % 100 = 92

百位数字为5,当百位数字为1时,前缀有(0~3141)共3142种,后缀可为(00~99)。

当百位数字为1时,后缀只能从00~92,共93种。

int countDigitOne(int n) {
    int ones = 0;
    for (long m = 1; m <= n; m *= 10) {
        int a = n / m;
        int b= n % m;
        ones += (a + 8) / 10 * m + (a % 10 == 1 ? b + 1 : 0);
        /*
        if (a % 10 >= 2) {
            ones += (a / 10 + 1) * m;
        } else {
            ones += a / 10 * m + (b + 1);
        }
        */
    }
    return ones;
}
View Code

6 判断单链表是否有环,返回环的起点

思路:设置快慢指针,如果在相遇前为空,则无环。

     快慢指针第一次相遇后,将快指针重置为head,一次走一步,再次相遇则为环的起点。

注意:初始值,判空

ListNode *detectCycle(ListNode *head) {
    if (head == NULL || head->next == NULL || head->next->next == NULL) {
        return NULL;
    }
    ListNode* fast = head->next->next;
    ListNode* slow = head->next;
    while (fast != slow) {
        if (fast->next == NULL || fast->next->next == NULL) {
            return NULL;
        }
        fast = fast->next->next;
        slow = slow->next;
    }
    fast = head;
    while (fast != slow) {
        slow = slow->next;
        fast = fast->next;
    }
    return fast;
}
View Code

 7 stl set

set 内部是升序排列的,底层实现为红黑树。

基本操作:

(1) set<int> data;

(2) data.erase(data.begin());//删除最小值

(3)data.insert(m)

 

返回第3大的数
int thirdMax(vector<int>& nums) {
    set<int> top3;//内部为升序排列
    for (int num : nums) {
         top3.insert(num);
         if (top3.size() > 3) {
             top3.erase(top3.begin());//删除最小值
         }
    }
    return top3.size() == 3 ? *top3.begin() : *top3.rbegin();
}
View Code

 8 求无序数组中,出现次数最高的k个数

思路:(1)用map计数

   (2)用priority_queue来取得前k个数(默认为大顶堆)

vector<int> topKFrequent(vector<int>& nums, int k) {
    map<int, int> count;
    for (int num : nums) {
        count[num]++;
    }//count times of each num 
    vector<int> result;
    priority_queue<pair<int, int>> order;//默认大顶堆
    for (auto it = count.begin(); it != count.end(); it++) {
        order.push(make_pair(it->second, it->first));
        if (order.size() > count.size() - k) {//we can get one num that belongs to result
            result.push_back(order.top().second);//the biggest
            order.pop();
        }
    }
    return result;
}
View Code

 9 给一个数组,里面只有一个数字一次,其它数字都出现3次,找出这个出现一次的数字,要求时间复杂度为O(n),空间复杂度为O(1)。

思路:利用位运算代替计数的存储结构和加法计算

(1) ones 表示第i位出现1次的掩码,twos 表示第i位出现2次的掩码,threes 表示第i位出现1次的掩码

(2) ones 与当前数与,得到当前出现2次的位,与twos或,得到当前总共出现2次的位

(3) threes = ones & twos 得到当前出现3次的位

(3) 把出现3次的位在ones和twos中置0, ones &= ~threes, twos &= ~threes

int singleNumber(vector<int>& nums) {
    int n = nums.size();
    int ones = 0, twos = 0, threes = 0;
    for (int i = 0; i < n; i++) {
        twos |= ones & A[i];//出现1次的位和当前数相与,得到出现两次的位;与以前的twos或,得到目前总共出现两次的位
        ones ^= A[i];// 当前出现1次的位与当前数异或,求出当前只出现一次的位。
        threes = ones & twos;//出现1次和2次的相与,得出现3次的位
        ones &= ~threes;//将出现3次的位在ones中置0
        twos &= ~threes;//将出现3次的位在twos中置0
    }
    return ones;
}
View Code

 10 无序数组中,出两个数只出现一次外,另外两个数均出现两次。

思路:(1)全部异或,可以得到两个数的异或结果

        (2)找出异或结果中的为1的最低位,这一位可以将两数区分,同时任意相同的两位数会被分到同一边

    (3) 分两边异或,可分别得出结果

vector<int> singleNumber(vector<int>& nums) {
    int allsum = 0;
    for (int num : nums) {
        allsum ^= num;
    }
    int diff = (allsum & (allsum - 1)) ^ allsum;//两数最低的不同位
    //同时同样的两个数肯定会分在同一边,因此分为两部分做异或
    vector<int> result(2, 0);
    for (int num : nums) {
        if (diff & num) {
            result[1] ^= num;
        } else {
            result[0] ^= num;
        }
    }
    return result;
}
View Code

 11 升序单链表转换成二叉搜索树

思路: 递归,找到 (n - 1)/2作为根,递归构造左子树和右子树

注意:将左子树的尾节点的next置位空。

TreeNode* sortedListToBST(ListNode* head) {
    if (head == NULL) {
        return NULL;
    }
    if (head->next == NULL) {
        return new TreeNode(head->val);
    }
    int length = 0;
    ListNode* p = head;
    while (p) {
        length ++;
        p = p->next;
    }
    ListNode* pre = head;
    p = pre->next;
    for (int i = 1; i < (length - 1) / 2; i++) {
        pre = pre->next;
        p = p->next;
    }
    TreeNode* root = new TreeNode(p->val);
    pre->next = NULL;
    root->left = sortedListToBST(head);
    root->right = sortedListToBST(p->next);
    return root;
}
View Code

 12 包含1~n的数组,其中有些数字出现两个,其余为1次或者未出现,找出未出现的数字

思路:遍历数组,将每个值对应的数组元素变成负数,然后遍历数组,正数对应的index就是未出现的数字。

vector<int> findDisappearedNumbers(vector<int>& nums) {
    vector<int> result;
    for (int i = 0; i < nums.size(); i++) {
        int val = abs(nums[i]) - 1;
        if (nums[val] > 0) {
            nums[val] = -nums[val];
        }
    }
    for (int i = 0; i < nums.size(); i++) {
        if (nums[i] > 0) {
            result.push_back(i + 1);
        }
    }
    return result;
}
View Code

 13 二叉树是否是对称

思路:  递归

bool isMirror(TreeNode* t1, TreeNode * t2) {
    if (t1 == NULL && t2 == NULL) {
        return true;
    }
    if (t1 == NULL || t2 == NULL) {
        return false;
    }
    if (t1->val != t2->val) {
        return false;
    }
    return isMirror(t1->left, t2->right) & isMirror(t1->right, t2->left);
}
bool isSymmetric(TreeNode* root) {
    if (root == NULL || (root->left == NULL && root->right == NULL)) {
        return true;
    }
    return isMirror(root->left, root->right);
}
View Code

 14 数组的随机化

vector<int> shuffle() {
    vector<int> result(nums);
    for (int i = 0; i < nums.size(); i++) {
        int pos = rand() % nums.size();
        swap(result[i], result[pos]);
     }
     return result;
}    
View Code

 15 1~n 能够构建的所有二叉搜索树

思路:枚举根节点的值,递归构建左子树和右子树

vector<TreeNode*> helper(int start, int end) {
    if (start > end) {
        vector<TreeNode*> trees;
        trees.push_back(NULL);
        return trees;
    }
    if (start == end) {
        TreeNode* root =  new TreeNode(start);
        vector<TreeNode*> trees;
        trees.push_back(root);
        return trees;
    }
    vector<TreeNode*> result;
    for (int r = start; r <= end; r++) {
        vector<TreeNode*> ltrees = helper(start, r - 1);//左子树
        vector<TreeNode*> rtrees = helper(r + 1, end);//右子树
        for (TreeNode* left : ltrees) {
            for (TreeNode* right : rtrees) {
                TreeNode* root = new TreeNode(r);
                root->left = left;
            root->right = right;
                result.push_back(root);
            }
        }
    }
    return result;
}
vector<TreeNode*> generateTrees(int n) {
    vector<TreeNode*> result;
    if (n != 0) {
            result = helper(1, n);
    }
    return result; 
}    
View Code

 16 矩阵区域求和

思路:利用动态规划的思路,存储求和结果矩阵;加一行0加一列0,避免边界检查。

sum[i][j] = matrix[i-1][j-1] + sum[i-1][j] + sum[i][j-1] - sum[i-1][j-1]

vector<vector<int>> sum;
NumMatrix(vector<vector<int>> &matrix) {
    int rows = matrix.size();
    int cols = rows > 0 ? matrix[0].size() : 0;
    this->sum = vector<vector<int>>(rows + 1, vector<int>(cols + 1, 0));
    for (int i = 1; i <= rows; i++) {
        for (int j = 1; j <= cols; j++) {
            sum[i][j] = matrix[i - 1][j - 1] + sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1];
        }
    }
}

int sumRegion(int row1, int col1, int row2, int col2) {
    return sum[row2 + 1][col2 + 1] - sum[row2 + 1][col1] - sum[row1][col2 + 1] + sum[row1][col1];
}
View Code

 17 Repeated Substring Pattern

给一个非空字符串,判断其是否是由子串重复构造。

Input: "abab"

Output: True

Explanation: It's the substring "ab" twice.

思路:输入的第一个字符是重复子串的第一个字符,输入的最后一个字符是重复子串的最后一个字符。

两个输入串拼接,去掉开头和结尾字符,查找原字符串,如果能找原串,则返回True。

def repeatedSubstringPattern(self, str):
        ss = (str + str)[1 : -1]
        return ss.find(str) != -1
View Code

 18 1~n的阶乘末尾包含多少个0?

思路:包含0 的个数等于n!中包含5的个数。

By given number 4617.

5^1 : 4617 ÷ 5 = 923.4, so we get 923 factors of 5

5^2 : 4617 ÷ 25 = 184.68, so we get 184 additional factors of 5

5^3 : 4617 ÷ 125 = 36.936, so we get 36 additional factors of 5

5^4 : 4617 ÷ 625 = 7.3872, so we get 7 additional factors of 5

5^5 : 4617 ÷ 3125 = 1.47744, so we get 1 more factor of 5

5^6 : 4617 ÷ 15625 = 0.295488, which is less than 1, so stop here.

Then 4617! has 923 + 184 + 36 + 7 + 1 = 1151 trailing zeroes.

int trailingZeroes(int n) {
    int result = 0;
    for(long long i=5; n/i>0; i*=5){
        result += (n/i);
    }
    return result;
}
View Code

 19 判断给定的数是否是平方数

思路: 二分查找

bool isPerfectSquare(int num) {
    long long l = 0, r = num;
    while (l <= r) {
        long long mid = (l + r) >> 1;
        long long sqmid = mid * mid;
        if (sqmid > num) r = mid - 1;
        else if (sqmid < num) l = mid + 1;
        else return true;
    }
    return false;
}
View Code

 20 最长不重复子串

"abcabcbb" --> 3

思路:在每个字符多次出现的时候,从其上次出现的位置到当前位置的前一个位置作为一个子串,与已有的最大值比较,去最大值。

int lengthOfLongestSubstring(string s) {
    vector<int> cindex(256, -1);
    int maxlen = 0;
    int start = -1;
    for (int i = 0; i < s.length(); i++) {
        if (cindex[s[i]] > start) {
            start = cindex[s[i]];
        }
        cindex[s[i]] = i;
        maxlen = max(maxlen, i - start);
    }
    return maxlen;
}
View Code

 21 给定一个字符串,求出能够构成的ip地址

思路:

枚举ip地址每一段的长度(1~3),且保证剩余的每一段至少有一位;

然后通过字符串分割求出每一段的字符串;

校验每一段的合法性(不能大于255;如果长度大于1,不能以0开头)

bool valid(string seg) {
    if (seg.length() > 3 || seg.length() < 1) {
        return false;
    }
    if (seg.length() > 1 && seg[0] == '0') {
        return false;
    } 
    if (atoi(seg.c_str()) > 255) {
        return false;
    }
    return true;
}
vector<string> restoreIpAddresses(string s) {
    vector<string> result;
    int chars = s.length();
    for (int i = 1; i <= 3 && i <= chars - 3; i++) {
        for (int j = 1; j <=3 && j <= chars - i - 2; j++) {
            for (int k = 1; k <= 3 && k <= chars - i - j - 1; k++) {
                string first = s.substr(0, i);
                string second = s.substr(i, j);
                string third = s.substr(i + j, k);
                string last = s.substr(i + j + k);
                if (valid(first) && valid(second) && valid(third) && valid(last)) {
                    result.push_back(first + "." + second + "." + third + "." + last);
                }
            }
        }
    }
    return result;
}
View Code

 22 Poor Pigs

思路:按照可试验次数n来分配矩阵每一维的长度,每一头pig可以 在n - 1次找出每一维的坐标。

int poorPigs(int buckets, int minutesToDie, int minutesToTest) {
    int times = minutesToTest / minutesToDie;
    int pigs = 0;
    while (pow(times + 1, pigs) < buckets) {
    pigs++;
    }
    return pigs;
}
View Code

 23 从1开始的数字构成的字符串,返回第n位数字

思路:位数计算(9,90,900...),注意溢出

int findNthDigit(int n) {
        if (n < 10) {
            return n;
        }
        int digit_every_one = 1;
        long long num_cur_level = 9 * pow(10, digit_every_one - 1);
        int sumdigits = 0;
        while (sumdigits + num_cur_level < n) {
            sumdigits += num_cur_level;
            digit_every_one++;
            num_cur_level = 9 * pow(10, digit_every_one - 1) * digit_every_one;
        }
        int start = pow(10, digit_every_one - 1) - 1;
        int left = n - sumdigits;
        start += left / digit_every_one;
        left %= digit_every_one;
        if (left) {
            start++;
            left = digit_every_one - left;
            while (left) {
                start /= 10;
                left--;
            }
        }
        return start % 10;
    }
View Code

 24  从左到右升序;上一行的末尾小于本行开头数字;(整体升序排列)

思路:将坐标转换成一维数组表示,进行二分查找

bool searchMatrix(vector<vector<int>>& matrix, int target) {
    if (matrix.size() == 0) {
        return false;
    }
    int rows = matrix.size();
    int cols = matrix[0].size();
    int left = 0;
    int right = rows * cols - 1;
    while (left <= right) {
        int mid = left + (right - left) / 2;
        int mrow = mid / cols;
        int mcol = mid % cols;
        if (matrix[mrow][mcol] == target) {
            return true;
        } else if (matrix[mrow][mcol] < target) {
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }
    return false;
}
View Code

 25 行升序,列升序排列的矩阵查找

思路:从矩阵左下角 或者 右上角开始搜索(只用往一个方向进行变化)

bool searchMatrix(vector<vector<int>>& matrix, int target) {
    int rows = matrix.size();
    if (rows == 0) {
        return false;
    }
    int cols = matrix[0].size();
    int i = 0;
    int j = cols - 1;
    while (i < rows && j >= 0) {
        if (matrix[i][j] == target) {
            return true;
        } else if (matrix[i][j] > target) {
            j--;
        } else {
            i++;
        }
    }
    return false;
}
View Code

 

posted @ 2016-12-05 17:35  lrlthinking  阅读(186)  评论(0)    收藏  举报