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]; }
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; }
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); }
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; }
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; }
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; }
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(); }
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; }
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; }
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; }
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; }
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; }
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); }
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; }
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; }
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]; }
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
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; }
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; }
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; }
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; }
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; }
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; }
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; }
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; }

浙公网安备 33010602011771号