面试准备算法

前K个高频元素

给一个整数数组nums和一个整数k,请返回其中出现频率前k高的元素。

在这里插入图片描述
遍历整个数组,并使用哈希表记录每个数字出现的次数,并形成一个「出现次数数组」。找出原数组的前 k 个高频元素,就相当于找出「出现次数数组」的前 k 大的值。

最简单的做法是给「出现次数数组」排序。但由于可能有 O(N) 个不同的出现次数(其中 N 为原数组长度),故总的算法复杂度会达到 O(NlogN),不满足题目的要求。

可以利用堆的思想:建立一个小顶堆,然后遍历出现次数数组。

  • 如果堆的元素个数小于k,就可以直接插入堆中。
  • 如果堆的元素等于k,则检查堆顶与当前元素出现次数的大小,如果堆顶更大,说明至少有k个数字比当前值大,故舍弃当前值;否则,就弹出堆顶,并将当前值插入堆中。

遍历完成后,堆中的元素就代表了出现次数数组中前k大的值。

在这里插入图片描述

`string s = static_cast<string>(cp);`

static_cast用于执行明确定义的类型转换,将const char*转换为string是一个明确定义的转换。

const_cast只能去除指针或引用的const属性。

在这里插入图片描述
A. 寻址: 虽然数据链路层的寻址通常是局部的,用于在同一网络中的设备之间进行通信,但它确实提供了基本的寻址功能。比如,以太网中的 MAC 地址就是数据链路层的地址。

B. 流量控制: 数据链路层通过各种机制(如滑动窗口协议)来控制数据传输的速率,以防止接收方被过多的数据淹没。

C. 差错控制: 数据链路层使用校验和、冗余码等技术来检测和纠正传输过程中的错误。

实现透明传输: 数据链路层将物理层提供的可能出错的物理连接改造成为逻辑上无差错的数据链路,对上层网络层来说,数据链路层提供的是一条透明的、可靠的链路。

const修饰成员函数

成员函数后加const,我们称这个函数为常函数。
常函数内不可以修改成员属性

成员属性声明时加mutable关键字后,仍然可以修改。

const修饰对象,表示这是一个常对象。
常对象只能调用常函数。

划分字母区间

给一个字符串s,要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。

在这里插入图片描述

class Solution {
public:
    vector<int> partitionLabels(string s) {
        //记录每个字母最远出现的位置
        int map1[26] = {0};
        for(int i=0; i<s.size(); i++){
            char c = s[i];
            map1[c - 'a'] = i;
        }

        //记录区间的左右边界
        int left = 0, right = 0;
        vector<int> res;
        for(int i=0; i<s.size(); i++){
            char c = s[i];
            //求当前边界的最远位置(所有已经出现字母的最远位置)
            right = max(right, map1[c - 'a']);
            //已经到最远位置
            if(right == i){
                res.push_back(right - left + 1);
                left = right + 1;
            }
        }
        return res;
    }
};

struct 和 class 的区别

  • struct成员默认是共有的
  • class继承默认是私有的

类所占大小由虚函数,所有非静态数据成员大小,对其所占字节数决定。

空类的内存大小是一个字节。

类中无论有多少个虚函数,只会多占一个虚表指针空间。

在这里插入图片描述
归并排序需要额外的辅助空间,而快速排序是原地排序,大部分排序算法的底层都是快速排序。

跳表是一个非常好的数据结构,在Linux内核里面经常可以看到,它的插入,删除,查找的平均时间复杂度为O(log N)。

在这里插入图片描述

假设目前有8台机器,有N个进程需要消耗2台这样的机器,规定每个进程每次只能申请一台,则至多允许( )个进程参于竞争,而不会发生死锁。

当8个进程如果都同时申请到了1台,就发生了死锁。如果是7个进程,那必然有一个能申请到2台。

在不同进程内的线程通信就会使用共享内存。

进程同步异步,和阻塞非阻塞没有直接关系。

死锁

如果一组进程中的每一个进程都在等待仅由该组进程中的其它进程才能引发的事件,那么该进程组是死锁的。

产生死锁必须同时具备下面4个必要条件,只要其中一个条件不成立,死锁就不会发生:

  1. 互斥条件。进程对所分配到的资源进行排他性使用,即在一段时间内,某资源只能被一个进程占用。如果此时还有其他进程请求该资源,那么请求进程只能等待,直到占用该资源的进程用完释放。
  2. 请求和保持条件。进程已经保持了至少一个资源,但又提出新的资源请求,而该资源已经被其它进程占用,此时请求进程被阻塞,占用的资源也保持不放。
  3. 不可抢占条件。进程已获得的资源在未使用完之前不能被抢占,只能在进程使用完由自己释放。
  4. 循环等待条件。在发生死锁时,必然存在一个进程——资源的循环里,即P0在等待P1释放,P1等待P2释放,Pn等待P1释放。

打开web浏览器访问www.baidu.com时,底层进行了哪些数据交互?

  1. DNS域名解析
  • 域名解析:浏览器首先会将输入的域名提交给本地DNS服务器进行解析。
  • 递归查询:如果本地DNS服务器没有缓存该域名对应的IP地址,就会向根域名服务器发起递归查询,直到找到对应的IP地址。
  1. TCP连接
  • 建立连接:浏览器与服务器之间建立TCP连接。这个过程涉及到三次握手,确保双方能够可靠低进行数据传输。
  • 选择端口:浏览器会随机选择一个端口,而服务器通常使用80端口(HTTP)或443端口(HHTPS)。
  1. 发送HTTP请求。
  • 构建请求:浏览器会构造一个HTTP请求,包含请求方法:通常为GET,表示从服务器获取资源。请求URL,HTTP协议版本号,请求头。
  • 发送请求:浏览器将构造好的HTTP请求发送给服务器。
  1. 服务器处理请求。
  • 接受请求:服务器接收到浏览器的HTTP请求。
  • 处理请求:服务器根据请求URL找到对应的资源,并生成相应的HTTP响应。
  • 生成的HTTP响应包含:状态码、响应头、响应体等。
  1. 浏览器接收响应。
  • 接收响应:浏览器接收到服务器的HTTP响应。
  • 解析渲染:浏览器会解析HTML代码,构建DOM树,最终在浏览器窗口显示页面。
  • 加载资源。
  • 异步请求。
  1. 关闭连接。

一天,某台线上服务器因网卡流量太大触发了告警,你最先想到的是上去查看系统的网络连接情况,应该使用netstat。

进程间通信效率最高的方式:共享内存。
共享内存是进程间通信中最快速的一种方式,它允许多个进程直接访问同一块内存区域,就像它们在访问自己的内存一样,这种方式避免了内核态和用户态之间的切换以及数据的复制,因此效率非常高。

管道:管道是一种半双工的通信方式,数据在管道中流动,需要内核的介入,效率相对较低。
消息队列:消息队列提供了一种从一个进程向另一个进程发送数据的方法。虽然比管道效率高,但仍涉及内核的介入和数据的复制。
套接字:套接字通常用于网络通信,也可用作进程间通信。但由于涉及网络协议栈,效率相对较低。

直接内存访问:进程可以直接访问共享内存,无需内核介入,减少了系统调用开销。
避免数据复制:数据不需要在内核空间和用户空间之间复制,减少了数据拷贝的开销。

在这里插入图片描述

  • export命令用于设置或显示环境变量。当在shell中执行程序时,它提供一组环境变量。
  • cat命令用于显示文件内容。
  • echo命令用于输出文本到标准输出。
  • env用于显示所有当前定义的环境变量。

sql使用EXPLAIN关键字优化性能。

符号链接(软链接)

  • 相当于Windows下的快捷方式,它指向另一个文件或目录。
  • 创建符号链接时,系统会创建一个新的inode,其中存储了目标文件的路径。
  • 删除源文件后,符号链接会失效,因为指向的路径不存在了。

硬链接

  • 相当于多个文件的别名,它们指向同一个inode。
  • 创建硬链接时,不会创建新的inode,而是增加了一个指向原有inode的指针。
  • 删除一个硬链接,只是减少了指向该inode的指针数量,只有当所有的硬链接都被删除后,文件才会被真正删除。

最后一块石头的重量

在这里插入图片描述

class Solution {
public:
    int lastStoneWeightII(vector<int>& stones) {
        int sum = accumulate(stones.begin(), stones.end(), 0);
        int target = sum / 2;

        vector<int> dp(target+1, 0);
        dp[0] = 0;
        for(int i=0; i<stones.size(); i++){
            for(int j=target; j>=stones[i]; j--){
                dp[j] = max(dp[j], dp[j-stones[i]]+stones[i]);
            }
        }

        return sum - dp[target] - dp[target];
    }
};

下一个排列

在这里插入图片描述
要求将给定数字序列重新排列成字典序中下一个更大的排列。
在这里插入图片描述
下一个排列总是比当前排列要大,除非该排列已经是最大的排列。
我们希望找到一种方法,能够找到一个大于当前序列的新序列,且变大的幅度尽可能小,具体地:

  1. 我们需要找到左边一个较小数与右边一个较大数交换,以便能够让当前排列变大,从而得到下一个排列。
  2. 同时要让这个较小数尽量靠右,较大数尽可能小。当交换完成后,较大数右边的数需要按照升序重新排列。

在这里插入图片描述

  1. 首先从后向前找到第一个顺序对(i,i+1),满足a[i] < a[i+1],这样较小数就是a[i],(i+1,n)必然是降序序列。
  2. 如找到了顺序对,就在区间[i+1,n)中从后向前找到第一个元素满足a[j] > a[i],这样较大数为a[j]。
  3. 交换a[i]与a[j]。此时证明[i+1,n)为降序,又把它变为升序。

和为K的子数组

前缀和+哈希表优化
枚举的瓶颈在于对每个i,我们需要枚举所有的j来判断是否符合条件。
定义pre[i]为[0,…,i]里所有数的和,则pre[i]可以由pre[i-1]递推而来

pre[i] = pre[i-1]+nums[i]

那么[j,…,i]这个子数组和为k可以转换为

pre[i] - pre[j-1] == k

课程表

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

饼干饼干圆又圆

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

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

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

打赏作者

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

抵扣说明:

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

余额充值