面试题38:数字在排序数组中出现的次数
统计一个数字在排序数组中出现的次数,例如输出排序数组是{1,2,3,3,3,3,4,5}和数字3,由于3在数组中出现了4次,因此输出为4
leetcode链接 https://leetcode-cn.com/problems/zai-pai-xu-shu-zu-zhong-cha-zhao-shu-zi-lcof/class Solution { public: int findFirst(vector
nums, int target, int begin, int end) { if (begin > end) { return -1; } int mid = begin + (end - begin) / 2; if (nums[mid] == target) { if ((mid == 0) || (nums[mid - 1] !=target)) { return mid; } else { return findFirst(nums, target, begin, mid - 1); } } else if (nums[mid] > target) { return findFirst(nums, target, begin, mid - 1); } else{ return findFirst(nums, target, mid + 1, end); } return -1; } int findLast(vector nums, int target, int begin, int end) { if (begin > end) { return -1; } int mid = begin + (end - begin) / 2; if (nums[mid] == target) { if ((mid == nums.size() - 1) || (nums[mid + 1] !=target)) { return mid; } else { return findLast(nums, target, mid + 1, end); } } else if (nums[mid] > target) { return findLast(nums, target, begin, mid - 1); } else{ return findLast(nums, target, mid + 1, end); } return -1; } int search(vector & nums, int target) { // 二分法找到target第一次出现的位置和最后一次出现的位置 int result = 0; if (nums.size() == 0) { return result; } int last = findLast(nums, target, 0, nums.size() - 1); int begin = findFirst(nums, target, 0, nums.size() -1); if (last == -1 || begin == -1) { return 0; } // cout << "last: " << last << " begin: " << begin << endl; result = last - begin + 1; return result; } };
解题思路:由于是排序数组,可以用二分法,找到最开始的位置和最后的位置。
面试题39:二叉树深度
输入一棵二叉树的根节点,求该树的深度。
leetcode链接 https://leetcode-cn.com/problems/er-cha-shu-de-shen-du-lcof//** * 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 maxDepth(TreeNode* root) { int result = 0; if (root == NULL) { return result; } if (root->left == NULL && root->right == NULL) { return result + 1; } if (root->left == NULL) { return maxDepth(root->right) + 1; } if (root->right == NULL) { return maxDepth(root->left) + 1; } return max(maxDepth(root->left), maxDepth(root->right)) + 1; } };
解题思路:如果一个节点没有左右节点,则其深度为1。若只有右节点,深度为右节点深度+1,若只有左节点,节点深度为左子树+1。若有左右节点,则节点深度为左右子树中较大值+1。
题目2:输入一棵二叉树的根节点,判断该树是不是平衡二叉树。如果某二叉树中任意节点的的左子树和右子树的深度相差不超过1,那么它就是一棵平衡二叉树。
/** * 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 isBalanced(TreeNode* root, int& depth) { if (root == NULL) { depth = 0; return true; } int left, right; if (isBalanced(root->left, left) && isBalanced(root->right, right)) { if (abs(left - right) <= 1) { depth = max(left, right) + 1; return true; } } return false; } bool isBalanced(TreeNode* root) { if (root == NULL) { return true; } if (root->left == NULL && root->right == NULL) { return true; } // 通过后序遍历是左右根的特性,一边遍历一边记录,避免重复遍历 int depth = 0; return isBalanced(root, depth); } };
解题思路:
思路1:判断每个节点是不是平衡节点,但是会不停重复遍历很多节点,比较影响性能。
/**
* 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 depth(TreeNode* root)
{
if (root == NULL)
{
return 0;
}
if (root->left == NULL && root->right == NULL)
{
return 1;
}
if (root->left == NULL)
{
return depth(root->right) + 1;
}
if (root->right == NULL)
{
return depth(root->left) + 1;
}
return max(depth(root->left), depth(root->right)) + 1;
}
bool isBalanced(TreeNode* root) {
if (root == NULL)
{
return true;
}
if (root->left == NULL && root->right == NULL)
{
return true;
}
int left = depth(root->left);
int right = depth(root->right);
// cout << "root: " << root->val << " right: " << right << " left: " << left << endl;
if (abs(left - right) > 1)
{
return false;
}
return isBalanced(root->left) && isBalanced(root->right);
}
};
思路2:采用后序遍历,这样在遍历到根节点的时候,已经遍历过其左右子树,就可以判断当前跟节点是不是平衡节点了。这样就可以一边遍历,一边判断。
/**
* 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 isBalanced(TreeNode* root, int& depth)
{
if (root == NULL)
{
depth = 0;
return true;
}
int left, right;
if (isBalanced(root->left, left) && isBalanced(root->right, right))
{
if (abs(left - right) <= 1)
{
depth = max(left, right) + 1;
return true;
}
}
return false;
}
bool isBalanced(TreeNode* root) {
if (root == NULL)
{
return true;
}
if (root->left == NULL && root->right == NULL)
{
return true;
}
// 通过后序遍历是左右根的特性,一边遍历一边记录,避免重复遍历
int depth = 0;
return isBalanced(root, depth);
}
};
面试题40:数组中只出现一次的数字
一个整型数组内除了两个数字之外,其余数字都只出现了一次,找到这个只出现了一次的数字。
leetcode链接 https://leetcode-cn.com/problems/shu-zu-zhong-shu-zi-chu-xian-de-ci-shu-lcof/class Solution { public: bool checkNIsOne(int n, int index) { n = n >> index; return n & 1; } int findFirstOne(int n) { int index = 0; while((n & 1) == 0) // 注意&运算符优先级比==低,要加括号 { n = n >> 1; index++; } return index; } vector
singleNumbers(vector & nums) { // 分两步 // 1.所有数求异或,拿到结果,找到结果二进制表示中第一个为1的位数n // 2.按照n位是否为1将数组分为两部分,分别求异或 vector result; if (nums.size() == 0) { return result; } int xorVal = 0; for(int i = 0; i < nums.size(); i++) { xorVal = xorVal ^ nums[i]; } int index = findFirstOne(xorVal); int num1 = 0, num2 = 0; for(int i = 0; i < nums.size(); i++) { if (checkNIsOne(nums[i], index)) { num1 ^= nums[i]; } else { num2 ^= nums[i]; } } result.push_back(num2); result.push_back(num1); return result; } };
解题思路:相同数字异或后为0。先将数组从前往后遍历,求异或值,最终异或结果为两个值出现一次数字的异或结果。以后结果转化为二进制表示,一定有一位是1,找到数字中第一个为1的位,记为n。将数组分为两部分,n位为1的为一个数组,为0的为另一个数组,则两个只出现一次的数组一定分散在两个数组中,且相同数字一定分布在相同数组中。对两个数组分别进行遍历异或,即可得到只出现一次的值。
面试题41:和为s的两个数字vs和为s的连续正数数列
输入一个递增排序的数组和一个数字s,在数组中查找两个数,使其和为s,如果有多对数字和为s,输出任意一对即可
leetcode链接 https://leetcode-cn.com/problems/he-wei-sde-liang-ge-shu-zi-lcof/class Solution { public: vector
twoSum(vector & nums, int target) { vector result; if (nums.size() == 0) { return result; } if (nums.size() == 1) { if(nums[0] == target) { return nums; } else { return result; } } int begin = 0; int end = nums.size() - 1; while(begin < end) { if ((nums[begin] + nums[end]) < target) { begin++; } else if ((nums[begin] + nums[end]) > target) { end--; } else { result.push_back(nums[begin]); result.push_back(nums[end]); break; } } return result; } };
解题思路:双指针。一个指向头,一个指向尾。如果和大于s,尾指针前移。如果和小于s,头指针后移。
题目二:输入一个整数s,打印出所有和为s的连续正整数序列(至少含有两个数)。例如输入15,由于1+2+3+4=4+5+6=7+8=15,因此最终结果打印3个连续序列
leetcode链接 https://leetcode-cn.com/problems/he-wei-sde-lian-xu-zheng-shu-xu-lie-lcof/class Solution { public: vector
> findContinuousSequence(int target) { vector > result; if (target < 1) { return result; } if (target == 3) { vector tmp; tmp.push_back(1); tmp.push_back(2); result.push_back(tmp); return result; } int small = 1, big = 2; int sum = 3; while((big-small) >= 1 || big == 2) { // cout << big << " " << small << " sum: " << sum << endl; if (sum < target) { big++; sum = sum + big; } else if (sum == target) { vector tmp; for(int i = small; i <= big; i++) { tmp.push_back(i); } result.push_back(tmp); big++; sum = sum + big; } else { sum = sum - small; small++; } } return result; } };
解题思路:两个指针,small指针初始化为1,bigger指针初始化为2。求small-big之间序列的和,如果和小于s,增大bigger,如果和大于s,增加small。直到bigger-smaller=1,停止。过程表示为
步骤 | small | big | 序列 | 序列和 | 与s相比 | 下一步操作 |
---|---|---|---|---|---|---|
1 | 1 | 2 | 1,2 | 3 | 小于 | 增大big |
2 | 1 | 3 | 1,2,3 | 6 | 小于 | 增大big |
3 | 1 | 4 | 1,2,3,4 | 10 | 大于 | 增大small |
4 | 2 | 4 | 2,3,4 | 9 | 等于 | 打印序列,增大big |
5 | 2 | 5 | 2,3,4,5 | 14 | 大于 | 增大small |
6 | 3 | 5 | 3,4,5 | 12 | 大于 | 增大small |
7 | 4 | 5 | 4,5 | 9 | 等于 | 打印序列,停止 |
面试题42:翻转单词vs左旋字符串
输入一个英文句子,翻转句子中单词的顺序,但是单词中的字符顺序不变。为简单起见,标点符号的处理和普通字母相同。例如输入为“I am a student." 输出为”student. a am I“。
leetcode链接 https://leetcode-cn.com/problems/reverse-words-in-a-string/class Solution(object): def reverse(self, s): tmp = "" for i in range(1, len(s) + 1): tmp += s[len(s) - i] return tmp def reverseWords(self, s): """ :type s: str :rtype: str """ r = self.reverse(s) # 整个句子进行翻转 result = "" items = r.split(" ") for item in items: if item == '': continue result += self.reverse(item) # 每个单词进行翻转,翻转回去 result += ' ' result = result.strip(' ') return result
解题思路:先翻转整个句子。再按照空格分隔,翻转每个单词。最后就是只有单词顺序发生了翻转。递归实现。涉及字符串分割问题,写python了。主要是看此路,不然就直接
class Solution(object):
def reverseWords(self, s):
"""
:type s: str
:rtype: str
"""
result = ""
items = s.split(" ")
for i in range(1, len(items)):
if items[len(items) - i] == '':
continue
result += items[len(items) - i]
result += " "
result += items[0]
result = result.strip(" ")
return result
题目二:字符串的左旋操作就是把字符串前面的若干字符转移到字符串的尾部。请定义一个函数,实现左旋操作。比如字符串是"abcdefg",返回该函数左旋两位得到的结果"cdefgab"。
leetcode链接 https://leetcode-cn.com/problems/zuo-xuan-zhuan-zi-fu-chuan-lcof/class Solution { public: string reverse(string s, int begin, int end) { string result = ""; if (begin > end) { return ""; } for(int i = end; i >= begin; i--) { result += s[i]; } return result; } string reverseLeftWords(string s, int n) { string result; if (s.length() == 0) { return result; } string head = reverse(s, 0, (n-1) % s.length()); string tail = reverse(s, (n-1) % s.length() + 1, s.length() - 1); result = reverse(head + tail, 0, s.length() - 1); return result; } };
解题思路:将字符串分为两部分。n%len长度和剩余长度,分别进行旋转。譬如例子中n=2,则旋转后结果bagfedc,再将字符串整体进行翻转,得到cdefgab,就是最终的结果。这个方法思路和代码都很简洁,但是效率低。其实可以确定旋转点,将旋转点前面的子串移到后面去,就可以了。