【每日刷题3.4】10道算法+10道面试 - 阿V

昨天没有完成,今天争取完成!!!冲呀,大厂offer我来啦。

算法题(牛客网)

1.求二叉树的层序遍历

 

 这个上个星期刷过,与其他遍历不同,层序遍历用的是队列保存节点。


 代码详情:

/**
 * struct TreeNode {
 *	int val;
 *	struct TreeNode *left;
 *	struct TreeNode *right;
 * };
 */

class Solution {
public:
    /**
     * 
     * @param root TreeNode类 
     * @return int整型vector<vector<>>
     */
    vector<vector<int> > levelOrder(TreeNode* root) {
        // write code here
        if (!root) return {};//算法第一步判断边界条件
        
        vector<vector<int>> res; //保存答案
        queue<TreeNode*> que; //保存遍历节点
        que.push(root);
        
        while(!que.empty()){
            int size = que.size(); //记录这一层有多少节点要遍历
            vector<int> ans; //保存当前层的节点值
            while(size--){
                root = que.front();
                que.pop();
                ans.push_back(root->val);
                
                if(root->left) que.push(root->left); //存在左孩子,保存进队列
                if(root->right)que.push(root->right); //同理
            }
            if (ans.size() != 0){
                res.push_back(ans);
            }
        }
        
        return res;
    }
};

2.寻找第K大

  题目要求了要用快速排序,所以不能使用堆排序了。


 代码详情:

class Solution {
public:
    int findKth(vector<int> a, int n, int K) {
        // write code here
        int l = 0,r = n-1; //左右指针
        K = n-K; //将k换算成第k小
        while(l <= r){
            int mid = quickSort(a, l, r); //计算中间指针
            
            if (mid == K){ //如果相等,说明该位置就是答案
                return a[mid];
            }
            else if(mid < K){ //中间指针在答案左边,去右边接着找
                l = mid + 1;
            }
            else{ //同理
                r = mid - 1;
            }
        }
        
        return -1;
    }
    
    
    int quickSort(vector<int>& nums,int l,int r){ //快排,寻找一个参考,左边都是小的,右边都是大的
        int i = l+1,j = r;
        int num = nums[l];
        while(i <= j){
            while(i <= r && num >= nums[i]){ //寻找比num大的数
                i++;
            }
            while(j > l && num < nums[j]){ //寻找比num小的数
                j--;
            }
            if (i >= j){
                break;
            }
            
            swap(nums[i],nums[j]); //交换i,j
        }
        swap(nums[l],nums[j]); //记得把l换到该换的位置
        
        return j;
    }
};

3.两数之和

 由于要求时间复杂度O(nlogn),普通的暴力枚举是不可行的,这里使用哈希表,一边查找,一边保存已查找的值。


 代码详情:

class Solution {
public:
    /**
     * 
     * @param numbers int整型vector 
     * @param target int整型 
     * @return int整型vector
     */
    vector<int> twoSum(vector<int>& numbers, int target) {
        // write code here
        unordered_map<int, int> map; //key保存数组数值,value保存数组下标
        
        for(int i = 0;i < numbers.size();i++){
            if (map.count(target - numbers[i])){ //查找哈希表是否存在一个值和当前值相加为target
                return {map[target - numbers[i]] + 1,i + 1};
            }
            else{
                map[numbers[i]] = i; //不存在就保存进哈希表
            }
        }
        
        return {};
    }
};

4.合并两个排序的链表

一看空间复杂度O(1),直接想到双指针


 

 代码详情:

/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
class Solution {
public:
    ListNode* Merge(ListNode* pHead1, ListNode* pHead2) {
        ListNode* head = new ListNode(INT_MIN); //创建一个空指针
        ListNode* root = head; //记录根指针
        
        while(pHead1 != nullptr && pHead2 != nullptr){
            if (pHead1->val <= pHead2->val){ //链接小的那个指针
                head->next = pHead1;
                pHead1 = pHead1->next;
            }
            else{
                head->next = pHead2;
                pHead2 = pHead2->next;
            }
            head = head->next;
        }
        if(pHead1 == nullptr){ //剩下的指针直接链接在头指针后面
            head->next = pHead2;
        }
        else{
            head->next = pHead1;
        }
        
        
        return root->next; //空指针不返回
    }
};

 5. 用两个栈实现队列

 栈是先进后出,而队列是先进先出,两个栈,一个负责进,一个负责出就能实现队列。


 代码详情:

class Solution
{
public:
    void push(int node) {
        stack1.push(node); //压入进栈
    }

    int pop() {
        if (stack2.empty() == true){ //出栈为空的时候,将进栈全部转入出栈
            while(!stack1.empty()){
                stack2.push(stack1.top());
                stack1.pop();
            }
        }
        int result = stack2.top(); //出栈不为空,先将出栈全部出完。
        stack2.pop();
        
        return result;
    }

private:
    stack<int> stack1; //stack1负责进栈
    stack<int> stack2; //stack2负责出栈
};

6.跳台阶

 最经典的动态规划题,就类似于斐波那契数列


 代码详情:

class Solution {
public:
    int jumpFloor(int number) {
        vector<int> dp(number + 1);
        dp[0] = 0;
        dp[1] = 1;
        dp[2] = 2;
        
        for(int i = 3;i<=number;++i){
            dp[i] = dp[i-1] + dp[i-2]; //i就是前面两个相加
        }
        
        return dp[number];
    }
};

 7.链表中的节点每k个一组翻转

 跟反转链表一样,但由于每k个反转,存在前后链表链接问题,处理好就不难。


 代码详情:

/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 * };
 */

class Solution {
public:
    /**
     * 
     * @param head ListNode类 
     * @param k int整型 
     * @return ListNode类
     */
    ListNode* reverseKGroup(ListNode* head, int k) {
        // write code here
        ListNode* node = head;
        for(int i = 0;i < k;++i){ //寻找k个链表
            if (!node) return head; //不足k个不反转
            node = node->next;
        }
        ListNode* res = reverse(head->next, head,node); //反转选中链表
        head->next = reverseKGroup(node, k); //将下一部分链表进行链接
        
        return res;
    }
    
    //反转函数
    ListNode* reverse(ListNode* right,ListNode* left,ListNode* target){
        ListNode* tem_right; //保存下一节点
        while(right != target){
            tem_right = right->next;
            right->next = left;
            left = right;
            right = tem_right;
        }
        
        return left;
    }
};

 这题没自己写出来,反转后链表链接还是比较难写哈哈哈。

8.连续子数组的最大和

这道题好绕,想了好久都实现不出来,太可恶了。


 代码详情:

class Solution {
public:
    int FindGreatestSumOfSubArray(vector<int> array) {
        if (array.size() == 1) return array[0]; //边界条件
        int res = INT_MIN; //记录最终答案
        int tem = 0; //记录相加大于零的连续子序列
        
        for(int arr:array){
            tem += arr;
            res = max(res,tem);
            if (tem < 0){ //如果小于零,舍弃前面数组
                tem = 0;
            }
        }
        return res;
    }
};

 9.最长无重复子数组

 一开始有点没头绪,突然灵机一动,想到用哈希表存储遍历过的数据,当有重复的时候,从重复数据再来遍历就不会错过了,真是太机灵了我哈哈哈。


 代码详情:

class Solution {
public:
    /**
     * 
     * @param arr int整型vector the array
     * @return int整型
     */
    int maxLength(vector<int>& arr) {
        // write code here
        unordered_map<int, int> map; //记录遍历过的数据,key为数据,value为下标
        int res = 0; //存储最长长度
        int tem = 0; //存储每次不重复数组长度
        
        for(int i = 0;i < arr.size();++i){
            if (map.count(arr[i])){ //判断是否重复
                res = max(tem,res); //记录最大值
                tem = 0;
                i = map[arr[i]]; //将i更新为重复数据的下标
                map.clear(); //清空哈希表
            }
            else{
                map[arr[i]] = i;
                tem++;
            }
        }
        
        return max(res,tem);
    }
};

 10.判断链表中是否有环

 之前写的时候很懵,但是看了解析,知道了为什么快慢指针可以判断出循环队列入口点。

 靠,才发现这题只要是否有环。。。。想多了(可恶~~~)


代码详情:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    bool hasCycle(ListNode *head) {
        ListNode* slow = head,* fast = head;
        
        do{
            if (fast == nullptr || fast->next == nullptr){ //如果有空节点,代表无环
                return false;
            }
            fast = fast->next->next; //快指针每次走两步
            slow = slow->next; //慢指针只走一步,如果有环,快指针迟早追上慢指针
        }while(slow != fast);
        
        return true;
    }
};

面试题(牛客网)

 1.TCP和UDP区别;

 TCP是基于字节的面向连接,可靠的传输控制协议,拥有流量控制和拥塞控制,支持点对点通信。

UDP是基于报文的无连接,不可靠的用户数据报协议。支持一对一,一对多,多对一,多对多通信

2.TCP三次握手四次挥手;

 3.如果服务端先关闭连接会发生什么?

 4.为什么会采用三次握手,若采用二次握手可以吗?

 5.为什么会采用四次挥手,若采用三次挥手可以吗?

 因为关闭连接时,server端收到客户端的FIN报文,并不会立即关闭socket,只能先回复一个ACK告诉client我已经收到你的关闭请求了,同时可能server还有数据没传输完,只有等server端数据传输完成了 才能发送FIN报文,所以这个地方要分两次发送,这样就有了四次挥手。
————————————————
版权声明:本文为CSDN博主「孤帆扁舟去」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_42490152/article/details/100942319

6. 服务端的Time_Wait状态再哪个阶段出现?持续多久?为什么要设计这么一个状态?

Time_Wait阶段是最后阶段发送确认收到server端的fin报文释放连接请求后回复给server端ack报文,之后client端就进入Time_Wait阶段.
持续多久即是问为什么不马上关闭直接进入closed阶段,主要是考虑到网络的不可靠,假如client最后阶段发送给server端端ack报文由于网络原因丢失了server没收到呢,server端会重新发送fin报文过来,这个时候client端就要等. 等多久?等一个计时器时间2MSL,如果该时间段内再次收到server的fin报文 那client就必须回复. 如果没有收到,client就认为server端已经接收到了最后的ack报文.
————————————————
版权声明:本文为CSDN博主「孤帆扁舟去」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_42490152/article/details/100942319 

 7.TCP如何实现可靠连接

 8.解释TCP的滑动窗口如何实现,除了流量控制还有什么作用?

 

现在心态有点着急,太过急于求成了,我先自己静一静,好好整理一下计算机网络的知识。

分享一下今日投递趣事:

 

 

 之前投递的公司没过,后面又发了一份,觉得奇怪打开一看红红的几款大字:昨日处理有误,我想这不是我有戏!!!结果仔细一看还是没过,梅开二度,靠。

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ZZW游戏制造

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值