名企高频笔试题目(1)

二叉树的之字形层次遍历

题目描述

给定一个二叉树,返回该二叉树的之字形层序遍历,(第一层从左向右,下一层从右向左,一直这样交替)
例如:
给定的二叉树是{3,9,20,#,#,15,7},
img
该二叉树之字形层序遍历的结果是

[

[3],

[20,9],

[15,7]

]

示例1

输入
{1,#,2}
输出
[[1],[2]]

分析:

经典的二叉树层次遍历,需要做一点点的改动。

vector<vector<int> > zigzagLevelOrder(TreeNode* root) {
	// write code here
	queue<TreeNode*> q;  //双端队列,保存每一层的节点
	vector<vector<int>> res;
	if (!root) return res;
	q.push(root);  //根节点入队
	int k = 0;
	while (!q.empty()) {
		vector<int> temp;
		int len = q.size();  //亦可设置哨兵为每一层的最后节点,不过当前做法写起来舒适一些
		for (int i = 0; i < len; ++i)
		{
			if (q.front()->left)  q.push(q.front()->left);
			if (q.front()->right)  q.push(q.front()->right);
			temp.push_back(q.front()->val);
			q.pop();
		}
		if (k % 2)  reverse(temp.begin(), temp.end());
		++k;
		res.push_back(temp);  //局部变量temp自动clear()
	}
	return res;
}

二叉树的镜像

题目描述

操作给定的二叉树,将其变换为源二叉树的镜像。

输入描述:
二叉树的镜像定义:源二叉树 
    	    8
    	   /  \
    	  6   10
    	 / \  / \
    	5  7 9 11
    	镜像二叉树
    	    8
    	   /  \
    	  10   6
    	 / \  / \
    	11 9 7  5
class Solution {
public:
    void Mirror(TreeNode *pRoot) {  //递归处理
        if(!pRoot)  return;
        swap(pRoot->left,pRoot->right);  //交换指针
        Mirror(pRoot->left);
        Mirror(pRoot->right);
    }
};

设计getMin功能的栈

题目描述

实现一个特殊功能的栈,在实现栈的基本功能的基础上,再实现返回栈中最小元素的操作。

示例1

输入
[[1,3],[1,2],[1,1],[3],[2],[3]]
输出
[1,2]
备注:
有三种操作种类,op1表示push,op2表示pop,op3表示getMin。你需要返回和op3出现次数一样多的数组,表示每次getMin的答案

1<=操作总数<=1000000
-1000000<=每个操作数<=1000000
数据保证没有不合法的操作

分析:

这个题目需要保存最小值,肯定不能每次取出所有元素然后排序,这做法开销忒大。

一般做法就是已知范围内的最小值与当前节点关联起来。可以额外开一个大小一样的最小值栈,也可以如下。

class Solution {
public:
	/**
	 * return a array which include all ans for op3
	 * @param op int整型vector<vector<>> operator
	 * @return int整型vector
	 */
	stack<pair<int, int>> stk;
	vector<int> getMinStack(vector<vector<int> >& op) {
		// write code here
		vector<int> res;
		for (auto opt : op)
			if (opt[0] == 1)  push(opt[1]);
			else if (opt[0] == 2) pop();
			else res.push_back(stk.top().second);  //first是当前节点,second是最小节点
		return res;
	}
	void push(int elem) {
		if (!stk.empty() && elem > stk.top().second)
			stk.push(make_pair(elem, stk.top().second));
		else  stk.push(make_pair(elem, elem));  //stk空或当前节点更小
	}
	void  pop() {
		stk.pop();
	}
};

最长的括号子串

题目描述

给出一个仅包含字符'('和')'的字符串,计算最长的格式正确的括号子串的长度。

对于字符串"(()"来说,最长的格式正确的子串是"()",长度为2.

再举一个例子:对于字符串")()())",来说,最长的格式正确的子串是"()()",长度为4.

示例1

输入
"(()"
输出
2

利用栈记录字符串的下标

int longestValidParentheses(string s) {
    // write code here
    stack<int> st;
    int res = 0, len = s.length(), cur = -1;
    for (int i = 0; i < len;++i) {
        if (s[i] == ')')
            if (st.empty()) cur = i;  //没有左括号可以匹配
            else {
                st.pop();
                if (st.empty())
                    res = max(res, i - cur);  //所有的左括号都匹配成功
                else
                    res = max(res, i - st.top());  //有左括号未匹配成功
            }
        else st.push(i);  //左括号下标入栈
    }
    return res;
}

重复数字有序数组的二分查找

题目描述

请实现有重复数字的有序数组的二分查找。

输出在数组中第一个大于等于查找值的位置,如果数组中不存在这样的数,则输出数组长度加一。

输入
5,4,[1,2,4,4,5]
输出
3

这里实现的是STL中lower_bound()的功能,看一个vs2019的实现

// FUNCTION TEMPLATE lower_bound
template <class _FwdIt, class _Ty, class _Pr>
_NODISCARD _CONSTEXPR20 _FwdIt lower_bound(_FwdIt _First, const _FwdIt _Last, const _Ty& _Val, _Pr _Pred) {
    // find first element not before _Val, using _Pred
    _Adl_verify_range(_First, _Last);  //检查区间合法性
    auto _UFirst                = _Get_unwrapped(_First);  //left指针
    _Iter_diff_t<_FwdIt> _Count = _STD distance(_UFirst, _Get_unwrapped(_Last));  //区间长度

    while (0 < _Count) { // divide and conquer, find half that contains answer
        const _Iter_diff_t<_FwdIt> _Count2 = _Count / 2;  
        const auto _UMid                   = _STD next(_UFirst, _Count2);
        if (_Pred(*_UMid, _Val)) { // try top half
            _UFirst = _Next_iter(_UMid);
            _Count -= _Count2 + 1;
        } else {
            _Count = _Count2;
        }
    }

    _Seek_wrapped(_First, _UFirst);
    return _First;
}

template <class _FwdIt, class _Ty>
_NODISCARD _CONSTEXPR20 _FwdIt lower_bound(_FwdIt _First, _FwdIt _Last, const _Ty& _Val) {
    // find first element not before _Val, using operator<
    return _STD lower_bound(_First, _Last, _Val, less<>());  //默认情况下序列升序排列
}

针对这个题目的简化版:

    int upper_bound_(int n, int v, vector<int>& a) {
        // write code here
        if(a.back() < v)  return n + 1;
        int l = 0,r = n,m = 0;  //区间左闭右开
        while(l != r){
            m = (l + r)>>1;  //上取整
            if(a[m] >= v) r = m;
            else l = m + 1;
        }
        return l + 1;
    }

二叉树是否存在节点和为指定值的路径

题目描述

给定一个二叉树和一个值sum,判断是否有从根节点到叶子节点的节点值之和等于sum的路径,
例如:
给出如下的二叉树,sum=22
img
返回true,因为存在一条路径 5→ 4→11→2的节点值之和为 22

这个题目跟华为13年的题整型数组分组类似,考察逻辑运算符“或”的递归

    bool hasPathSum(TreeNode* root, int sum) {
        // write code here
        if(root){
            if(!root->left&&!root->right&&sum == root->val) return true;
            else return hasPathSum(root->left,sum - root->val)||hasPathSum(root->right,sum - root->val);
        }
        else return false;  //节点为空,返回假
    }

数组相加和为零的三元组

题目描述

给出一个有n个元素的数组S,S中是否有元素a,b,c满足a+b+c=0?找出数组S中所有满足条件的三元组。

注意:

  1. 三元组(a、b、c)中的元素必须按非降序排列。(即a≤b≤c)
  2. 解集中不能包含重复的三元组。
例如,给定的数组 S = {-1 0 1 2 -1 -4},解集为(-1, 0, 1) (-1, -1, 2)
class Solution {
public:
    vector<vector<int> > threeSum(vector<int>& num) {
        sort(num.begin(), num.end());  //排序
        vector<vector<int> >res;
        int len = num.size();
        for (int i = 0; i < len; ++i) {
            if (num[i] > 0)   break;
            else if (i > 0 && num[i] == num[i - 1])  continue;  //去重
            int j = i + 1;  //双指针夹逼
            int k = len - 1;
            while (j < k)
            {
                if (num[i] + num[j] + num[k] == 0) {
                    res.emplace_back(vector<int>{num[i], num[j], num[k]});
                    while (j < k && num[j] == num[j + 1])  ++j;  //去重
                    while (j < k && num[k] == num[k - 1])  --k;  //去重
                    ++j;
                    --k;
                }
                else if (num[i] + num[j] + num[k] < 0) ++j;
                else --k;
            }
        }
        return res;
    }
};

合并有序列表

题目描述

将两个有序的链表合并为一个新链表,要求新的链表是通过拼接两个链表的节点来生成的。

class Solution {
public:
	/**
	 *
	 * @param l1 ListNode类
	 * @param l2 ListNode类
	 * @return ListNode类
	 */
	ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
		// write code here
		ListNode* res = new ListNode(0);  //头节点
		ListNode* cur = res;  //遍历指针
		while (l1 && l2) {  //当两条链表都不为空
			if (l1->val < l2->val) {  //选择节点值小者加入新链表
				cur->next = l1;
				l1 = l1->next;
			}
			else {
				cur->next = l2;
				l2 = l2->next;
			}
			cur = cur->next;
		}
		cur->next = l1 == nullptr ? l2 : l1;  //若有链表不为空则加入新链表尾部
		return res->next;
	}
};

判断链表中是否有环

题目描述

判断给定的链表中是否有环

扩展:

你能给出空间复杂度img的解法么?

双指针法。

class Solution {
public:
    bool hasCycle(ListNode *head) {
        if(head == nullptr || head->next == nullptr)
            return false;
        ListNode* fast = head->next->next;
        ListNode* slow = head;
        while(fast->next&&fast){  //链表长度可能是奇数或者偶数
            fast = fast->next->next;
            slow = slow->next;
            if(fast == slow)
                return true;
        }
        return false;
    }
};

寻找第K大的数

题目描述

有一个整数数组,请你根据快速排序的思路,找出数组中第K大的数。

给定一个整数数组a,同时给定它的大小n和要找的K(K在1到n之间),请返回第K大的数,保证答案存在。

测试样例:

[1,3,5,2,2],5,3
返回:2

topK的题目,指定用快排思想的话,每次只排含有第K大数的一边。

class Finder {
public:
	int findKth(vector<int> a, int n, int K) {
		// write code here
		int start = 0, end = n - 1,cur = -1;  //初始化
		--K;  //数组下标从零开始,K从1开始
		while (cur!=K) {
			cur = partition(a, start, end);
			if (cur > K)
				end = cur - 1;
			else
				start = cur + 1;

		}
		return a[K];
	}
	int partition(vector<int>& a, int start, int end)  //从大到小排序
	{
		int i = start, j = end, temp = a[start];
		while (i < j) {
			while (a[j] < temp && i != j)  //从右往左找大于支点
				--j;
			if (i < j)  //右往左调整
				a[i++] = a[j];
			while (a[i] > temp && i != j)  //从左往右找小于支点
				++i;
			if (i < j)  //左往右调整
				a[j--] = a[i];
		}
		a[i] = temp;  //回填支点
		return i;
	}
};

求解平方根

题目描述

实现函数 int sqrt(int x).

计算并返回x的平方根

int sqrt(int x) {
	// write code here
	float x0 = 1.0, x1 = x0 - (pow(x0, 2) - x) / (2 * x0);  //牛顿迭代法,二阶可导收敛
	while (fabs(x1 - x0) > 1e-7) {  //迭代终止条件
		x0 = x1;
		x1 = x0 - (pow(x0, 2) - x) / (2 * x0);
	}
	return x1;
}

设计LRU缓存结构

题目描述

设计LRU缓存结构,该结构在构造时确定大小,假设大小为K,并有如下两个功能

  • set(key, value):将记录(key, value)插入该结构
  • get(key):返回key对应的value值

[要求]

  1. set和get方法的时间复杂度为O(1)
  2. 某个key的set或get操作一旦发生,认为这个key的记录成了最常使用的。
  3. 当缓存的大小超过K时,移除最不经常使用的记录,即set或get最久远的。

若opt=1,接下来两个整数x, y,表示set(x, y)
若opt=2,接下来一个整数x,表示get(x),若x未出现过或已被移除,则返回-1
对于每个操作2,输出一个答案

c++
class Solution {
public:
	vector<int> LRU(vector<vector<int> >& operators, int k) {
		// write code here
		vector<int> res;
		len = k;
		for (auto opt : operators)
			if (opt[0] == 1)
				set(opt[1], opt[2]);
			else
				res.push_back(get(opt[1]));
		return res;
	}
	void set(int key, int value)  //设置键值对
	{
		inUse.push_front(make_pair(key, value));  //最近使用在前
		m[key] = inUse.begin();  //使用iterator,而不是下标,因为链表变更
		if (inUse.size() > len)
		{
			m.erase(inUse.back().first);  //同步维护哈希表和链表
			inUse.pop_back();
		}
	}
	int get(int key)  //获取并更新键值对
	{
		auto it = m.find(key);
		if (it == m.end())  return -1;  //未加入或已移出
		else {
			inUse.splice(inUse.begin(), inUse, it->second);  //splice函数把原片段拼接后删除原片段
			return it->second->second;
		}
	}
private:
	int len;  //缓存结构长度
	list<pair<int, int>> inUse;  //正在使用中的链表
	unordered_map<int, list<pair<int, int>>::iterator> m;  //使set,get复杂度为O(1)
};

链表第一个公共节点

题目描述

输入两个链表,找出它们的第一个公共结点。(注意因为传入数据是链表,所以错误测试数据的提示是用其他方式显示的,保证传入数据是正确的)

class Solution {
public:
	ListNode* FindFirstCommonNode(ListNode* pHead1, ListNode* pHead2) {
		stack<ListNode*> st1, st2;  //关键是理解两链表的形状是‘Y’型
		ListNode* ans = nullptr;
		while (pHead1){  //链表一入栈
			st1.push(pHead1);
			pHead1 = pHead1->next;
		}
		while (pHead2) {  //链表二入栈
			st2.push(pHead2);
			pHead2 = pHead2->next;
		}
		while (!st1.empty() && !st2.empty() && st1.top() == st2.top())
		{
			ans = st1.top();
			st1.pop();  //弹出公共节点
			st2.pop();
		}
		return ans;
	}
};

合并K个已排序的链表

题目描述

合并 k 个已排序的链表并将其作为一个已排序的链表返回。分析并描述其复杂度。
使用优先队列

class Solution {
public:
    struct compare{  //需要自定义比较结构体
        bool operator()(const ListNode* l1,const ListNode* l2)const{
            return l1->val > l2->val;
        }
    };
    ListNode *mergeKLists(vector<ListNode *> &lists) {
        priority_queue<ListNode*,deque<ListNode*> ,compare> pq;
        for(auto l:lists)
            if(l)  pq.push(l);  //将各个链表的头节点入队
        ListNode* res = new ListNode(0);  //头节点
        ListNode* p = res;
        while(!pq.empty())
        {
            ListNode* q = pq.top();
            pq.pop();
            if(q->next)  pq.push(q->next);  //逐个处理各个元素
            p = p->next = q;  //链式赋值,从右到左
}
        return res->next;
    }
};
posted @ 2020-09-04 15:55  kite97  阅读(314)  评论(0编辑  收藏  举报