開頭簡單的自我介紹,面試官和我聊了聊天緩解個人緊張狀況,然后就讓開螢屏共享開視頻做題目,做完以后,問了一些問題,就讓等通知了,估計是涼了,不過這里且把當時做的筆試題目復盤一下吧!題目是ai做的題解,唉,AI都比我強,比我面試的時候解釋的強多了,未來該何去何從啊...
微*團隊c++筆試題
45分鐘
一、填空題
1、請計算32位機器上如下代碼輸出值:
void Func(char str[100]){
cout<<sizeof(str)<<endl;
}
int main(){
char str[] = "Hello";
char *p = str;
int n = 10;
void *p1 = malloc(100);
cout<<sizeof(str)<<endl;//6
cout<<sizeof(p)<<endl; //8
cout<<sizeof(p1)<<endl; //8
cout<<sizeof(n)<<endl;//4
Func(str);//8
return 0;
}
2、請問運行Test函式會有出現結果: 段錯誤_____
#include <bits/stdc++.h>
using namespace std;
void GetMemory(char *p){
p = (char *)malloc(100);
}
void Test(void) {
char *str = NULL;
GetMemory(str);
strcpy(str, "hello world");
printf("%s\n",str);
}
int main(){
Test();
return 0;
}
這段代碼會出現段錯誤的原因是在GetMemory函式中,雖然p指標指向了分配的記憶體空間,但是由于p是傳遞的指標副本,函式結束后并不會改變原來的指標str,因此在Test函式中使用str指標時,它仍然是NULL指標,所以會出現段錯誤,
為了解決這個問題,可以將GetMemory函式的引數改為指向指標的指標,這樣就可以改變原來的指標了,修改后的代碼如下:
#include <bits/stdc++.h>
using namespace std;
void GetMemory(char **p){
*p = (char *)malloc(100);
}
void Test(void) {
char *str = NULL;
GetMemory(&str); // 傳遞指向指標的指標
strcpy(str, "hello world");
printf("%s\n",str);
}
int main(){
Test();
return 0;
}
3、static區域變數與普通區域變數有什么區別
static區域變數和普通區域變數的主要區別在于它們的生命周期和作用域,
普通區域變數的生命周期只存在于函式呼叫期間,當函式回傳時,它所占用的記憶體空間會被釋放,因此,每次函式呼叫時,都會重新創建一個新的區域變數,
而static區域變數的生命周期則存在于整個程式運行期間,即使函式回傳,它所占用的記憶體空間也不會被釋放,因此,static區域變數在第一次創建后就一直存在于記憶體中,下次函式呼叫時可以繼續使用之前的值,
另外,static區域變數的作用域僅限于定義它的函式內部,但是它在函式呼叫期間保持其值不變,因此可以用來實作函式內部的計數器或狀態標記等功能,
總之,static區域變數和普通區域變數都是在函式內部定義的區域變數,但是它們的生命周期和作用域有所不同,
4、寫出下列代碼的輸出內容
include <stdio.h>
int inc(int a){
return (++a);
}
int multi(int* a, int *b, int* c){
return(*c = *a * *b);
}
typedef int (*FUNC1 ) (int in);
typedef int (*FUNC2) (int*, int*, int*);
void show(FUNC2 fun, int arg1, int* arg2){
FUNC1 p = &inc;
int temp = p(arg1);
fun(&temp, &arg1, arg2);
printf(" %d\n", *arg2);
}
main(){
int a;
show(multi, 10, &a);
return 0;
}
110
5、請找出下面代碼中的所有錯誤
說明:以下代碼是把一個字串倒序,如“abcd”倒序后變為“dcba”
#include "string.h"
main()
{
char* src = "https://www.cnblogs.com/bingeblog/archive/2023/05/12/hello , world";
char* dest = NULL;
int len = strlen(src);
dest = (char*) malloc(len );
char* d = dest;
char* s = src[len];
while(len-- != 0 )
d++ = s--;
printf("%s", dest);
return 0;
}
#include <string.h>
#include <stdlib.h> // 需要包含stdlib.h頭檔案
int main() { // main函式需要指定回傳值型別int
const char* src = "https://www.cnblogs.com/bingeblog/archive/2023/05/12/hello , world"; // src是指向字串常量的指標,需要加上const關鍵字
char* dest = NULL;
int len = strlen(src);
dest = (char*) malloc(len + 1); // 分配的記憶體空間應該是len+1,因為字串以'\0'結尾
char* d = dest + len - 1; // d指向dest的最后一個字符
const char* s = src; // s指向src的第一個字符
while(len-- != 0) {
*d-- = *s++; // 將s所指向的字符賦值給d所指向的字符,并將d和s分別向前和向后移動一位
}
*d = '\0'; // 需要在dest的末尾添加'\0',使其成為一個以'\0'結尾的字串
printf("%s", dest);
free(dest); // 釋放動態分配的記憶體
return 0;
}
/*
忘記包含stdlib.h頭檔案,導致編譯器無法識別malloc和free函式,
src是指向字串常量的指標,需要加上const關鍵字,
分配的記憶體空間應該是len+1,因為字串以'\0'結尾,
d指向dest的最后一個字符,而不是dest的第一個字符,
s指向src的第一個字符,而不是src的最后一個字符,
在while回圈中,需要將s所指向的字符賦值給d所指向的字符,并將d和s分別向前和向后移動一位,
需要在dest的末尾添加'\0',使其成為一個以'\0'結尾的字串,
在程式結束前需要釋放動態分配的記憶體
*/
6.請問下面代碼是否合法,為什么?
uint16_t wId = 2; //合法賦值
uint16_t* p1 = &wId; //合法,p1指向wld
uint32_t *p2 = p1; //原因是在將uint16_t型別的指標p1賦值給uint32_t型別的指標p2時,發生了型別不匹配的錯誤,p1指向的是一個16位的無符號整數,而p2指向的是一個32位的無符號整數,這兩種型別的指標所指向的資料的大小不同,因此,將p1賦值給p2會導致指標型別不匹配的錯誤,
uint32_t dwId = *p2; //程式試圖將一個32位的無符號整數賦值給一個16位的無符號整數,這會導致截斷錯誤,具體來說,如果dwId的值大于16位無符號整數的最大值(65535),則只會保留低16位,高16位會被截斷,這可能會導致程式邏輯錯誤或崩潰,
二、編程題
請撰寫能直接實作strstr()函式功能的代碼(在str1中找到是否包含str2,若包含回傳str1中匹配的起始指標)
char* strstr(const char* str1, const char* str2) {
if (*str2 == '\0') {
return (char*) str1;
}
const char* p1 = str1;
while (*p1 != '\0') {
const char* p1_begin = p1;
const char* p2 = str2;
while (*p1 != '\0' && *p2 != '\0' && *p1 == *p2) {
p1++;
p2++;
}
if (*p2 == '\0') {
return (char*) p1_begin;
}
if (*p1 == '\0') {
return nullptr;
}
p1 = p1_begin + 1;
}
return nullptr;
}
/*
該函式首先判斷str2是否為空字串,如果是,則直接回傳str1的起始指標,然后在str1中回圈查找,每次從當前位置開始,與str2逐個字符比較,如果匹配成功,則繼續比較下一個字符,否則從下一個位置開始重新比較,如果str2匹配完了,則回傳當前位置的起始指標;如果str1匹配完了,則表示沒有找到,回傳nullptr,
*/
//kmp演算法
#include <iostream>
#include <vector>
using namespace std;
// 計算next陣列
void getNext(string pattern, vector<int>& next) {
int n = pattern.size();
next.resize(n);
next[0] = -1;
int j = -1;
for (int i = 1; i < n; i++) {
while (j >= 0 && pattern[i] != pattern[j + 1]) {
j = next[j];
}
if (pattern[i] == pattern[j + 1]) {
j++;
}
next[i] = j;
}
}
// KMP演算法
int kmp(string text, string pattern) {
int n = text.size();
int m = pattern.size();
if (m == 0) {
return 0;
}
vector<int> next;
getNext(pattern, next);
int j = -1;
for (int i = 0; i < n; i++) {
while (j >= 0 && text[i] != pattern[j + 1]) {
j = next[j];
}
if (text[i] == pattern[j + 1]) {
j++;
}
if (j == m - 1) {
return i - j;
}
}
return -1;
}
int main() {
string text = "ababcabcacbab";
string pattern = "abcac";
int pos = kmp(text, pattern);
if (pos == -1) {
cout << "Pattern not found in text" << endl;
} else {
cout << "Pattern found in text at position " << pos << endl;
}
return 0;
}
KMP演算法的思路是,在匹配字串的程序中,當發現某個字符不匹配時,不需要從頭開始重新匹配,而是利用已經匹配的資訊,盡可能地減少比較次數,
具體實作上,首先需要計算出模式串的next陣列,next陣串列示當匹配失敗時,應該將模式串向右移動幾位,然后在匹配字串的程序中,利用next陣列進行跳轉,如果當前字符匹配成功,則繼續比較下一個字符;如果匹配失敗,則根據next陣列跳轉到模式串的某個位置,重新開始比較,
在上面的代碼中,getNext函式計算模式串的next陣列,kmp函式實作了KMP演算法,
三、演算法(以下兩題,任選一題)
1、用拉鏈法實作hash,介面:插入,查找,洗掉
hash函式,可以不實作
要求要用鏈表實作
#include <iostream>
#include <vector>
using namespace std;
// 哈希表節點
struct HashNode {
int key;
int value;
HashNode* next;
HashNode(int k, int v) : key(k), value(v), next(nullptr) {}
};
// 哈希表
class HashTable {
private:
vector<HashNode*> table;
int capacity;
int size;
// 計算哈希值
int hash(int key) {
return key % capacity;
}
public:
// 建構式
HashTable(int cap) : capacity(cap), size(0) {
table.resize(capacity, nullptr);
}
// 插入元素
void insert(int key, int value) {
int index = hash(key);
HashNode* node = table[index];
while (node != nullptr) {
if (node->key == key) {
node->value = https://www.cnblogs.com/bingeblog/archive/2023/05/12/value;
return;
}
node = node->next;
}
node = new HashNode(key, value);
node->next = table[index];
table[index] = node;
size++;
}
// 查找元素
bool find(int key, int& value) {
int index = hash(key);
HashNode* node = table[index];
while (node != nullptr) {
if (node->key == key) {
value = node->value;
return true;
}
node = node->next;
}
return false;
}
// 洗掉元素
bool remove(int key) {
int index = hash(key);
HashNode* node = table[index];
HashNode* prev = nullptr;
while (node != nullptr) {
if (node->key == key) {
if (prev == nullptr) {
table[index] = node->next;
} else {
prev->next = node->next;
}
delete node;
size--;
return true;
}
prev = node;
node = node->next;
}
return false;
}
};
int main() {
HashTable hashTable(10);
hashTable.insert(1, 10);
hashTable.insert(2, 20);
hashTable.insert(3, 30);
int value;
if (hashTable.find(2, value)) {
cout <<"Value of key 2 is " << value << endl;
} else {
cout << "Key 2 not found" << endl;
}
if (hashTable.remove(3)) {
cout << "Key 3 removed" << endl;
} else {
cout << "Key 3 not found" << endl;
}
return 0;
}
/*
哈希表是一種常用的資料結構,它可以實作快速的查找、插入和洗掉操作,哈希表的核心思想是將鍵映射到一個存盤位置,這個存盤位置就是哈希值,為了解決哈希沖突,可以使用拉鏈法,即將每個哈希值對應的元素放在一個鏈表中,
上面的代碼中,HashTable類表示哈希表,HashNode結構體表示哈希表的節點,哈希表使用vector實作,每個元素是一個指向鏈表頭節點的指標,插入、查找、洗掉操作都需要計算出哈希值,然后在相應的鏈表中進行操作,
*/
2、實作一個大根堆,兩個程序:
a、構建堆
b、彈出堆頂資料
#include <iostream>
#include <vector>
using namespace std;
// 構建大根堆
void buildMaxHeap(vector<int>& nums) {
int n = nums.size();
for (int i = n / 2 - 1; i >= 0; i--) { // 從最后一個非葉子節點開始調整
int j = i;
while (j * 2 + 1 < n) { // 如果有左孩子
int k = j * 2 + 1; // 左孩子的下標
if (k + 1 < n && nums[k + 1] > nums[k]) { // 如果有右孩子且右孩子比左孩子大
k++; // 右孩子的下標
}
if (nums[k] > nums[j]) { // 如果孩子比父節點大
swap(nums[k], nums[j]); // 交換父節點和孩子
j = k; // 繼續向下調整
} else {
break;
}
}
}
}
// 彈出堆頂元素
int popMaxHeap(vector<int>& nums) {
int n = nums.size();
int maxVal = nums[0];
nums[0] = nums[n - 1]; // 把最后一個元素放到堆頂
nums.pop_back(); // 洗掉最后一個元素
n--;
int i = 0;
while (i * 2 + 1 < n) { // 如果有左孩子
int j = i * 2 + 1; // 左孩子的下標
if (j + 1 < n && nums[j + 1] > nums[j]) { // 如果有右孩子且右孩子比左孩子大
j++; // 右孩子的下標
}
if (nums[j] > nums[i]) { // 如果孩子比父節點大
swap(nums[j], nums[i]); // 交換父節點和孩子
i = j; // 繼續向下調整
} else {
break;
}
}
return maxVal;
}
int main() {
vector<int> nums = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5};
buildMaxHeap(nums);
while (!nums.empty()) {
cout << popMaxHeap(nums) << " ";
}
cout << endl;
return 0;
}
/*
大根堆是一種常用的資料結構,它可以實作快速的查找最大值、插入和洗掉操作,大根堆的核心思想是將元素按照完全二叉樹的形式存盤,并且滿足每個節點的值都大于等于其左右孩子節點的值,
上面的代碼中,buildMaxHeap函式用于構建大根堆,popMaxHeap函式用于彈出堆頂元素,構建大根堆的程序是從最后一個非葉子節點開始,依次向上調整每個節點,使得整個堆滿足大根堆的性質,彈出堆頂元素的程序是把最后一個元素放到堆頂,然后依次向下調整每個節點,使得整個堆重新滿足大根堆的性質,
*/
本文來自博客園,作者:BingeBlog,轉載請注明原文鏈接:https://www.cnblogs.com/bingeblog/p/17396294.html
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/552342.html
標籤:其他
上一篇:2020年年終總結
下一篇:返回列表
