C语言【23道】经典面试题【下】(3),2024年最新熬夜整理Web前端面试笔试题

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

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

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

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

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

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

正文

for(i = 0; i < MAX-1 && flag == 1; i++) {

flag = 0;

for(j = 0; j < MAX-i-1; j++) {

if(number[j+1] < number[j]) {

SWAP(number[j+1], number[j]);

flag = 1;

}

}

printf(“第%d 次排序:”, i+1);

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

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

printf(“\n”);

}

}




[]( )14.排序法-改良的插入排序

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



说明



> 插入排序法由未排序的后半部前端取出一个值,插入已排序前半部的适当位置,概念简单但速度不快。排序要加快的基本原则之一,是让后一次的排序进行时,尽量利用前一次排序后的结果,以加快排序的速度,Shell排序法即是基于此一概念来改良插入排序法。

> 

> 解法  

> Shell排序法最初是D.L Shell于1959所提出,假设要排序的元素有n个,则每次进行插入排序时并不是所有的元素同时进行时,而是取一段间隔。  

> Shell首先将间隔设定为n/2,然后跳跃进行插入排序,再来将间隔n/4,跳跃进行排序动作,再来间隔设定为n/8、n/16,直到间隔为1之后的最后一次排序终止,由于上一次的排序动作都会将固定间隔内的元素排序好,所以当间隔越来越小时,某些元素位于正确位置的机率越高,因此最后几次的排序动作将可以大幅减低。举个例子来说,假设有一未排序的数字如右:89 12 65 97 61 81 27 2 61 98数字的总数共有10个,所以第一次我们将间隔设定为10 / 2 = 5,此时我们对间隔为5的数字进行排序,如下所示:  

> 画线连结的部份表示要一起进行排序的部份,再来将间隔设定为5 / 2的商,也就是2,则第二次的插入排序对象如下所示:再来间隔设定为2 / 2 = 1,此时就是单纯的插入排序了,由于大部份的元素都已大致排序过了,  

> 所以最后一次的插入排序几乎没作什么排序动作了:  

> 将间隔设定为n / 2是D.L Shell最初所提出,在教科书中使用这个间隔比较好说明,然而Shell排序法的关键在于间隔的选定,例如Sedgewick证明选用以下的间隔可以加快Shell排序法的速度:  

> 其中4\*(2j)2 + 3\*(2j) + 1不可超过元素总数n值,使用上式找出j后代入4\*(2j)2 + 3\*(2j) + 1求得第一个间隔,然后将2j除以2代入求得第二个间隔,再来依此类推。后来还有人证明有其它的间隔选定法可以将Shell排序法的速度再加快;另外Shell排序法的概念也可以用来改良气泡排序法。



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

int main(void) {

int number[MAX] = {0};

int i;

srand(time(NULL));

printf(“排序前:”);

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

number[i] = rand() % 100;

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

}

shellsort(number);

return 0;

}

void shellsort(int number[]) {

int i, j, k, gap, t;

gap = MAX / 2;

while(gap > 0) {

for(k = 0; k < gap; k++) {

for(i = k+gap; i < MAX; i+=gap) {

for(j = i - gap; j >= k; j-=gap) {

if(number[j] > number[j+gap]) {

SWAP(number[j], number[j+gap]);

}

else

break;

}

}

}

printf(“\ngap = %d:”, gap);

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

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

printf(“\n”);

gap /= 2;

}

}




[]( )15.排序法-改良的气泡排序

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



说明  

请看看之前介绍过的气泡排序法:



for(i = 0; i < MAX-1 && flag == 1; i++) {

flag = 0;

for(j = 0; j < MAX-i-1; j++) {

if(number[j+1] < number[j]) {

SWAP(number[j+1], number[j]);

flag = 1;

}

}

}




事实上这个气泡排序法已经不是单纯的气泡排序了,它使用了旗标与右端左移两个方法来改进排序的效能,而Shaker排序法使用到后面这个观念进一步改良气泡排序法。  

解法  

在上面的气泡排序法中,交换的动作并不会一直进行至阵列的最后一个,而是会进行至MAX-i-1,所以排序的过程中,阵列右方排序好的元素会一直增加,使得左边排序的次数逐渐减少,如我们的例子所示:  

排序前:95 27 90 49 80 58 6 9 18 50  

27 90 49 80 58 6 9 18 50 \[95\] 95浮出  

27 49 80 58 6 9 18 50 \[90 95\] 90浮出  

27 49 58 6 9 18 50 \[80 90 95\] 80浮出  

27 49 6 9 18 50 \[58 80 90 95\] …  

27 6 9 18 49 \[50 58 80 90 95\] …  

6 9 18 27 \[49 50 58 80 90 95\] …  

6 9 18 \[27 49 50 58 80 90 95\]  

方括号括住的部份表示已排序完毕,Shaker排序使用了这个概念,如果让左边的元素也具有这样的性质,让左右两边的元素都能先排序完成,如此未排序的元素会集中在中间,由于左右两边同时排序,中间未排序的部份将会很快的减少。方法就在于气泡排序的双向进行,先让气泡排序由左向右进行,再来让气泡排序由右往左进行,如此完成一次排序的动作,而您必须使用left与right两个旗标来记录左右两端已排序的元素位置。  

一个排序的例子如下所示:  

排序前:45 19 77 81 13 28 18 19 77 11  

往右排序:19 45 77 13 28 18 19 77 11 \[81\]  

向左排序:\[11\] 19 45 77 13 28 18 19 77 \[81\]  

往右排序:\[11\] 19 45 13 28 18 19 \[77 77 81\]  

向左排序:\[11 13\] 19 45 18 28 19 \[77 77 81\]  

往右排序:\[11 13\] 19 18 28 19 \[45 77 77 81\]  

向左排序:\[11 13 18\] 19 19 28 \[45 77 77 81\]  

往右排序:\[11 13 18\] 19 19 \[28 45 77 77 81\]  

向左排序:\[11 13 18 19 19\] \[28 45 77 77 81\]  

如上所示,括号中表示左右两边已排序完成的部份,当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;

}

#### 最后更多分享:**前端字节跳动真题解析**

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




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

**需要这份系统化的资料的朋友,可以添加V获取:vip1024c (备注前端)**
![img](https://img-blog.csdnimg.cn/img_convert/3ed45064d73974c56d9e68c7f9caa005.png)

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

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;

}



最后更多分享:前端字节跳动真题解析
  • [外链图片转存中…(img-bsCKWdEl-1713028973400)]

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

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值