面试题20. 表示数值的字符串
本题的题目描述比较简单,例子比较少,解法也有多种,比如DFA确定有限状态机(然鹅我不会,回头再去了解了解),那咋办呢,有个投机取巧的方法就是正则表达式匹配直接调用库函数,那就不说了。正常的解法那就按照题目的意思和规则办,有啥情况就分析啥情况,走一步看一步就完事。
代码如下(思路在注释):
class Solution {
public:
bool isNumber(string s) {
if(s.empty() || s==" ") return false;
int l =s.find_first_not_of(' ');//去掉前边的空格
int start = l;
int r =s.find_last_not_of(' ');//去掉后边的空格
bool numflag = false;//数值标记
bool eflag = false;//指数标记
bool dotflag = false;//点标记
for(;l<=r; ++l)
{
if(isdigit(s[l]))
numflag = true;
else if(s[l]=='.')
{
//.之前不能出现e或者.
if(dotflag || eflag)
return false;
dotflag = true;
}else if(s[l]=='e' || s[l]=='E')
{
//E之前不能出现E,且必须有数字
if(eflag || !numflag)
return false;
eflag = true;
numflag = false;//重置数字标志
}else if(s[l]=='+' || s[l]=='-')
{
//+-号必须在第一个位置或者在E之后的第一个位置
if(l!=start && s[l-1]!='e' && s[l-1]!='E')
return false;
}else
//其余不合法字符
return false;
}
return numflag;
}
};
面试题21. 调整数组顺序使奇数位于偶数前面
解法1:双指针
class Solution {
public:
vector<int> exchange(vector<int>& nums) {
//双指针法
int lo = 0, hi = nums.size()-1;
while(lo<hi)
{
while(lo<hi && (nums[lo]&1))
++lo;
while(lo<hi && (nums[hi]&1)==0)
--hi;
swap(nums[lo],nums[hi]);
}
return nums;
}
};
解法2:快慢指针
class Solution {
public:
vector<int> exchange(vector<int>& nums) {
//快慢指针
int slow = 0, fast = 0;
while(fast<nums.size())
{
if(nums[fast]&1)
swap(nums[slow++],nums[fast]);
++fast;
}
return nums;
}
};
面试题22. 链表中倒数第k个节点
思路:1、最直接的想法肯定是遍历一遍链表,然后统计节点个数n,然后从头节点开始顺序遍历,向前进n-k个节点,即到达倒数第k个节点,这样的情况下需要顺序遍历两次链表,因此时间复杂度有O(n^2),效率是较低的;2、链表的遍历思想中很容易想到一种思路,即双指针或者是快慢指针,本题可以引用双指针,初始化两个指针起始都指向头指针,然后指针p2先走k步,然后p1,p2在同时前进直到p2到达链表尾端,此时p1即倒数第k个节点,这样实际上只需要遍历一遍链表,即p2从头走到了尾,因此时间复杂度降低到O(n)。
class Solution {
public:
ListNode* getKthFromEnd(ListNode* head, int k) {
auto p1 = head;
auto p2 = head;
while(k--)
{
p2 = p2->next;
}
while(p2)
{
p2=p2->next;
p1=p1->next;
}
return p1;
}
};
面试题24. 反转链表
解法1:递归
class Solution {
public:
ListNode* reverseList(ListNode* head) {
//递归法
if(!head || !head->next) return head;
ListNode* newhead = reverseList(head->next);
head->next->next = head;
head->next = nullptr;
return newhead;
}
};
解法2:双指针
class Solution {
public:
ListNode* reverseList(ListNode* head) {
//双指针
ListNode* pre = nullptr, *cur = head;
while(cur)
{
auto post = cur->next;
cur->next = pre;
pre = cur;
cur = post;
}
return pre;
}
};
面试题25. 合并两个排序的链表
思路:整体思想和归并排序一样,定义一个伪头部dummy_head与一定前驱节点pre,然后对l1和l2两个链表进行遍历归并,直至某一链表值为空,最后再将非空的链表接上即可。
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
//伪头结点法
ListNode* dummy_head = new ListNode(-1);
auto pre = dummy_head;
while(l1 && l2)
{
if(l1->val<=l2->val)
{
pre->next = l1;
pre = l1;
l1 = l1->next;
}else
{
pre->next = l2;
pre = l2;
l2 = l2->next;
}
}
if(!l1) pre->next = l2;
else pre->next = l1;
return dummy_head->next;
}
};
面试题26. 树的子结构
解题思路:递归求解,1、定义一个辅助函数helper(),去判断两棵树是否相等;2、对树A进行遍历 ,以每一个节点为根节点的树与树B是否相等,如果存在相等,即B为A的子树。
代码如下:
class Solution {
public:
bool isSubStructure(TreeNode* A, TreeNode* B) {
//递归
if(!B || !A) return false;
if(A->val==B->val)
if(helper(A,B)) return true;
return isSubStructure(A->left,B) || isSubStructure(A->right,B);
}
private:
bool helper(TreeNode* A, TreeNode* B)
{
if(!B) return true;
if(!A && B) return false;
if(A->val!=B->val) return false;
return helper(A->left,B->left) && helper(A->right,B->right);
}
};
面试题27. 二叉树的镜像(翻转二叉树)
解法1:递归
class Solution {
public:
TreeNode* mirrorTree(TreeNode* root) {
//递归法
if(!root) return root;
auto left = mirrorTree(root->left);
auto right = mirrorTree(root->right);
root->left = right;
root->right = left;
return root;
}
};
解法2:辅助队列
class Solution {
public:
TreeNode* mirrorTree(TreeNode* root) {
//队列
if(!root) return root;
queue<TreeNode*> q;
q.push(root);
while(!q.empty())
{
auto cur = q.front();
q.pop();
auto tmp = cur->left;
cur->left = cur->right;
cur->right = tmp;
if(cur->left) q.push(cur->left);
if(cur->right) q.push(cur->right);
}
return root;
}
};
今日刷题笔记顺利完成,今日更多的题目是关于链表与树的内容,因此有许多的递归应用。同时也能感觉到链表中常用的方法:双指针可以优化链表的遍历或操作。明日继续!!!