贝壳找房面试之手撕代码

  1. 二叉树的锯齿形层次遍历
    是leetcode103道题目,难度中等。
    解法如下,思路很简单,借助两个栈,flag记录是先入左孩子还是先入右孩子。

    执行用时 :8 ms, 在所有 C++ 提交中击败了76.49%的用户

    内存消耗 :13.3 MB, 在所有 C++ 提交中击败了86.78%的用户

    class Solution {
    public:
        vector> zigzagLevelOrder(TreeNode* root) {
            vector >result;
            if(root==nullptr)
                return result;
            stack S1,S2;
            S1.push(root);
            int flag=1;//左孩子先入栈,右孩子先出栈
            TreeNode* tmp;
            vector vec;
            while(!S1.empty() or !S2.empty()){
                if(!vec.empty()){
                    result.push_back(vec);
                    vec.clear();
                }
                if(!S1.empty()){
                    while(!S1.empty()){
                        tmp=S1.top();
                        S1.pop();
                        vec.push_back(tmp->val);
                        if(flag==1){
                            if(tmp->left)
                                S2.push(tmp->left);
                            if(tmp->right)
                                S2.push(tmp->right);
                        }else{
                            if(tmp->right)
                                S2.push(tmp->right);
                            if(tmp->left)
                                S2.push(tmp->left);
                        }
                    }
                    flag=0-flag;
                }
                else{
                    while(!S2.empty()){
                        tmp=S2.top();
                        S2.pop();
                        vec.push_back(tmp->val);
                        if(flag==1){
                            if(tmp->left)
                                S1.push(tmp->left);
                            if(tmp->right)
                                S1.push(tmp->right);
                        }else{
                            if(tmp->right)
                                S1.push(tmp->right);
                            if(tmp->left)
                                S1.push(tmp->left);
                        }
                    }
                    flag=0-flag;
                }
            }
            result.push_back(vec);
            return result;
        }
    };

     

  2. 链表反序
    leetcode206
    leetcode92
    206题代码如下:

    执行用时 :12 ms, 在所有 C++ 提交中击败了84.92%的用户

    内存消耗 :9.3 MB, 在所有 C++ 提交中击败了9.63%的用户

    #include 
    #include 
    using namespace std;
    
    struct ListNode{
        int val;
        ListNode *next;
        ListNode(int x):val(x),next(nullptr){}
    };
    
    ListNode* reverseList(ListNode* head) {
        if(head== nullptr or head->next==nullptr)
            return head;
        ListNode* tail=nullptr;
        ListNode* newhead=nullptr;
        while(head){
            newhead=head;
            head=head->next;
            newhead->next=tail;
            tail=newhead;
        }
        head=newhead;
        tail=nullptr;
        newhead= nullptr;
        return head;
    }
    
    ListNode* buildList(vector vec){
        ListNode* head=nullptr;
        if(vec.empty())
            return head;
        for(int i=vec.size()-1;i>=0;--i){
            ListNode* tmp=new ListNode(vec[i]);
            tmp->next=head;
            head=tmp;
        }
        return head;
    }
    
    void destorylist(ListNode* head){
        if(head==nullptr)
            return;
        ListNode* tmp=nullptr;
        while(head){
            tmp=head;
            head=head->next;
            delete tmp;
        }
    }
    
    int main(){
        vector vec{1,2,3,4,5,6,7};
        ListNode* head=buildList(vec);
        head=reverseList(head);
        ListNode* tmp=head;
        while(tmp){
            cout<val<<" ";
            tmp=tmp->next;
        }
        destorylist(head);
        return 0;
    }

    输出结果:7 6 5 4 3 2 1
    92题是一个中等难度的题,反转从位置m到位置n进行反转。

    执行用时 :8 ms, 在所有 C++ 提交中击败了55.01%的用户

    内存消耗 :8.6 MB, 在所有 C++ 提交中击败了79.29%的用户

    ListNode* reverseBetween(ListNode* head, int m, int n) {
        if(head==nullptr or head->next==nullptr){
            return head;
        }
        ListNode* q=nullptr;//记录反转的前一个结点
        ListNode* q1=head;//记录反转链表头节点
        ListNode* p=nullptr;//反转后反转链表的头结点
        for(int i=1;inext;//找到反转链表头节点
        }
        ListNode* tail=q1;//记录反转链表尾结点后的一个结点
        for(int i=m;i<=n;++i){
            tail=tail->next;
        }
        while(m<=n){
            p=q1;
            q1=q1->next;
            p->next=tail;
            tail=p;
            ++m;
        }
        if(q){
            q->next=p;
            return head;
        }
        return p;
    }

     

  3. 写一下二叉树深度遍历非递归。
    DFS是指,对每个分支的可能分支深入到不能深入为止。就是说把一个分支访问完才能访问其他分支。树的深度优先遍历就是先序遍历。
    非递归,借助栈完成DFS(depthFirstSearch)。

    void BFS(Tree *root){
        if(root==nullptr){
            return;
        }
        stack S1;
        S1.push(root);
        Tree *tmp=nullptr;
        while(!S1.empty()){
            tmp=S1.top();
            cout<data<<" ";
            S1.pop();
            if(tmp->right)
                S1.push(tmp->right);
            if(tmp->left)
                S1.push(tmp->left);
        }
        cout<

    深度优先遍历的递归写法:

    void BFS(Tree *root){
        if(root==nullptr){
            return;
        }
        cout<data<<" ";
        BFS(root->left);
        BFS(root->right);
    }

    同时再写一下广度优先遍历:
     

    void DFS(Tree *root){
        if(root==nullptr){
            return;
        }
        stack S1,S2;//S1用来存储结点,S2作为临时交换用的空间
        S1.push(root);
        Tree* tmp;
        while(!S1.empty()){
            if(!S1.empty()){
                while(!S1.empty()){//每次从S1读取数据
                    tmp=S1.top();
                    cout<data<<" ";
                    S1.pop();
                    if(tmp->left)//左孩子先入S2栈,后面利用S2出栈后,就成了左孩子后入S1
                        S2.push(tmp->left);
                    if(tmp->right)
                        S2.push(tmp->right);
                }
                while(!S2.empty()){//S2作为临时交换用
                    tmp=S2.top();
                    S2.pop();
                    S1.push(tmp);
                }
            }
        }
    }

    利用队列实现广度优先遍历的话,只需要一个队列就行,可以节省开销,具体如下:
     

    void DFS(Tree *root) {
        if (root == nullptr) {
            return;
        }
        queue Q;
        Q.push(root);
        Tree* tmp;
        while(!Q.empty()){
            tmp=Q.front();
            Q.pop();
            cout<data<<" ";
            if(tmp->left)
                Q.push(tmp->left);
            if(tmp->right)
                Q.push(tmp->right);
        }
    }

     

  4. 手写冒泡排序,数组去重
    冒泡排序在前面介绍过,自认为面试官让写冒泡排序的话,应该弄的高级点,首先,模板类,这样可以排序不同类型的数,其次具有升序排序和降序排序几种功能,而且还能自定义排序准则,这里就涉及函数作为参数了,此外,还能在指定范围内排序,多种功能,还可以写出优化后的冒泡排序。
    数组去重
    不知道是不是跟leetcode26题一个意思,这道题的解法如下:
        int removeDuplicates(vector& nums) {
            if(nums.size()<=1)
                return nums.size();
            int i=0,j=1;
            while(j

     

  5. 手写快排,手撕topk快排算法
    leetcode912这道题是对数组进行排序。

    执行用时 :144 ms, 在所有 C++ 提交中击败了27.81%的用户

    内存消耗 :12.6 MB, 在所有 C++ 提交中击败了100.00%的用户

     
        void swap(int &a,int &b){
            int tmp=a;
            a=b;
            b=tmp;
        }
        int partition(vector &nums,int lo,int hi){
            swap(nums[lo],nums[lo+rand()%(hi-lo+1)]);
            int pivot=nums[lo];
            int mi=lo;
            for(int k=lo+1;k<=hi;k++)
                if(nums[k]& nums,int lo,int hi){
            if(lo sortArray(vector& nums) {
            int lo=0,hi=nums.size()-1;
            quicksort(nums,lo,hi);
            return nums;
        }

    topk快排算法
    topk问题有两种类型:1.海量数据里面选出最大的前k个数,问法有一亿个数据找到前k大的数据,或者找到第k大的数据;2.从很大一个数组里面选出最大的前k个数。
    问题1的情况,数据无法存储,因此也无法通过排序来选出前k个数;问题2是可以把数据存储起来,可以对数组进行排序之后,取前k个数。
    对于问题2,可以借助快排来找到前k个数。快速排序需要找到一个轴点,轴点右边的数均大于轴点,左边的数均小于轴点,因此如果找到一个轴点,加上轴点及其右边的数的个数为K,那么我们就找到了这最大的前K个数。在进行partition的过程中,设轴点右边的数的个数为x,有如下几种情况:
    1.x 2.x=K-1时,我们要找的轴点就是这个轴点,输出此轴点以及其右边的数;
    3.x>K-1时,我们要找的轴点一定在此轴点的右边,对右边的数进行partition。
    输出的结果不一定有序。
    时间复杂度分析:最坏情况下,轴点从数组第一个位置移动到倒数第K个位置,partition次数为N-K,一次partition的时间复杂度为:O(hi-lo),时间复杂度T(n)=(N+N-1+N-2+...+(K+1))≈O(N^2)。

    int partition(vector &nums,int lo,int hi){
        swap(nums[lo],nums[lo+rand()%(hi-lo+1)]);
        int pivot=nums[lo];
        int mi=lo;
        for(int k=lo+1;k<=hi;k++)
            if(nums[k] &nums,int lo,int hi,int K){
        int pivot=partition(nums,lo,hi);
        if(hi-pivot+1==K){//找到了这个轴点
            for(int i=0;i<(nums.size()-pivot);++i){
                cout< vec{1,2,9,3,11,4,21,5,55,6,10,7};
        int lo=0,hi=vec.size()-1;
        int K=3;
        topK(vec,lo,hi,K);
        return 0;
    }

    输出结果:
    11 21 55
    下面介绍一下第一种情况的算法:
    无法通过数组进行简单排序时,我们应该想到应用堆排序,构建大小为K的小顶堆,对于输入的数据,如果小于堆顶,则丢弃这个数据,如果大于堆顶,则用此数据替换堆顶,再进行下溢,直到所有数据输入完毕。
    时间复杂度分析:对数据进行下溢最坏的情况为堆的高度,就是logK,假设海量数据为N,那么时间复杂度T(n)=O(NlogK)。
    使用STL算法里优先队列解决这个问题,STL优先队列本身就是通过二叉堆实现的,最小优先队列的第一个元素就是最小堆堆顶元素。(这里需要去了解stl里queue的性质和用法)
     

    #include 
    #include 
    #include 
    using namesapce std;
    
    int main(){
        priority_queue,greater > minHeap;
        int K;
        cin>>K;
        int tmp,i=0;
        while(cin>>tmp){
            if(iminHeap.top()){
                    minHeap.pop();
                    minHeap.push(tmp);
                }
            }
        }
        for(int i=0;i

    输出结果:
    6 3 8 11 13 15 54 32 7 87 65 42 14 23 35 57 79 12
    ^D
    42 54 57 65 79 87

  6. 解释快速排序的思想,是否稳定,其他排序算法的稳定性
    见排序算法大总结
  7. 如何查找一个矩阵里最小的 和为0的矩阵
  8. 1.层序遍历二叉树,2.找出一个数组的中间数
    问题1.层次遍历二叉树,也叫广度优先遍历吧,这个在前面讲到过,可以利用两个栈,也可以利用队列实现。
    问题2.找出一个数组的中间数,可以跟partiton结合起来,使用partition找到一个轴点,该轴点右边数据个数为数组长度一半;对于偶数个数的数组,中间数有两个,输出该轴点以及右边的这个数;对于奇数个数的数组,输出该轴点。前面我们自己利用自己写的partition函数,现在我们使用stl库中的partition函数。代码如下:
     
  9. 给一个数组,输出每一个元素的后边第一个比他大的数的下标
    这跟leetcode739题很像,中等难度。
    思路1:如果对于每个元素,都把它后面的元素遍历来找到第一个比他大的数的下标,那么最坏情况下的时间复杂度是T(n)=(n-1)+(n-2)+...+1+0≈O(n^2),属于暴力解法,这并不OK。
    思路2:构建一个递减栈,从栈底到栈顶递减(由于数组获取数据可以通过下标,所以栈存下标就可以了),意思就是,如果将当前数组的递减子数组入栈,直到数组当前元素大于栈顶元素(不再递减),则出栈,一直出到栈顶元素大于当前元素,或者栈为空就停止;然后将当前元素下标入栈。栈从0到n-1存储下标,当遇到出栈条件时就出栈。

    执行用时 :304 ms, 在所有 C++ 提交中击败了46.42%的用户

    内存消耗 :16.1 MB, 在所有 C++ 提交中击败了69.26%的用户

        vector dailyTemperatures(vector& T) {
            int n=T.size();
            vector result(n,0);
            stack S;
            for(int i=0;iT[S.top()]){
                    result[S.top()]=i+1-S.top();
                    S.pop();
                }
            }
            return result;
        }

     

  10. 手写判断一棵树是不是平衡二叉树
    一颗树是平衡二叉树,左右子树高度差不大于1,且左右子树也必须是平衡二叉树。这是leetcode110,借助递归实现这个判断。

    执行用时 :24 ms, 在所有 C++ 提交中击败了49.68%的用户

    内存消耗 :17.4 MB, 在所有 C++ 提交中击败了16.31%的用户

    
        bool isBalanced(TreeNode* root) {
            if(root==nullptr)
                return true;
            if(abs(depth(root->left)-depth(root->right))>1)
                return false;
            return isBalanced(root->left)&&isBalanced(root->right);
        }
        int depth(TreeNode* root){
            if(root==nullptr)
                return 0;
            return max(depth(root->left),depth(root->right))+1;
        }

     

  11. 如何计算一个包含重复元素的数组中不同元素的个数,[1,1,2,3,4,4,5]返回5
    解法1:这个通过stl里的sort+unique就可以解决,输出最后向量的长度就行。
    解法2:利用set,set不存放重复元素,把向量都放进set里,最后输出set的元素个数。(这里需要去了解set的性质和用法)
  12. 讲一个最熟悉的算法

    这里需要弄清一个最熟悉的算法,并能很清楚的讲解,最好还能有改进版,或者不同版本。

你可能感兴趣的:(c++)