后端编程面试题 --- c++/golang混合

c/c++ 测试题

  • 1 根据以下提示代码,实现相应功能。查找函数性能要求为 o(1)
typedef struct peer {
struct in_addr remote_addr;
struct peer *next;
void *data;
} peer_t;
peer_t *peers[256];
peer_t * peer_lookup(uint32_t addr, peer_t **peers);
peer_t * peer_add(uint32_t addr, peer_t **peers);
int peer_remove(uint32_t addr, peer_t **peers);

实现的完整代码为:

#include <arpa/inet.h>
#include <iostream>

using namespace std;

typedef struct peer {
	struct in_addr remote_addr;
	struct peer *next;
	void *data;
} peer_t;

peer_t * peers[256];

peer_t * peer_lookup(uint32_t addr, peer_t **peers);
peer_t * peer_add(uint32_t addr, peer_t **peers);
int peer_remove(uint32_t addr, peer_t **peers);


peer_t * peer_lookup(uint32_t addr, peer_t **peers)
{
	std::hash<uint32_t> hash_fn;
	size_t hash_code = hash_fn(addr) % 256;
	peer_t * ppeer = NULL;

	if (ppeer = peers[hash_code])
	{
		while (ppeer) {
			if (ppeer->remote_addr.s_addr = htonl(addr)) {
				cout << "lookup node ok" << endl;
				break;
			}
		}
	}

	return ppeer;
}

//ntohl()
peer_t * peer_add(uint32_t addr, peer_t **peers)
{
	std::hash<uint32_t> hash_fn;
	size_t hash_code = hash_fn(addr) % 256;

	auto peer = new peer_t;
	peer->remote_addr.s_addr = htonl(addr);
	peer->data = NULL;
	if (peers[hash_code] == NULL) {
		peers[hash_code] = peer;
		peer->next = NULL;
	}
	else {
		peer->next = peers[hash_code];
		peers[hash_code] = peer;
	}

	return peer;
}

int peer_remove(uint32_t addr, peer_t **peers)
{
	int ret = -1;
	std::hash<uint32_t> hash_fn;
	size_t hash_code = hash_fn(addr) % 256;
	peer_t * prev = NULL, * ppeer = NULL;

	if (ppeer = peers[hash_code])
	{
		while (ppeer) {
			if (ppeer->remote_addr.s_addr = htonl(addr)) {
				if (NULL == prev) {
					peers[hash_code] = ppeer->next;
				}
				else {
					prev->next = ppeer->next;
				}

				delete ppeer;
				cout << "deleet node from hash table" << endl;
				ret = 0;
				break;
			}
			prev = ppeer;
		}
	}
	return ret;
}

int main(int argc, char **argv)
{
	for(auto i=0; i<256; i++) {
		peers[i] = NULL;
	}

	cout << "now insert node" << endl;
	auto peer1 = peer_add(123, peers);
	auto peer2 = peer_add(456, peers);

	cout << endl << "after insert node 123, 456" << endl;
	auto ppeer = peer_lookup(123, peers);
	if (ppeer) {
		cout << "peer node's addr : " << ntohl(ppeer->remote_addr.s_addr) << endl;
	}
	ppeer = peer_lookup(456, peers);
	if (ppeer) {
		cout << "peer node's addr : " << ntohl(ppeer->remote_addr.s_addr) << endl;
	}


	cout << endl << "remove node 123" << endl;
	if (0 == peer_remove(123, peers)) {
		cout << "remvoe peer node whose id is 123" << endl;
	}

	cout << endl << "after remove node 123" << endl;
	ppeer = peer_lookup(123, peers);
	if (ppeer) {
		cout << "peer node's addr : " << ntohl(ppeer->remote_addr.s_addr) << endl;
	}	
	ppeer = peer_lookup(456, peers);
	if (ppeer) {
		cout << "peer node's addr : " << ntohl(ppeer->remote_addr.s_addr) << endl;
	}	
}


测试结果:

[root@bogon mianshi]# g++ main.cc 
[root@bogon mianshi]# ./a.out 
now insert node

after insert node 123, 456
lookup node ok
peer node's addr : 123
lookup node ok
peer node's addr : 456

remove node 123
deleet node from hash table
remvoe peer node whose id is 123

after remove node 123
lookup node ok
peer node's addr : 456
[root@bogon mianshi]#

真正的lookup为o(1)需要用rehash解决哈稀冲突。
解决哈西冲突前提是散列表可以扩容,peers数组需要从固定256个变成**,另外申请一个size_t len记录槽位容量。

  • 2 实现⼀个 timer ,有什么数据结构可以选择?请举2个以上例⼦并给出简要说明
    散列表作为定时器挂载的数据结构,使用时间轮模型,详情请参照博客:fastdfs定时器设计

std::multimap容器,保存的有序的键/值对,元素可以重复。(满足定时器最重要的两个应用指标:有序性,探针定时时间可以精确设置为下一次的超时时间;可在同一个时间戳定义多个不同的定时任务)
github定时器项目地址

红黑树

定时器的实现原理都比较相近。每种定时器都有适应的业务场景:

大量时间间隔相同的事件,用散列表管理 ,反正执行顺序,和放入的顺序一样。
其他的用堆,减少堆上数据 ,减少维护,防止“爆堆”。
不同的定时器之间,性能差异是在定时时间调整,定时任务插入,定时任务获取,和灵活性,扩展性,这些指标与业务场景的适配通常是一种平衡。

  • 3 是否有⾃⼰造轮⼦的经验?请简要描述
    线程池,使用reactor模型。采用队列注入任务,条件变量通讯,master管理业务线程执行。
    线程池博客地址

golang 试题

  1. 请实现 Check 函数,调⽤ check_word_correct
package main
  
/*
#include <stdio.h>
#include <stdlib.h>
int check_word_correct(const char **texts, int &correct_level)
{
        ;
}
*/
import "C"
import (
        "fmt"
        "time"
)

// 返回值: 正确级别 [0,10]
func Check(texts []string) (int, error) {
        arg := make([](*C.char), 0)  //C语言char*指针创建切片
        l := len(texts)
        for i,_ := range texts{
                char := C.CString(texts[i])
                defer C.free(unsafe.Pointer(char)) //释放内存
                strptr := (*C.char)(unsafe.Pointer(char))
                arg = append(arg, strptr)  //将char*指针加入到arg切片
        }

        var level int;
        ret := C.check_word_correct((**C.char)(unsafe.Pointer(&arg[0])), &level)
        if ret == 0 {
                return level, nil
        } else {
                return level, errors.New("C.check_word_correct occured some error")
        }
}


func main() {
        texts := []string{"aa", "bb", "cc"}
        Check(texts)
}

  1. PCM2File 函数的功能是把 16 位采样深度的 PCM 数据写⼊⽂件。请参考 pcm2Bytes , 实现
    File2PCM 函数从⽂件中把相应的 PCM 数据读取出来并给出必要的注释
func pcm2Bytes(floats []float32) []byte {
	buf := make([]int16, len(floats))
	for i, f := range floats {
		buf[i] = int16(f)
	}
	var bytes []byte
	l := int(unsafe.Sizeof(buf[0])) * len(buf)
	x := (*reflect.SliceHeader)(unsafe.Pointer(&bytes))
	x.Data = uintptr(unsafe.Pointer(&buf[0]))
	x.Len = int(l)
	x.Cap = int(l)
	return bytes
}

func PCM2File(file string, pcm []float32) error {
	return ioutil.WriteFile(file, pcm2Bytes(pcm), 0600)
} 

func Bytes2pcm(bytes []byte) []float32 {
        buf := make([]float32, len(bytes)/2)
        i := 0
        for {   
                temp := int16(bytes[i]) + int16(bytes[i+1]) * 256
                buf[i/2] = float32(temp)
                i = i + 2
                if i >= len(bytes) {
                        break               
                }
        }
        return buf
}

func File2PCM(file string) ([]float32, error) {
	var fdata[]byte
	fdata, err := ioutil.ReadFile(file)
	if err != nil {
		return []float32, errors.New("read from file error")
	}
	return Bytes2pcm(), nil
}
  1. 实现⼀个允许超时的 chan string 接收器
package main
import (
        "fmt"
        "time"
        "strconv"
)
func main() {
        ch := make(chan string)
        quit := make(chan bool)
        go func() {
                for i := 0; i < 15; i++ {
                        ch <- strconv.Itoa(i)
                        time.Sleep(time.Second)
                }
        }()

        go func() {
                timeout := time.After(time.Second * 3)
                flagrun := true
                for ; flagrun == true ; {
                        select {
                        case <-timeout:
                                fmt.Println("3秒定时器超时,向退出通道发送信号,使能进程退出。或向其它业务发出信号,实现状态回传")
                                flagrun = false
                                quit <- true
                        case str := <-ch:
                                fmt.Println("string: ", str)
                        }
                }
        }()
        <-quit
        close(ch)
        close(quit)
        fmt.Println("程序结束")
}

测试结果:

[root@bogon mianshi]# go build main.go 
[root@bogon mianshi]# ./main 
string:  0
string:  1
string:  2
3秒定时器超时,向退出通道发送信号,使能进程退出。或向其它业务发出信号,实现状态回传
程序结束
[root@bogon mianshi]#
  1. 顺序输出 fruits
  
import "fmt"

func main() {
        fruits := map[string]int{
                "oranges": 100,
                "apples": 200,
                "bananas": 300,
        }
        for  k, v := range fruits {
                fmt.Println(k, v)
        }
}

测试结果:

[root@bogon mianshi]# go build main.go 
[root@bogon mianshi]# 
[root@bogon mianshi]# 
[root@bogon mianshi]# ./main 
oranges 100
apples 200
bananas 300
[root@bogon mianshi]# 
发布了83 篇原创文章 · 获赞 71 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/jacky128256/article/details/104641139