c/c++ 測試題
- 1 設計一個哈希表
#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;
}
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記錄槽位容量。具體可參照Fastdfs的設計。
- 2 實現⼀個 timer 的方法:
散列表作爲定時器掛載的數據結構,使用時間輪模型,詳情請參照博客:fastdfs定時器設計
std::multimap容器,保存的有序的鍵/值對,元素可以重複。(滿足定時器最重要的兩個應用指標:有序性,探針定時時間可以精確設置爲下一次的超時時間;可在同一個時間戳定義多個不同的定時任務)
github定時器項目地址
紅黑樹
定時器的實現原理都比較相近。每種定時器都有適應的業務場景:
大量時間間隔相同的事件,用散列表管理 ,反正執行順序,和放入的順序一樣。
其他的用堆,減少堆上數據 ,減少維護,防止“爆堆”。
不同的定時器之間,性能差異是在定時時間調整,定時任務插入,定時任務獲取,和靈活性,擴展性,這些指標與業務場景的適配通常是一種平衡。
golang 試題
- 請實現 Check 函數,調⽤ check_word_correct
package main
/*
#include <stdio.h>
#include <stdlib.h>
int checkword(const char **texts, int *correct_level)
{
printf("the content is:%s\n",str);
}
*/
import "C"
import "unsafe"
import "errors"
func main(){
texts := []string{"aa", "bb", "cc"}
Check(texts)
}
// 返回值: 正確級別 [0,10]
func Check(texts []string) (int, error) {
ctexts := make([](*C.char), 0)
for i,_ := range texts{
char := C.CString(texts[i])
defer C.free(unsafe.Pointer(char))
strptr := (*C.char)(unsafe.Pointer(char))
ctexts = append(ctexts, strptr)
}
var level int;
ret := C.checkword((**C.char)(unsafe.Pointer(&ctexts[0])), (*C.int)(unsafe.Pointer(&level)))
if ret == 0 {
return level, nil
} else {
return level, errors.New("C.checkword occured some error")
}
}
測試:
[root@bogon ll]# go build main.go
[root@bogon ll]# ./main
the content is:test
[root@bogon ll]#
- 數據持久化、數據從文件加載
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
}
- 實現⼀個允許超時的 chan string 接收器
package main
import (
"fmt"
"time"
"strconv"
)
func main() {
ch := make(chan string)
quit := make(chan bool)
go func() {
for i := 0; i < 20; i++ {
ch <- strconv.Itoa(i)
time.Sleep(time.Second)
}
}()
go func() {
timeout := time.After(time.Second * 4)
flagrun := true
for ; flagrun == true ; {
select {
case <-timeout:
fmt.Println("4秒定時器超時,向退出通道發送信號,使能進程退出。或向其它業務發出信號,實現狀態回傳")
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
string: 3
string: 4
4秒定時器超時,向退出通道發送信號,使能進程退出。或向其它業務發出信號,實現狀態回傳
程序結束
[root@bogon mianshi]#
- 順序輸出 kk
import "fmt"
func main() {
kk := map[string]int{
"app": 101,
"android": 211,
"ios": 456,
}
for k, v := range kk {
fmt.Println(k, v)
}
}
測試結果:
[root@bogon mianshi]# go build main.go
[root@bogon mianshi]#
[root@bogon mianshi]#
[root@bogon mianshi]# ./main
app 101
android 211
ios 456
[root@bogon mianshi]#