後端開發測試題目:c++/golang

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 試題

  1. 請實現 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]#
  1. 數據持久化、數據從文件加載
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 < 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]#
  1. 順序輸出 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]# 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章