后台面试题目

本文深入探讨了链表逆序的实现方式,介绍了通过两个栈完成队列的原理,并解析了兄弟单词查找算法。此外,文章还涵盖了智能指针、线程局部存储、Linux内存管理等高级主题,为读者提供了全面的数据结构与算法知识。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  1. 链表逆序。
List*reverseList(List *head)
{
    if (head == nullptr)
        return nullptr;
    List *p,*t,*q=nullptr;
    p = head;
    while (p != nullptr)
    {
        t = p->next;
        p->next = q;
        q = p;
        p = t;
    }
    return q;
}
List *Dreverse(List *head)
{
    if(head == nullptr)
        return head;
    List *pre,*pointer,*temp;
    pre = nullptr;
    pointer = head;
    while (pointer != nullptr)
    {
        temp = pointer->next;
        pointer->next = pre;
        pointer->pre = temp;
        pre = pointer;
        pointer = temp;
    }
    return pre;
}
  1. 两个栈完成一个队列。
template<class T>
class Queue{
public:
    void push(T item);
    void pop();
    T back();
    T front();
    bool empty();
    int size();
private:
    stack<T> s1;
    stack<T> s2;
};

template<class T>
void Queue<T>::push(T item)
{
    s1.push(item);
}

template<class T>
void Queue<T>::pop()
{
     if (s1.empty() && s2.empty())
            return;

    if (!s2.empty()){
        s2.pop();
    }
    else
    {
        while (s1.size() > 0)
        {
            s2.push(s1.top());
            s1.pop();
        }
        s2.pop();
    }
}

template<class T>
T Queue<T>::back()
{
    if (s1.empty() && s2.empty())
            return 0;

    if (!s1.empty())
    {
        return s1.top();
    }
    else
    {
        while (s2.size() > 0)
        {
            s1.push(s2.top());
            s2.pop();
        }
        return s1.top();
    }
}

template<class T>
T Queue<T>::front()
{
    if (s1.empty() && s2.empty())
            return 0;

    if (!s2.empty())
    {
        return s2.top();
    }
    else
    {
        while (s1.size() > 0)
        {
            s2.push(s1.top());
            s1.pop();
        }
        return s2.top();
    }
}

template<class T>
bool Queue<T>::empty()
{
    return (s1.empty() && s2.empty());
}

template<class T>
int Queue<T>::size()
{
    return (s1.size() + s2.size());
}

测试代码:

int main(int argc,char**argv)
{
    Queue<int> q;
    q.push(1);
    q.push(2);
    q.push(3);
    q.push(4);
    q.push(5);
    q.push(6);
    cout<<q.front()<<endl;
    cout<<q.back()<<endl;
    cout<<q.size()<<endl;
    cout<<q.empty()<<endl;
    printf("******1*****\n");
    q.pop();
    q.pop();
    cout<<q.front()<<endl;
    cout<<q.back()<<endl;
    cout<<q.size()<<endl;
    cout<<q.empty()<<endl;
    printf("******2*****\n");
    q.pop();
    q.pop();
    cout<<q.front()<<endl;
    cout<<q.back()<<endl;
    cout<<q.size()<<endl;
    cout<<q.empty()<<endl;
    printf("******3*****\n");
    q.pop();
    q.pop();
    printf("******4*****\n");
    q.pop();
    cout<<q.front()<<endl;
    cout<<q.back()<<endl;
    cout<<q.size()<<endl;
    cout<<q.empty()<<endl;
	return 0;
}

需要指出一点的是,在使用Queue之前一点要判断是否为空,否则会报错segmentation fault。测试最后的front()和back()会报错。
3. 查找兄弟单词。

#include <vector>
#include <string>
#include <iostream>
using namespace std;

bool isBrother(const string word1,const string word2)
{
    if (word1==word2 || word1.length() != word2.length())
        return false;
    string s1 = word1;
    string s2 = word2;
    sort(s1.begin(),s1.end());
    sort(s2.begin(),s2.end());
    if(s1 == s2)
        return true;
    return false;
}

vector<string> BrotherWords(const string word,vector<string> array)
{
    vector<string> temp;
    int size = array.size();
    for (int i = 0; i < size; i++)
    {
        const string &w = array[i];
        if (isBrother(w,word))
        {
            temp.push_back(array[i]);
        }
    }
    return temp;
}



int main()
{
    string word1 = "abcd";
    vector<string> words={"abcd","adcb","bacd","cdba","dbac",
    "abcf","cdea","efgh","abcde","aefgh"};
    vector<string> brothers =  BrotherWords(word1,words);
    for (int i = 0; i < brothers.size(); i++)
    {
        cout<<brothers[i].c_str()<<endl;
    }
    
    return 0;
}

命令:g++ -o brother brother_word.cpp -std=c+
4. IP和UDP的区别。
UDP:UDP协议是传输层协议,它是无连接,不保证可靠的传输层协议。在传输过程中没有流量控制和确认机制,数据报可能会丢失,延迟,乱序到达信宿。
UDP只是提供了利用校验和检查数据完整性的简单差错控制。
UDP支持一对一、一对多、多对一和多对多的交互通信。
IP:ip是TCP/IP体系中的网络层协议,IP数据报协议非常简单,仅能提供不可靠、无连接的传送服务。不可靠即不保证分组成功传送,对分组丢失、分组无序或重新传送等问题,IP都不作检测,也不通知发送端或接收端。无连接则是指每个分组被独立地处理和传送。其次,IP协议是点到点的。点到点通信的最大问题便是如何进行恰当路由选择。
5. 说说volatile关键字的作用。
volatile关键字的目的是防止编译器对代码指令进行优化。操作系统为了加速对内存的访问,往往在编译的时候对程序指令进行优化,编译器优化常用的方法有:将内存变量缓存到寄存器;调整指令顺序充分利用CPU指令流水线,常见的是重新排序读写指令。使用volatile,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。
6. 线程局部存储。
线程局部存储的意思是,把一个全局变量存到线程里面去,防止别的线程对其进行改变,不会影响本线程里面的值。可以做到系统不支持原子操作的自定义数据类型,在不使用锁的情况下保证线程安全。在Linux中还有一种更为高效的线程局部存储方法,就是使用关键字__thread来定义变量。 static __thread char t_buf[32] = {’\0’};
extern __thread int t_val = 0;
凡是带有__thread的变量,每个线程都拥有该变量的一份拷贝,且互不干扰。线程局部存储中的变量将一直存在,直至线程终止,当线程终止时会自动释放这一存储。
如果变量声明中使用量关键字static或者extern,那么关键字__thread必须紧随其后;与一般的全局变量或静态变量一样,线程局部变量在声明时可以设置一个初始化值;可以使用C语言取地址符(&)来获取线程局部变量的地址。
7. Linux内存显示。
在Linux下查看内存我们一般用free命令:
$ free
total used free shared buffers cached
Mem: 3266180 3250004 16176 0 110652 2668236
-/+ buffers/cache: 471116 2795064
Swap: 2048276 80160 1968116
对内存查看free命令输出内容的解释:
total:总计物理内存的大小。
used:已使用多大。
free:可用有多少。
Shared:多个进程共享的内存总额。
Buffers/cached:磁盘缓存的大小。
第三行(-/+ buffers/cached):
used:已使用多大。
free:可用有多少。
缓存和交换机制:
缓存机制是一种用空间换时间的机制,由于内存的速度基本上是磁盘速度的105105倍,操作系统将一些特别常用的磁盘上的数据存在内存中,在用户要访问的时候直接从内存中找出数据展示给用户,这样虽然占用了一些内存的空间,却很大程度的加速了数据的访问;
交换机制是使用时间换空间的。相比磁盘,内存真的小的可怜,我们日常使用的民用计算机一般就是4G、8G4G、8G的内存,磁盘的却有500G、1T500G、1T之大。交换机制就是把一些不常用的内存的数据暂时存储到磁盘上面上去,腾出来的内存空间留作他用,在需要的时候再从磁盘中将数据读入内存中。这样做虽然从真正需要数据到数据展示在户面前的过程变慢了,但却让整个系统拥有了看似更多的内存。
8. 对于全局变量int数据,多线程操作后的结果?是否存在中间值?
在多线程编程中,简单类型的数据时线程安全的,对1字节byte/2字节word/4字节int类型的读写操作都是原子性的,1个int不会有中间状态,若它原始值是0,往其写入0x12345678,则另一个线程绝对不会读到0x12000000或是0x00005678的值。
9. 说说c++的智能指针*[7]*。
unique_ptr:unique_ptr实现独占式拥有或严格拥有概念,保证同一时间内只有一个智能指针可以指向该对象。它对于避免资源泄露(例如“以new创建对象后因为发生异常而忘记调用delete”)特别有用。

unique_ptr<string> p1 (new string ("test"));   
unique_ptr<string> p2;                       
p2 = p1;//此时会报错!!

尝试复制p1时会编译出错,这样就避免p1不再指向有效的数据。
当程序试图将一个 unique_ptr 赋值给另一个时,如果源 unique_ptr 是个临时右值,编译器允许这么做;如果源 unique_ptr 将存在一段时间,编译器将禁止这么做,比如:

unique_ptr<string> p1(new string ("test")); 
unique_ptr<string> p2; 
p2 = p1;                                      // #1 不允许
unique_ptr<string> p3; 
p3 = unique_ptr<string>(new string ("test"));   // #2 允许

其中留下悬挂的unique_ptr(pu1),这可能导致危害。而p3不会留下悬挂的unique_ptr,因为它调用 unique_ptr 的构造函数,该构造函数创建的临时对象在其所有权让给 p3 后就会被销毁。
shared_ptr:实现共享式拥有概念。多个智能指针可以指向相同对象,该对象和其相关资源会在“最后一个引用被销毁”时候释放。它采用计数机制来表明资源被几个指针共享,shared_ptr 是为了解决 auto_ptr 在对象所有权上的局限性(auto_ptr 是独占的), 在使用引用计数的机制上提供了可以共享所有权的智能指针。
成员函数:
use_count 返回引用计数的个数。
unique 返回是否是独占所有权( use_count 为 1)。
swap 交换两个 shared_ptr 对象(即交换所拥有的对象)。
reset 放弃内部对象的所有权或拥有对象的变更, 会引起原有对象的引用计数的减少。
get 返回内部对象(指针), 由于已经重载了()方法, 因此和直接使用对象是一样的。

share_ptr<int> sp1(new int(22));
share_ptr<int> sp2;
cout<<*sp1<<ebdl;//22
cout<<*sp2<,endl;//22
sp1.reset();
cout<<*sp2<,endl;//22

因为sp1和sp2共享一块内存,当sp1调用reset时不影响sp2,只有导致引用次数的降低,不会导致内存释放。
weak_ptr: 是一种不控制对象生命周期的智能指针, 它指向一个 shared_ptr 管理的对象, weak_ptr只是提供了对管理对象的一个访问手段,可以解决share_ptr之间相互引用导致的死锁问题。

class D;	//声明
class C
{
public:
	shared_ptr<D> pd_;
	~C()
	{
		cout << "C delete\n";
	}
};

class D
{
public:
	shared_ptr<C> pc_;
	~D()
	{
		cout << "D delete\n";
	}
};

void test()
{
	shared_ptr<D> pd(new D());
	shared_ptr<C> pc(new C());
	cout << pd.use_count() << endl;	//1
	cout << pc.use_count() << endl;	//1
	pd->pc_ = pc;
	pc->pd_ = pd;
	cout << pd.use_count() << endl;	//2
	cout << pc.use_count() << endl;	//2
}

int main()
{
	test();
	return 0;
}

可以看到test函数中pc ,pd之间互相引用,两个资源的引用计数为2,当智能指针pc,pd析构时两个资源引用计数会减1,但是两者引用计数还是为1,导致跳出函数时资源没有被释放,造成内存泄露。如果把其中一个改为weak_ptr就可以了,我们把类A里面的shared_ptr pb_,改为weak_ptr pb_ ,运行结果如下:

1
1
1
2
D delete
C delete

参考链接:

  1. https://blog.youkuaiyun.com/cywosp/article/details/26469435。
  2. https://blog.youkuaiyun.com/cywosp/article/details/26876231。
  3. https://www.cnblogs.com/sunyllove/p/9968219.html。
  4. https://blog.youkuaiyun.com/u011373710/article/details/70037649。
  5. https://blog.youkuaiyun.com/superarhow/article/details/18009667。
  6. https://www.cnblogs.com/chongjz/p/12562677.html。
  7. https://www.cnblogs.com/WindSun/p/11444429.html。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值