C语言【23道】经典面试题【下】(2),2024年华为Web前端面试真题解析

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Web前端全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024c (备注前端)
img

正文

void heapsort(int[]);

int main(void) {

int number[MAX+1] = {-1};

int i, num;

srand(time(NULL));

printf(“排序前:”);

for(i = 1; i <= MAX; i++) {

number[i] = rand() % 100;

printf("%d ", number[i]);

}

printf(“\n建立堆积树:”);

createheap(number);

for(i = 1; i <= MAX; i++)

printf("%d ", number[i]);

printf(“\n”);

heapsort(number);

printf(“\n”);

return 0;

}

void createheap(int number[]) {

int i, s, p;

int heap[MAX+1] = {-1};

for(i = 1; i <= MAX; i++) {

heap[i] = number[i];

s = i;

p = i / 2;

while(s >= 2 && heap[p] > heap[s]) {

SWAP(heap[p], heap[s]);

s = p;

p = s / 2;

}

}

for(i = 1; i <= MAX; i++)

number[i] = heap[i];

}

void heapsort(int number[]) {

int i, m, p, s;

m = MAX;

while(m > 1) {

SWAP(number[1], number[m]);

m–;

p = 1;

s = 2 * p;

while(s <= m) {

if(s < m && number[s+1] < number[s])

s++;

if(number[p] <= number[s])

break;

SWAP(number[p], number[s]);

p = s;

s = 2 * p;

}

printf(“\n排序中:”);

for(i = MAX; i > 0; i–)

printf("%d ", number[i]);

}

}




[]( )17.快速排序法(一)

=======================================================================



说明



> 快速排序法(quick sort)是目前所公认最快的排序方法之一(视解题的对象而定),虽然快速排序法在最差状况下可以达O(n2),但是在多数的情况下,快速排序法的效率表现是相当不错的。快速排序法的基本精神是在数列中找出适当的轴心,然后将数列一分为二,分别对左边与右边数列进行排序,而影响快速排序法效率的正是轴心的选择。这边所介绍的第一个快速排序法版本,是在多数的教科书上所提及的版本,因为它最容易理解,也最符合轴心分割与左右进行排序的概念,适合对初学者进行讲解。

> 

> 解法  

> 这边所介绍的快速演算如下:将最左边的数设定为轴,并记录其值为s  

> 廻圈处理:



1.  令索引i 从数列左方往右方找,直到找到大于s 的数

2.  令索引j 从数列左右方往左方找,直到找到小于s 的数

3.  如果i >= j,则离开回圈

4.  如果i < j,则交换索引i与j两处的值

5.  将左侧的轴与j 进行交换

6.  对轴左边进行递回

7.  对轴右边进行递回



透过以下演算法,则轴左边的值都会小于s,轴右边的值都会大于s,如此再对轴左右两边进行递回,就可以对完成排序的目的,例如下面的实例,_表示要交换的数,\[\]表示轴:  

\[41\] 24 76_ 11 45 64 21 69 19 36\*  

\[41\] 24 36 11 45\* 64 21 69 19\* 76  

\[41\] 24 36 11 19 64\* 21\* 69 45 76  

\[41\] 24 36 11 19 21 64 69 45 76  

21 24 36 11 19 \[41\] 64 69 45 76  

在上面的例子中,41左边的值都比它小,而右边的值都比它大,如此左右再进行递回至排序完成。



#include <stdio.h>

#include <stdlib.h>

#include <time.h>

#define MAX 10

#define SWAP(x,y) {int t; t = x; x = y; y = t;}

void quicksort(int[], int, int);

int main(void) {

int number[MAX] = {0};

int i, num;

srand(time(NULL));

printf(“排序前:”);

for(i = 0; i < MAX; i++) {

number[i] = rand() % 100;

printf("%d ", number[i]);

}

quicksort(number, 0, MAX-1);

printf(“\n排序后:”);

for(i = 0; i < MAX; i++)

printf("%d ", number[i]);

printf(“\n”);

return 0;

}

void quicksort(int number[], int left, int right) {

int i, j, s;

if(left < right) {

s = number[left];

i = left;

j = right + 1;

while(1) {

// 向右找

while(i + 1 < number.length && number[++i] < s) ;

// 向左找

while(j -1 > -1 && number[–j] > s) ;

if(i >= j)

break;

SWAP(number[i], number[j]);

}

number[left] = number[j];

number[j] = s;

quicksort(number, left, j-1); // 对左边进行递回

quicksort(number, j+1, right); // 对右边进行递回

}

}




[]( )18.快速排序法(二)

=======================================================================



说明



> 在快速排序法(一)中,每次将最左边的元素设为轴,而之前曾经说过,快速排序法的加速在于轴的选择,在这个例子中,只将轴设定为中间的元素,依这个元素作基准进行比较,这可以增加快速排序法的效率。

> 

> 解法  

> 在这个例子中,取中间的元素s作比较,同样的先得右找比s大的索引i,然后找比s小的索引j,只要两边的索引还没有交会,就交换i 与j 的元素值,这次不用再进行轴的交换了,因为在寻找交换的过程中,轴位置的元素也会参与交换的动作,例如:  

> 41 24 76 11 45 64 21 69 19 36  

> 首先left为0,right为9,(left+right)/2 = 4(取整数的商),所以轴为索引4的位置,比较的元素是45,您往右找比45大的,往左找比45小的进行交换:  

> 41 24 76\* 11 \[45\] 64 21 69 19 _36  

> 41 24 36 11 45_ 64 21 69 19\* 76  

> 41 24 36 11 19 64\* 21\* 69 45 76  

> \[41 24 36 11 19 21\] \[64 69 45 76\]  

> 完成以上之后,再初别对左边括号与右边括号的部份进行递回,如此就可以完成排序的目的。



#include <stdio.h>

#include <stdlib.h>

#include <time.h>

#define MAX 10

#define SWAP(x,y) {int t; t = x; x = y; y = t;}

void quicksort(int[], int, int);

int main(void) {

int number[MAX] = {0};

int i, num;

srand(time(NULL));

printf(“排序前:”);

for(i = 0; i < MAX; i++) {

number[i] = rand() % 100;

printf("%d ", number[i]);

}

quicksort(number, 0, MAX-1);

printf(“\n排序后:”);

for(i = 0; i < MAX; i++)

printf("%d ", number[i]);

printf(“\n”);

return 0;

}

void quicksort(int number[], int left, int right) {

int i, j, s;

if(left < right) {

s = number[(left+right)/2];

i = left - 1;

j = right + 1;

while(1) {

while(number[++i] < s) ; // 向右找

while(number[–j] > s) ; // 向左找

if(i >= j)

break;

SWAP(number[i], number[j]);

}

quicksort(number, left, i-1); // 对左边进行递回

quicksort(number, j+1, right); // 对右边进行递回

}

}




[]( )19.快速排序法(三)

=======================================================================



说明



> 之前说过轴的选择是快速排序法的效率关键之一,在这边的快速排序法的轴选择方式更加快了快速排序法的效率,它是来自演算法名书Introduction to Algorithms 之中。

> 

> 解法  

> 先说明这个快速排序法的概念,它以最右边的值s作比较的标准,将整个数列分为三个部份,一个是小于s的部份,一个是大于s的部份,一个是未处理的部份,如下所示:



在排序的过程中,i 与j 都会不断的往右进行比较与交换,最后数列会变为以下的状态:



然后将s的值置于中间,接下来就以相同的步骤会左右两边的数列进行排序的动作,如下所示:



然后将s的值置于中间,接下来就以相同的步骤会左右两边的数列进行排序的动作,如下所示:



整个演算的过程,直接摘录书中的虚拟码来作说明:



QUICKSORT(A, p, r)

if p < r

then q <- PARTITION(A, p, r)

QUICKSORT(A, p, q-1)

QUICKSORT(A, q+1, r)

end QUICKSORT

PARTITION(A, p, r)

x <- A[r]

i <- p-1

for j <- p to r-1

do if A[j] <= x

then i <- i+1

exchange A[i]<->A[j]

exchange A[i+1]<->A[r]

return i+1

end PARTITION




一个实际例子的演算如下所示:



#include <stdio.h>

#include <stdlib.h>

#include <time.h>

#define MAX 10

#define SWAP(x,y) {int t; t = x; x = y; y = t;}

int partition(int[], int, int);

void quicksort(int[], int, int);

int main(void) {

int number[MAX] = {0};

int i, num;

srand(time(NULL));

printf(“排序前:”);

for(i = 0; i < MAX; i++) {

number[i] = rand() % 100;

printf("%d ", number[i]);

}

quicksort(number, 0, MAX-1);

printf(“\n排序后:”);

for(i = 0; i < MAX; i++)

printf("%d ", number[i]);

printf(“\n”);

return 0;

}

int partition(int number[], int left, int right) {

int i, j, s;

s = number[right];

i = left - 1;

for(j = left; j < right; j++) {

if(number[j] <= s) {

i++;

SWAP(number[i], number[j]);

}

}

SWAP(number[i+1], number[right]);

return i+1;

}

void quicksort(int number[], int left, int right) {

int q;

if(left < right) {

q = partition(number, left, right);

quicksort(number, left, q-1);

quicksort(number, q+1, right);

}

}




[]( )20.多维矩阵转一维矩阵

========================================================================



说明



> 有的时候,为了运算方便或资料储存的空间问题,使用一维阵列会比二维或多维阵列来得方便,例如上三角矩阵、下三角矩阵或对角矩阵,使用一维阵列会比使用二维阵列来得节省空间。

> 

> 解法  

> 以二维阵列转一维阵列为例,索引值由0开始,在由二维阵列转一维阵列时,我们有两种方式:「以列(Row)为主」或「以行(Column)为主」。由于C/C++、Java等的记忆体配置方式都是  

> 以列为主,所以您可能会比较熟悉前者(Fortran的记忆体配置方式是以行为主)。以列为主的二维阵列要转为一维阵列时,是将二维阵列由上往下一列一列读入一维阵列,此时索引的对应公式如下所示,其中row与column是二维阵列索引,loc表示对应的一维阵列索引:loc = column + row_行数以行为主的二维阵列要转为一维阵列时,是将二维阵列由左往右一行一读入一维阵列,此时索引的对应公式如下所示:  

> loc = row + column_列数公式的推导您画图看看就知道了,如果是三维阵列,则公式如下所示,其中i(个数u1)、j(个数u2)、k(个数u3)分别表示三维阵列的三个索引:  

> 以列为主:loc = i_u2_u3 + j_u3 + k  

> 以行为主:loc = k_u1_u2 + j_u1 + i  

> 更高维度的可以自行依此类推,但通常更高维度的建议使用其它资料结构(例如物件包装)会比较具体,也不易搞错。在C/C++中若使用到指标时,会遇到指标运算与记忆体空间位址的处理问题,此时也是用到这边的公式,不过必须在每一个项上乘上资料型态的记忆体大小。



#include <stdio.h>

#include <stdlib.h>

int main(void) {

int arr1[3][4] = {{1, 2, 3, 4},

{5, 6, 7, 8},

{9, 10, 11, 12}};

int arr2[12] = {0};

int row, column, i;

printf(“原二维资料:\n”);

for(row = 0; row < 3; row++) {

for(column = 0; column < 4; column++) {

printf(“%4d”, arr1[row][column]);

}

printf(“\n”);

}

printf(“\n以列为主:”);

for(row = 0; row < 3; row++) {

for(column = 0; column < 4; column++) {

i = column + row * 4;

arr2[i] = arr1[row][column];

}

}

for(i = 0; i < 12; i++)

printf("%d ", arr2[i]);

printf(“\n以行为主:”);

for(row = 0; row < 3; row++) {

for(column = 0; column < 4; column++) {

i = row + column * 3;

arr2[i] = arr1[row][column];

}

}

for(i = 0; i < 12; i++)

printf("%d ", arr2[i]);

printf(“\n”);

return 0;

}




[]( )21.上三角、下三角、对称矩阵

===========================================================================



说明  

上三角矩阵是矩阵在对角线以下的元素均为0,即Aij = 0,i > j,例如:  

1 2 3 4 5  

0 6 7 8 9  

0 0 10 11 12  

0 0 0 13 14  

0 0 0 0 15  

下三角矩阵是矩阵在对角线以上的元素均为0,即Aij = 0,i < j,例如:  

1 0 0 0 0  

2 6 0 0 0  

3 7 10 0 0  

4 8 11 13 0  

5 9 12 14 15  

对称矩阵是矩阵元素对称于对角线,例如:  

1 2 3 4 5  

2 6 7 8 9  

3 7 10 11 12  

4 8 11 13 14  

5 9 12 14 15  

上三角或下三角矩阵也有大部份的元素不储存值(为0),我们可以将它们使用一维阵列来储存以节省储存空间,而对称矩阵因为对称于对角线,所以可以视为上三角或下三角矩阵来储存。  

解法  

假设矩阵为nxn,为了计算方便,我们让阵列索引由1开始,上三角矩阵化为一维阵列,若以列为主,其公式为:loc = n\*(i-1) - i\*(i-1)/2 + j化为以行为主,其公式为:loc = j\*(j-1)/2 + i下三角矩阵化为一维阵列,若以列为主,其公式为:loc = i\*(i-1)/2 + j若以行为主,其公式为:loc = n\*(j-1) - j\*(j-1)/2 + i公式的导证其实是由等差级数公式得到,您可以自行绘图并看看就可以导证出来,对于C/C++或Java等索引由0开始的语言来说,只要将i与j各加1,求得loc之后减1即可套用以上的公式。



#include <stdio.h>

#include <stdlib.h>

#define N 5

int main(void) {

int arr1[N][N] = {

{1, 2, 3, 4, 5},

{0, 6, 7, 8, 9},

{0, 0, 10, 11, 12},

{0, 0, 0, 13, 14},

{0, 0, 0, 0, 15}};

int arr2[N*(1+N)/2] = {0};

int i, j, loc = 0;

printf(“原二维资料:\n”);

for(i = 0; i < N; i++) {

for(j = 0; j < N; j++) {

printf(“%4d”, arr1[i][j]);

}

printf(“\n”);

}

printf(“\n以列为主:”);

for(i = 0; i < N; i++) {

for(j = 0; j < N; j++) {

if(arr1[i][j] != 0)

arr2[loc++] = arr1[i][j];

}

}

for(i = 0; i < N*(1+N)/2; i++)

printf("%d ", arr2[i]);

printf(“\n输入索引(i, j):”);

scanf(“%d, %d”, &i, &j);

loc = Ni - i(i+1)/2 + j;

printf(“(%d, %d) = %d”, i, j, arr2[loc]);

printf(“\n”);

return 0;

}




[]( )22.m元素集合的n个元素子集

===========================================================================



说明  

假设有个集合拥有m个元素,任意的从集合中取出n个元素,则这n个元素所形成的可能子集有那些?



解法  

假设有5个元素的集点,取出3个元素的可能子集如下:  

{1 2 3}、{1 2 4 }、{1 2 5}、{1 3 4}、{1 3 5}、{1 4 5}、{2 3 4}、{2 3 5}、{2 4 5}、{3 4 5}  

这些子集已经使用字典顺序排列,如此才可以观察出一些规则:



1.  如果最右一个元素小于m,则如同码表一样的不断加1

2.  如果右边一位已至最大值,则加1的位置往左移

3.  每次加1的位置往左移后,必须重新调整右边的元素为递减顺序



所以关键点就在于哪一个位置必须进行加1的动作,到底是最右一个位置要加1?还是其它的位置?在实际撰写程式时,可以使用一个变数positon来记录加1的位置,position的初值设定为n-1,因为我们要使用阵列,而最右边的索引值为最大的n-1,在position位置的值若小于m就不断加1,如果大于m了,position就减1,也就是往左移一个位置;由于位置左移后,右边的元素会经过调整,所以我们必须检查最右边的元素是否小于m,如果是,则position调整回n-1,如果不是,则positon维持不变。



#include <stdio.h>

#include <stdlib.h>

#define MAX 20

int main(void) {

int set[MAX];

int m, n, position;

int i;

printf(“输入集合个数m:”);

scanf(“%d”, &m);

printf(“输入取出元素n:”);

scanf(“%d”, &n);

for(i = 0; i < n; i++)

set[i] = i + 1;

// 显示第一个集合

for(i = 0; i < n; i++)

printf("%d ", set[i]);

putchar(‘\n’);

position = n - 1;

while(1) {

if(set[n-1] == m)

position–;

else

position = n - 1;

set[position]++;

// 调整右边元素

for(i = position + 1; i < n; i++)

set[i] = set[i-1] + 1;

for(i = 0; i < n; i++)

printf("%d ", set[i]);

putchar(‘\n’);

if(set[0] >= m - n + 1)

break;

}

return 0;

}




[]( )23.数字拆解

===================================================================



说明  

这个题目来自于数字拆解,我将之改为C语言的版本,并加上说明。  

题目是这样的:  

3 = 2+1 = 1+1+1 所以3有三种拆法  

4 = 3 + 1 = 2 + 2 = 2 + 1 + 1 = 1 + 1 + 1 + 1 共五种  

5 = 4 + 1 = 3 + 2 = 3 + 1 + 1 = 2 + 2 + 1 = 2 + 1 + 1 + 1 = 1 + 1 +1 +1 +1  

共七种依此类推,请问一个指定数字NUM的拆解方法个数有多少个?  

解法  

我们以上例中最后一个数字5的拆解为例,假设f( n )为数字n的可拆解方式个数,而f(x, y)为使用y以下的数字来拆解x的方法个数,则观察:  

5 = 4 + 1 = 3 + 2 = 3 + 1 + 1 = 2 + 2 + 1 = 2 + 1 + 1 + 1 = 1 + 1 +1 +1 +1  

使用函式来表示的话:  

f(5) = f(4, 1) + f(3,2) + f(2,3) + f(1,4) + f(0,5)  

其中f(1, 4) = f(1, 3) + f(1, 2) + f(1, 1),但是使用大于1的数字来拆解1没有意义,所以f(1, 4) =f(1, 1),而同样的,f(0, 5)会等于f(0, 0),所以:  

f(5) = f(4, 1) + f(3,2) + f(2,3) + f(1,1) + f(0,0)  

依照以上的说明,使用动态程式规画(Dynamic programming)来进行求解,其中f(4,1)其实就是f(5-1, min(5-1,1)),f(x, y)就等于f(n-y, min(n-x, y)),其中n为要拆解的数字,而min()表示取两  

者中较小的数。使用一个二维阵列表格table\[x\]\[y\]来表示f(x, y),刚开始时,将每列的索引0与索引1元素值设定为1,因为任何数以0以下的数拆解必只有1种,而任何数以1以下的数拆解也必只有1种:  

for(i = 0; i < NUM +1; i++){  

table\[i\]\[0\] = 1; // 任何数以0以下的数拆解必只有1种  

table\[i\]\[1\] = 1; // 任何数以1以下的数拆解必只有1种  

}接下来就开始一个一个进行拆解了,如果数字为NUM,则我们的阵列维度大小必须为NUM x  

(NUM/2+1),以数字10为例,其维度为10 x 6我们的表格将会如下所示:  

1 1 0 0 0 0  

1 1 0 0 0 0  

1 1 2 0 0 0  

1 1 2 3 0 0  

1 1 3 4 5 0  

1 1 3 5 6 7  

1 1 4 7 9 0  

1 1 4 8 0 0  

1 1 5 0 0 0  

1 1 0 0 0 0



#include <stdio.h>

#include <stdlib.h>

#define NUM 10 // 要拆解的数字

#define DEBUG 0

int main(void) {

int table[NUM][NUM/2+1] = {0}; // 动态规画表格

Vue 编码基础

2.1.1. 组件规范

2.1.2. 模板中使用简单的表达式

2.1.3 指令都使用缩写形式

2.1.4 标签顺序保持一致

2.1.5 必须为 v-for 设置键值 key

2.1.6 v-show 与 v-if 选择

2.1.7 script 标签内部结构顺序

2.1.8 Vue Router 规范

Vue 项目目录规范

2.2.1 基础

2.2.2 使用 Vue-cli 脚手架

2.2.3 目录说明

2.2.4注释说明

2.2.5 其他

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024c (备注前端)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
,则我们的阵列维度大小必须为NUM x

(NUM/2+1),以数字10为例,其维度为10 x 6我们的表格将会如下所示:

1 1 0 0 0 0

1 1 0 0 0 0

1 1 2 0 0 0

1 1 2 3 0 0

1 1 3 4 5 0

1 1 3 5 6 7

1 1 4 7 9 0

1 1 4 8 0 0

1 1 5 0 0 0

1 1 0 0 0 0


#include <stdio.h>

#include <stdlib.h>

#define NUM 10 // 要拆解的数字

#define DEBUG 0

int main(void) {

int table[NUM][NUM/2+1] = {0}; // 动态规画表格



#### Vue 编码基础

2.1.1. 组件规范  

2.1.2. 模板中使用简单的表达式  

2.1.3 指令都使用缩写形式  

2.1.4 标签顺序保持一致  

2.1.5 必须为 v-for 设置键值 key  

2.1.6 v-show 与 v-if 选择  

2.1.7 script 标签内部结构顺序  

2.1.8 Vue Router 规范



#### Vue 项目目录规范

2.2.1 基础  

2.2.2 使用 Vue-cli 脚手架  

2.2.3 目录说明  

2.2.4注释说明  

2.2.5 其他



![](https://i-blog.csdnimg.cn/blog_migrate/5867e944d8fc112fd02abb05651ff08e.png)

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**需要这份系统化的资料的朋友,可以添加V获取:vip1024c (备注前端)**
[外链图片转存中...(img-LjTmlVTq-1713028943617)]

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值