LeetCode 69. Sqrt(x)

题目描述:

Implement int sqrt(int x).

Compute and return the square root of x.

实现整数的开平方运算, 使用二分搜索, 也就是方程求根的二分法来计算. 因为计算结果为整数, 所以最后如果没有平方根的准确值, 要把最后的结果减1.

class Solution {
public:
    int mySqrt(int x) {
        if(x == 1) return 1;
        long long left = 1, right = x, mid = (left + right) / 2;
        while(left < right){
            long long m = mid * mid;
            if(m > x) 
                right = mid;
            else if(m < x)
                left = mid + 1;
            else 
                return mid;
            mid = (left + right) / 2;
        }
        return left - 1;
    }
};

LeetCode 68. Text Justification

题目描述:

Given an array of words and a length L, format the text such that each line has exactly L characters and is fully (left and right) justified.

You should pack your words in a greedy approach; that is, pack as many words as you can in each line. Pad extra spaces ’ ’ when necessary so that each line has exactly L characters.

Extra spaces between words should be distributed as evenly as possible. If the number of spaces on a line do not divide evenly between words, the empty slots on the left will be assigned more spaces than the slots on the right.

For the last line of text, it should be left justified and no extra space is inserted between words.

For example,

words: ["This", "is", "an", "example", "of", "text", "justification."]

L: 16.

Return the formatted lines as:

[
   "This    is    an",
   "example  of text",
   "justification.  "
]

Note: Each word is guaranteed not to exceed L in length.

细节比较多, 难度倒也不是很大, 但是这种题在面试的时候我觉得更难, 因为考虑许多情况, 人肉调试还是比较困难的.

class Solution {
public:
    vector<string> fullJustify(vector<string>& words, int maxWidth) {
        vector<string> ret;
        if(maxWidth == 0) { // 因为每个单词长度都不会超过maxWidth, 所以可以直接返回
            ret.push_back(string(""));
            return ret;
        }
        // start保存每行单词的起始下标, end表示结束下标, 单词的下标范围为[start, end)
        // lenInLine保存每一行的长度
        int start = 0, end = 0, lenInLine = 0;
        while (start < words.size()) {
            // 计算这一行能放多少个单词, 把第一个单词放进去, +1是因为后面还有一个空格
            lenInLine = words[start].length() + 1; 
            end = start + 1; 
            while (end < words.size() && lenInLine < maxWidth) {
                lenInLine += words[end++].length();
                lenInLine += 1; // 计算空格
            }
            if(lenInLine - 1 > maxWidth){ // 去掉尾部空格后如果还是大于maxWidth, 那么应该少放一个单词
                end--;
            }
            if (end == words.size()) { // 根据题目, 最后一行要单独处理, 使用左对齐而不是两端对齐
                string line = words[start];
                for (int i = start + 1; i < end; i++) {
                    line += " ";
                    line += words[i];
                }
                line.insert(line.end(), maxWidth - line.length(), ' ');
                ret.push_back(line);
            }
            else {
                createLine(words, start, end, ret, maxWidth); // 创建行
            }
            start = end;
        }
        return ret;
    }

    void createLine(vector<string> &words, int start, int end, vector<string> &ret, int maxWidth) {
        string line;
        int wordCnt = end - start, spacePerInterval = 0, spaceCnt = maxWidth;
        if (wordCnt == 0) return; // end == start, 没有单词
        for (int i = start; i < end; i++) { // 计算总空格数
            spaceCnt -= words[i].length();
        }
        // 如果spaceCnt不能平均分配, 那么左边的一个或多个间隔就要增加一个空格
        // moreSpaceLen保存左边的多少个间隔需要多的空格
        int moreSpaceLen = spaceCnt % (wordCnt - 1 ? wordCnt - 1 : 1);
        spacePerInterval = spaceCnt / (wordCnt - 1 ? wordCnt - 1 : 1);
        line += words[start];
        if(wordCnt == 1){
            // 因为下面的循环从start + 1开始, 所以如果行内只有一个单词,
            // 就要在其之后填充空格直到长度达到maxWidth
            line.insert(line.end(), maxWidth - line.length(), ' ');
        }
        else{
            for (int i = start + 1; i < end; i++) {
                if (i <= start + moreSpaceLen) {
                    line.insert(line.end(), spacePerInterval + 1, ' ');
                }
                else {
                    line.insert(line.end(), spacePerInterval, ' ');
                }
                line += words[i];
            }
        }
        ret.push_back(line);
    }
};

LeetCode 67. Add Binary

题目描述:

Given two binary strings, return their sum (also a binary string).

For example,

a = "11"

b = "1"

Return "100".

模拟加法计算, 只不过是使用二进制格式.

class Solution {
public:
    string addBinary(string a, string b) {
        if(a.length() < b.length()) swap(a, b);
        int pa = a.length() - 1, pb = b.length() - 1, jw = 0;
        while(pb >= 0){
            a[pa] = a[pa] - '0' + b[pb] - '0' + jw + '0';
            if(a[pa] >= '2'){
                a[pa] = a[pa] % '2' + '0';
                jw = 1;
            }
            else{
                jw = 0;
            }
            pa--, pb--;
        }
        while(pa >= 0){
            a[pa] = a[pa] + jw;
            if(a[pa] >= '2'){
                a[pa] = a[pa] % '2' + '0';
                jw = 1;
            }
            else{
                jw = 0;
            }
            pa--;
        }
        if(jw){
            a.insert(a.begin(), '1');
        }
        return a;
    }
};

LeetCode 66. Plus One

题目描述:

Given a non-negative number represented as an array of digits, plus one to the number.

The digits are stored such that the most significant digit is at the head of the list.

数组模拟计算, 由于只加1, 所以比较简单.

class Solution {
public:
    vector<int> plusOne(vector<int> &digits) {
        vector<int> ret = digits;
        int jw = 1;
        for(int i = ret.size() - 1; i >= 0 && jw; i--){
            ret[i]++;
            if(ret[i] == 10){
                jw = 1;
                ret[i] = 0;
            }
            else{
                jw = 0;
            }
        }
        if(jw){
            ret.insert(ret.begin(), 1);
        }
        return ret;
    }
};

LeetCode 65. Valid Number

题目描述:

Validate if a given string is numeric.

Some examples:

"0" => true

" 0.1 " => true

"abc" => false

"1 a" => false

"2e10" => true

Note: It is intended for the problem statement to be ambiguous. You should gather all requirements up front before implementing one.

判断一个输入字符串是否是数字, 这道题的要求并不是很明确, 一下子很难想的全面, 并且也不容易跟出题人想到一块去. 实际上主要有以下几点:

  • 字符串前后可以有空格, 但是数字中间不能出现空格
  • 允许出现的字符有+, -, ., e和阿拉伯数字
  • 数字可以以.开头或结尾
  • e后面的指数部分只能是整数
class Solution {
public:
    bool isNumber(string s) {
        delSpace(s);
        for(int i = 0; i < s.length(); i++){ // 检查除e, ., 和数字以外的字符
            if((s[i] < '0' || s[i] > '9')  && s[i] != 'e' && s[i] != '.' && s[i] != '-' && s[i] != '+') return false;
        }
        int eCnt = 0;
        for(int i = 0; i < s.length(); i++){ // 计算e的个数
            if(s[i] == 'e') eCnt++;
        }
        //printf("%d\n", eCnt);
        if(eCnt > 1) return false; // e多于1个则返回false
        if(eCnt == 1){
            // 如果有e出现, 前后都有不含e的数字
            int i;
            for(i = 0; s[i] != 'e'; i++);
            // e的前面可以是小数, 但是后面不可以
            if(!isNumberWithoutE(s.substr(0, i)) || !isNumberWithoutEAndDot(s.substr(i + 1, s.length() - i - 1))){
                return false;
            }
            else return true;
        }
        else{
            // 如果没有e
            return isNumberWithoutE(s);
        }
    }
    
    bool isNumberWithoutE(string s){ // 判断是不是不包含e的数字
        if(s.empty()) return false;
        if(s.length() == 1 && (s[0] == '.' || s[0] == '-' || s[0] == '+')) return false; // 只有符号或小数点
        int dashPos = -1, dotCnt = 0, plusSignPos = -1;
        for(int i = 0; i < s.length(); i++){ // 查找是否有不位于开头的正负号
            if(s[i] == '-') dashPos = i;
            else if(s[i] == '+') plusSignPos = i;
        }
        if(dashPos > 0 || plusSignPos > 0) return false;
        if(s[0] == '+' || s[0] == '-'){ // 清除开头的正负号
            s.erase(s.begin(), s.begin() + 1);
        }
        if(s.length() == 1 && s[0] == '.') return false; // 如果去掉符号后只有小数点了, 那么不是数字
        for(int i = 0; i < s.length(); i++){ // 计算小数点的数量
            if(s[i] == '.') dotCnt++;
        }
        if(dotCnt > 1) return false;
        else return true;
    }
    
    bool isNumberWithoutEAndDot(string s){ //判断是不是不包含e的整数
        if(s.empty()) return false;
        if(s.length() == 1 && (s[0] == '.' || s[0] == '-' || s[0] == '+')) return false;
        int dashPos = -1, plusSignPos = -1;
        for(int i = 0; i < s.length(); i++){ // 查找是否有不位于开头的正负号
            if(s[i] == '-') dashPos = i;
            else if(s[i] == '+') plusSignPos = i;
            else if(s[i] == '.') return false; // 如果有小数点则返回false
        }
        if(dashPos > 0 || plusSignPos > 0) return false;
        else return true;
    }
    
    void delSpace(string &s){ //删除前后空格
        int i;
        for(i = 0; i < s.size() && s[i] == ' '; i++);
        s.erase(s.begin(), s.begin() + i);
        for(i = 0; i < s.size() && s[s.size() - i - 1] == ' '; i++);
        s.erase(s.end() - i, s.end());
    }
};

LeetCode 64. Minimum Path Sum

题目描述:

Given a m x n grid filled with non-negative numbers, find a path from top left to bottom right which minimizes the sum of all numbers along its path.

Note: You can only move either down or right at any point in time.

动态规划问题, 到达每个格子的最小的路径和等于到达左边左边格子的路径和与到达上面的格子的路径和中的较小值加上当前格子的值.

class Solution {
public:
    int minPathSum(vector<vector<int> > &grid) {
        int height = grid.size(), width = grid[0].size();
        
        for(int i = 1; i < height; i++)
            grid[i][0] += grid[i - 1][0];
        for(int i = 1; i < width; i++)
            grid[0][i] += grid[0][i - 1];
            
        for(int i = 1; i < height; i++){
            for(int j = 1; j < width; j++){
                grid[i][j] += (grid[i - 1][j] < grid[i][j - 1] ? grid[i - 1][j] : grid[i][j - 1]);
            }
        }
        
        return grid[height - 1][width - 1];
    }
};

LeetCode 63. Unique Paths II

题目描述

Follow up for “Unique Paths”:

Now consider if some obstacles are added to the grids. How many unique paths would there be?

An obstacle and empty space is marked as 1 and 0 respectively in the grid.

For example, There is one obstacle in the middle of a 3x3 grid as illustrated below.

[
  [0,0,0],
  [0,1,0],
  [0,0,0]
]

The total number of unique paths is 2.

Note: m and n will be at most 100.

紧跟着上一题Unique Paths, 这一题增加了条件, 在地图上会出现障碍物(用1表示), 障碍物不能出现在路线上. 仍然采用上一题的动态规划法, 只不过多了一条:

  • 所有障碍物的位置到达的路线数量都为0
class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        int m = obstacleGrid.size();
        if(m == 0)
            return 0;
        int n = obstacleGrid[0].size();
        vector<vector<int>> arr(m, vector<int>(n, 0));
        int i, j;
        for(i = 0; i < m && obstacleGrid[i][0] != 1; i++)
            arr[i][0] = 1;
        for(i = 0; i < n && obstacleGrid[0][i] != 1; i++)
            arr[0][i] = 1;
        for(i = 1; i < m; i++){
            for(j = 1; j < n; j++){
                if(obstacleGrid[i][j])
                    continue;
                    
                arr[i][j] = arr[i - 1][j] + arr[i][j - 1];
            }
        }
        
        return arr[m - 1][n - 1];
    }
};

LeetCode 62. Unique Paths

题目描述:

A robot is located at the top-left corner of a m x n grid (marked ‘Start’ in the diagram below).

The robot can only move either down or right at any point in time. The robot is trying to reach the bottom-right corner of the grid (marked ‘Finish’ in the diagram below).

How many possible unique paths are there?

Above is a 3 x 7 grid. How many possible unique paths are there?

Note: m and n will be at most 100.

经典的动态规划题目, 由于每一个格子只能从上面的格子和左面的格子到达, 所以到达每个格子的路线数量等于到达上面格子的路线数量+到达左面格子的路线数量.

class Solution {
public:
    int uniquePaths(int m, int n) {
        int arr[m][n];
        int i, j;
        for(i = 0; i < m; i++)
            arr[i][0] = 1;
        for(i = 0; i < n; i++)
            arr[0][i] = 1;
        for(i = 1; i < m; i++){
            for(j = 1; j < n; j++){
                arr[i][j] = arr[i - 1][j] + arr[i][j - 1];
            }
        }
        
        return arr[m - 1][n - 1];
    }
};

LeetCode 61. Rotate List

题目描述:

Given a list, rotate the list to the right by k places, where k is non-negative.

For example:

Given 1->2->3->4->5->NULL and k = 2,

return 4->5->1->2->3->NULL.

最简单的方法是把所有节点指针保存在一个数组中, 设总长度为len, 然后找到第len - k个节点为新链表的头结点, 它之前的节点是新链表的尾节点. 把原链表的尾节点指向头结点即可. 要注意k可能会大于len, 所以可以先把klen取余.

/**
* Definition for singly-linked list.
* struct ListNode {
*     int val;
*     ListNode *next;
*     ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
    ListNode* rotateRight(ListNode* head, int k) {
        if(!head)
            return head;
        vector<ListNode*> l;
        ListNode* p = head, *reHead;
        while(p){
            l.push_back(p);
            p = p->next;
        }
        
        k = k % l.size();
        if(k == 0)
            return head;
        auto iter = l.end() - k;
        (*(iter - 1))->next = NULL;
        (*(l.end() - 1))->next = *(l.begin());
        return *iter;
    }
};

不使用额外空间的方法需要多遍历几次链表:

class Solution {
public:
    ListNode* rotateRight(ListNode* head, int k) {
        if(!head)
            return head;
        int len = 0;
        ListNode *p = head, *newHead = nullptr, *tail = nullptr, *newTail = nullptr;
        while(p){
            len++;
            if(!p->next)
                tail = p;
            p = p->next;
        }
        k = k % len;
        if(!k) return head;
        newHead = head;
        for(int i = 0; i < len - k; i++){
            newTail = newHead;
            newHead = newHead->next;
        }
        newTail->next = nullptr;
        tail->next = head;
        return newHead;
    }
};

LeetCode 60. Permutation Sequence

题目描述:

The set [1,2,3,…,n] contains a total of n! unique permutations.

By listing and labeling all of the permutations in order, We get the following sequence (ie, for n = 3):

  1. “123”
  2. “132”
  3. “213”
  4. “231”
  5. “312”
  6. “321”

Given n and k, return the kth permutation sequence.

Note: Given n will be between 1 and 9 inclusive.

返回[1,n]n个数字的全排列按照字典序排序后的第k个排列, n在[1,9]的范围内.

通过观察题目给出的n = 3的全排列可以看出, 以1, 2, 3开头的排列各有两个. n的全排列有n!种, 而以[1,n]中某个数字开头的排列种数有(n - 1)!种, 所以第k个排列的第一个数为(k - 1) / (n - 1)! + 1. 将k更新为k % (n - 1)!, 把第一个已经确定的数从数字集合中去掉(该集合应有序), 就可以以类似递归的方法确定整个序列.

实际上每一次确定数字在集合数组中的下标比直接确定数字要更加有效. 而且由于n在1到9之间, 因此可以先把n!计算出来放到数组中.

class Solution {
public:
    vector<int> arr = {0, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880};
    string getPermutation(int n, int k) {
        int kk = k - 1;
        vector<int> nums;
        for(int i = 1; i <= n; i++){
            nums.push_back(i); //nums[i]的值实际是i + 1
        }
        string ret;
        for(int i = 0; i < n; i++){
            if(i == n - 1){
                ret.push_back(nums[0] + '0'); //i = n - 1时下方会出现除零错误
                break;
            }
            int t = kk / arr[n - 1 - i];
            ret.push_back(nums[t] + '0'); //因为nums[i] = i + 1, 所以不需要再加1
            nums.erase(nums.begin() + t); //把已经确定的元素删除
            kk = kk % arr[n - i - 1];
        }
        return ret;
    }
};