2024年最全C语言【23道】经典面试题【下】,大厂Web前端面试真题精选

结尾

正式学习前端大概 3 年多了,很早就想整理这个书单了,因为常常会有朋友问,前端该如何学习,学习前端该看哪些书,我就讲讲我学习的道路中看的一些书,虽然整理的书不多,但是每一本都是那种看一本就秒不绝口的感觉。

以下大部分是我看过的,或者说身边的人推荐的书籍,每一本我都有些相关的推荐语,如果你有看到更好的书欢迎推荐呀。

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

前端学习书籍导图-1

如上所示,括号中表示左右两边已排序完成的部份,当left > right时,则排序完成。


#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 shakersort(int[]);

int main(void) {

int number[MAX] = {0};

int i;

srand(time(NULL));



16.排序法-改良的选择排序

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

说明

选择排序法的概念简单,每次从未排序部份选一最小值,插入已排序部份的后端,其时间主要花费于在整个未排序部份寻找最小值,如果能让搜寻最小值的方式加快,选择排序法的速率也

就可以加快,Heap排序法让搜寻的路径由树根至最后一个树叶,而不是整个未排序部份,因而称之为改良的选择排序法。

解法

Heap排序法使用Heap Tree(堆积树),树是一种资料结构,而堆积树是一个二元树,也就是每一个父节点最多只有两个子节点(关于树的详细定义还请见资料结构书籍),堆积树的父节点若小于子节点,则称之为最小堆积(Min Heap),父节点若大于子节点,则称之为最大堆积(Max

Heap),而同一层的子节点则无需理会其大小关系,例如下面就是一个堆积树:

可以使用一维阵列来储存堆积树的所有元素与其顺序,为了计算方便,使用的起始索引是1而不是0,索引1是树根位置,如果左子节点储存在阵列中的索引为s,则其父节点的索引为s/2,而右子节点为s+1,就如上图所示,将上图的堆积树转换为一维阵列之后如下所示:

首先必须知道如何建立堆积树,加至堆积树的元素会先放置在最后一个树叶节点位置,然后检

查父节点是否小于子节点(最小堆积),将小的元素不断与父节点交换,直到满足堆积树的条件

为止,例如在上图的堆积加入一个元素12,则堆积树的调整方式如下所示:

建立好堆积树之后,树根一定是所有元素的最小值,您的目的就是:

将最小值取出然后调整树为堆积树

不断重复以上的步骤,就可以达到排序的效果,最小值的取出方式是将树根与最后一个树叶节点交换,然后切下树叶节点,重新调整树为堆积树,如下所示:

调整完毕后,树根节点又是最小值了,于是我们可以重覆这个步骤,再取出最小值,并调整树为堆积树,如下所示:

如此重覆步骤之后,由于使用一维阵列来储存堆积树,每一次将树叶与树根交换的动作就是将最小值放至后端的阵列,所以最后阵列就是变为已排序的状态。其实堆积在调整的过程中,就是一个选择的行为,每次将最小值选至树根,而选择的路径并不是所有的元素,而是由树根至树叶的路径,因而可以加快选择的过程, 所以Heap排序法才会被称之为改良的选择排序法。


#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 createheap(int[]);

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 = N*i - 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;

}


### 最后

**前端CSS面试题文档,JavaScript面试题文档,Vue面试题文档,大厂面试题文档**

**[开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】](https://bbs.csdn.net/forums/4304bb5a486d4c3ab8389e65ecb71ac0)**

![](https://img-blog.csdnimg.cn/img_convert/c7c1afcac69cf080253b6c29bbc0ad73.webp?x-oss-process=image/format,png)

![](https://img-blog.csdnimg.cn/img_convert/6952b7572b4025b622189113f990a83b.webp?x-oss-process=image/format,png)



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;

}

最后

前端CSS面试题文档,JavaScript面试题文档,Vue面试题文档,大厂面试题文档

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

[外链图片转存中…(img-GqksS3vo-1715641997066)]

[外链图片转存中…(img-Ru5hBXTv-1715641997067)]

  • 17
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值