2024年大数据最全C C++数据结构(二) ,大数据开发系统工程师面试题

img
img

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

需要这份系统化资料的朋友,可以戳这里获取

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

📝 代码示例

// 因为传过来的是phead的地址,phead是一级指针的地址,所以要拿二级指针接收
void SListPushBack(SLTNode\*\* pphead, SLTDateType x) {
	assert(pphead); // pphead不可以为空指针.
	// 先增加一个节点
	SLTNode\* newnode = BuyListNode(x);

	// 如果头节点是空,那么直接把newnode的地址赋值给头节点
	if (\*pphead == NULL) {
		// \*pphead:对pphead解引用,其实访问的就是plist;
		\*pphead = newnode;
	}
	else {
		// 找到尾节点
		SLTNode\* tail = \*pphead;
		while (tail->next != NULL) {
			tail = tail->next;
		}
		tail->next = newnode;
	}
}

代码解释

SLTNode* tail = *pphead:首先定义一个指向 头节点 的指针 tail
 
tail->next != NULL:如果 tail 节点的 next 不为 空指针 ,说明此时还没有找到尾节点,因为尾节点的 next 是为 空指针 的,则进入循环;
 
tail = tail->next:因为当前节点的 next 存放的是下一个节点的地址,那么我们就是把下一个节点的地址赋给 tail,换句话说,也就是让 tail 指向第二个节点;
 
以此类推,直到 tail->next空指针,说明 tail 已经找到了 尾节点,即跳出 while 循环;
 
tail->next = newnode:然后把 newnode 节点的地址赋给 tail->next

🍑 头插

还是和尾插一样,需要改变 phead 的值,所以我们的形参需要用二级指针接收 phead 的地址。

头部插⼊,可以分成两个步骤。

1 步,把新节点 newnodenext 指针指向原先的头节点。

2 步,把新节点变为链表的头节点,如下图所示👇
在这里插入图片描述

动图演示👇
在这里插入图片描述

注意:头插 就不用区分链表是否为空的

📝 代码示例

void SListPushFront(SLTNode\*\* pphead, SLTDateType x) {
	assert(pphead);
	SLTNode\* newnode = BuyListNode(x); // 1、创建新节点
	newnode->next = \*pphead; // 2、让新节点的next指向原来的头节点
	\*pphead = newnode; // 3、phead头指针 指向新节点
}

代码解释

SLTNode* newnode = BuyListNode(x):先增加一个新节点;
 
newnode->next = *pphead:对 pphead 解引用就是访问 pheadphead 为原来 头指针 指向的 头节点,那么把这个 地址 赋给新节点的 next 指针;
 
*pphead = newnode:最后再把 头指针 指向的 头节点 更新为 新节点,此时 新节点 就是链表的第一个节点啦!

3. 删除数据

链表的删除操作,同样也分为 4 种情况。

(1)在单链表 头部 删除数据;
 
(2)在单链表 尾部 删除数据;
 
(3)在单链表指定 pos 位置删除数据 x
 
(4)在单链表指定 pos 位置的 下一个 位置删除数据 x

🍑 尾删

尾部删除,是最简单的情况,但是我们要考虑几种特殊情况:

(1)如果链表为空,肯定不能删除
 
(2)如果链表只有一个节点,那么直接删除
 
(3)如果链表有两个以及两个以上的节点;

对于第 3 种情况,如果有两个以上的节点,那么只需把倒数第 2 个节点的 next 指针指向空即可。如下图所示👇
在这里插入图片描述
动图演示👇
在这里插入图片描述

📝 代码示例

void SListPopBack(SLTNode\*\* pphead) {
	// 判断链表为空的情况(粗暴的方式)
	assert(\*pphead != NULL);

	// 1.如果只有一个节点的情况
	if ((\*pphead)->next == NULL) {
		free(\*pphead); // 释放头节点
		\*pphead = NULL;
	}
	else // 2.如果有两个以及两个以上的节点
	{ 
		SLTNode\* prev = NULL;
		SLTNode\* tail = \*pphead;
		while (tail->next != NULL) {
			prev = tail; // 找到tial前面的那个节点
			tail = tail->next; // tial指向它的下一个节点
		}
		free(tail); // 释放最后一个节点的空间
		tail = NULL; // 把空间置为空指针
		prev->next = NULL; // 把tial上一个节点的next指向的元素置为NULL
	}
}

代码解释

我们直接看 else 语句的代码:
 
SLTNode* prev = NULL:首先定义一个指针变量 prev,用来指向倒数第 2 个节点;
 
SLTNode* tail = *pphead:然后再定义一个指针变量 tail,并把 头节点 的地址赋给它,让它用来找到 尾节点
 
while 循环部分:当 tail->next 不等于 空指针 时,说明 尾节点 还没有找到,那么就把当前 tail 的地址赋给 prev,然后 tail 指向下一个节点去,以此循环,直到 tail->next 等于 空指针,然后跳出循环;
 
最后释放掉 tail 的空间,然后把 prev->next 置为 空指针

🍑 头删

尾部删除,也要考虑几种特殊情况:

(1)当链表为空时候,没有可以删除的元素,那么就终止程序;
 
(2)如果链表只有一个节点,那么直接删除;
 
(3)如果链表有两个以及两个以上的节点。

对于第 3 种情况,如果有两个以上的节点,直接把链表的头节点设为原先头节点的 next 指针即可。如下图所示👇
在这里插入图片描述

动图演示👇
在这里插入图片描述

📝 代码示例

void SListPopFront(SLTNode\*\* pphead) {
	assert(\*pphead != NULL); // 判断链表为空的情况(粗暴的方式)
	SLTNode\* temp = (\*pphead)->next;
	free(\*pphead);
	\*pphead = temp;
}

代码解释

定义一个指针变量 temp,把头节点的 next 指针指向的地址赋给它,此时 temp 就是指向第 2 个元素的;
 
然后释放掉 头节点 的空间,再把 temp 的内容交给 头指针

4. 单链表查找

在查找元素时,链表不像 顺序表 那样可以通过下标快速进⾏定位,只能从头节点开始向后⼀个⼀个节点逐⼀查找。

例如给出⼀个链表,需要查找从头节点开始的第 3 个节点(如下图所示👇)。
在这里插入图片描述

1 步,将查找的指针定位到头节点(如下图所示👇)。
在这里插入图片描述

2 步,根据头节点的 next 指针,定位到第 2 个节点(如下图所示👇)。
在这里插入图片描述

3 步,根据第 2 个节点的 next 指针,定位到第 3 个节点,查找完毕(如下图所示👇)。
在这里插入图片描述

动图演示👇
在这里插入图片描述

📝 代码示例

SLTNode\* SListFind(SLTNode\* phead, SLTDateType x) {
	SLTNode\* cur = phead;
	while (cur) {
		if (cur->data == x) {
			return cur;
		}
		else {
			cur = cur->next;
		}
	}
	return NULL; // 没找到
}

单链表中的数据只能按顺序进⾏访问,最坏的时间复杂度是

O

(

n

)

O(n)

O(n) 。

5. 单链表指定位置操作数据

在插入数据和删除数据里面,分别还有两种方法没有讲到,那就是:

(1)指定 pos 位置插入和删除;
 
(2)指定 pos 位置的后面插入和删除;

那么为什么到现在才讲呢?那是因为 单链表 不同于 顺序表,这里的 pos 位置,是通过上面的 查找 得来的!

🍑 在 pos 位置的前面插入数据

在单链表指定 pos 位置插入数据 x,实际上数据 x 会被插入到 pos前面 位置插入数据,同样也要考虑几种特殊情况:

(1)如果链表为空,或者只有一个节点,那么就和头插一样,直接插入;
 
(2)如果链表有两个以上的节点;

对于第 2 种情况,如果链表有两个以上的节点,那么可以分为两个步骤。

1 步,新节点的 next 指针,指向 插⼊位置的节点

2 步,插⼊位置前置节点next 指针,指向新节点(如下图所示👇)。
在这里插入图片描述

动图演示👇
在这里插入图片描述

注意:首先需要调用 查找函数 找到 pos 位置的地址,然后才能进行插入;
 
另外只要内存空间允许,能够插⼊链表的元素是⽆穷⽆尽的,不需要像 顺序表 那样考虑扩容的问题。

📝 代码示例

// 在pos前面的位置插入(pos是有find查找来的)
void SListInsert(SLTNode\*\* pphead, SLTNode\* pos, SLTDateType x) {
	assert(pphead);
	assert(pos);
	// 1. pos是第一个节点,那么就是头插
	if (\*pphead == pos) {
		SListPushFront(pphead, x);
	}
	else { // 2. pos不是第一个节点
		// 找到pos的前一个位置
		SLTNode\* posPrev = \*pphead;
		while (posPrev->next != pos) {
			posPrev = posPrev->next;
		}
		SLTNode\* newnode = BuyListNode(x);
		posPrev->next = newnode;
		newnode->next = pos;
	}
}

🍑 删除在 pos 位置的数据

直接删除 pos 位置的 当前 数据 x,同样也要考虑几种特殊情况:

(1)如果链表为空,或者只有一个节点,那么就和头删一样,直接删除;
 
(2)如果链表有两个以上的节点;
 
注意:这里就是删除 pos 位置指向的数据,不是删除前面的节点,也不是删除后面的节点

对于第 2 种情况,如果链表有两个以上的节点,同样很简单,把要删除节点的前置节点的 next 指针,指向要删除元素的下⼀个节点即可(如下图所示👇)。
在这里插入图片描述

动图演示👇
在这里插入图片描述

📝 代码示例

void SListErase(SLTNode\*\* pphead, SLTNode\* pos) {
	assert(pphead);
	assert(pos);
	// 如果pos是头节点,那么用头删除
	if (\*pphead == pos) {
		SListPopFront(pphead);
	}
	else { // 如果pos在中间或者尾节点,那么就走else
		SLTNode\* posPrev = \*pphead;
		while (posPrev->next != pos) {
			posPrev = posPrev->next;
		}
		posPrev->next = pos->next;
		free(pos);
		pos = NULL;
	}
}

6. 单链表指定位置的后面操作数据

在单链表指定的 pos 位置的后面 插入数据删除数据

🍑 在 pos 位置的后面插入数据

这里与在 pos 位置的前面插入数据不同的是,这次插入数据,是直接插到 pos 的后面位置的,这里不需要考虑什么特殊情况;

1 步,定义一个指针变量 posNext,用于存放 pos 位置的 next 指针地址

2 步,把新节点 newnode 的地址赋给 pos 位置的 next 指针,然后把 posNext 的地址赋给新节点 newnode 的指针 next(如下图所示👇)。
在这里插入图片描述

📝 代码示例

void SListInsertAfter(SLTNode\* pos, SLTDateType x) {
	assert(pos);
	SLTNode\* posNext = pos->next;
	SLTNode\* newnode = BuyListNode(x); 
	pos->next = newnode;
	newnode->next = posNext;
}

🍑 删除在 pos 位置后面的数据

这个也很简单,只需要考虑 pos 是否存在,如果 pos 为空,肯定不能删除;

如果 pos 不为空,正常输出就好如下图所示👇
在这里插入图片描述

📝 代码示例

void SListEraseAfter(SLTNode\* pos) {
	assert(pos);
	SLTNode\* posNext = pos->next;
	if (posNext != NULL) {
		pos->next = posNext->next;
		free(posNext);
		posNext = NULL;
	}
}

7. 单链表打印

单链表的打印很简单,直接用 循环 依次打印 单链表 内的元素个数就好了。

📝 代码示例

void SListPrint(SLTNode\* phead) {
	SLTNode\* cur = phead;
	while (cur != NULL) {
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}

8. 单链表销毁

顺序表 一样,都是使用动态内存开辟的空间,用完以后,需要释放

📝 代码示例

// 销毁链表
void SListDestroy(SLTNode\*\* pphead) {
	assert(pphead);
	SLTNode\* cur = \*pphead;
	while (cur != NULL) { // 当前的节点不等于空指针
		SLTNode\* curNext = cur->next; // 保存 当前节点的第一个地址
		free(cur); // 把当前节点置为空指针
		cur = curNext; 
	}
	\*pphead = NULL; // 最后释放头节点
}

9. 总结

总结:

1、单链表结构,适合头插头删
 
2、尾部或者中间某个位置的插入和删除不适合
 
3、如果要使用链表单独存储数据,那么双向链表更适合

img
img
img

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

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

需要这份系统化资料的朋友,可以戳这里获取

curNext = cur->next; // 保存 当前节点的第一个地址
free(cur); // 把当前节点置为空指针
cur = curNext;
}
*pphead = NULL; // 最后释放头节点
}


## 9. 总结


**总结:**



> 
> 1、单链表结构,适合头插头删  
>     
>  2、尾部或者中间某个位置的插入和删除不适合  
>     
>  3、如果要使用链表单独存储数据,那么双向链表更适合
> 
> 
> 



[外链图片转存中...(img-BamRnYxd-1715590735290)]
[外链图片转存中...(img-NcCzlFZG-1715590735291)]
[外链图片转存中...(img-0O1l4GAo-1715590735291)]

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

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

**[需要这份系统化资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618545628)**

  • 24
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: C/C++ 是应用广泛的编程语言,其在数据结构应用方面也十分重要。面试中相关的 C/C++ 数据结构问题主要围绕数组、链表、叉树和图等方面。以下是一些常见问题及其解答: 1. 如何反转一个单向链表? 答:可以使用三个指针来实现:cur 代表当前节点,pre 代表上一个节点,next 代表下一个节点。每次遍历时,将 cur 的 next 指向 pre,然后将三个指针分别向后移动即可。 2. 如何判断两个链表是否相交,并找出相交的点? 答:可以分别遍历两个链表,得到各自的长度。然后让长的链表先走 n 步,使得两个链表剩余的长度相等。接下来同时遍历两个链表,比较节点是否相同即可找出相交的点。 3. 如何判断一个叉树是否为平衡叉树? 答:可以计算每个节点的左右子树深度差,如果任何一个节点的深度差大于1,则此树不是平衡叉树。可以使用递归实现,每次计算当前节点的深度,然后递归判断其左右子树是否平衡。 4. 如何实现图的深度优先搜索(DFS)和广度优先搜索(BFS)算法? 答:DFS 可以使用递归实现。从某个节点开始,逐个访问其未被访问的邻接节点,并将其标记为已访问。然后对每个未被访问的邻接节点递归调用 DFS 函数。BFS 可以使用队列实现。从某个节点开始,将其加入队列,并标记为已访问。然后从队列中弹出节点,并访问其所有未被访问的邻接节点,并将其加入队列中。重复此过程直到队列为空。 以上是一些常见的 C/C++ 数据结构面试问题及其解答。在面试中,除了掌握相关算法和数据结构知识外,还需多做练习和积累经验,才能更好地应对各种面试问题。 ### 回答2: C语言是一种用于编写系统级程序的高级编程语言,具有简单、高效、灵活等特点,是许多操作系统、编译器等软件的首选语言,也是许多企业在进行面试时重点考察的技能。在C/C++数据结构面试题中,经常会涉及到各种数据结构相关的算法和应用,测试面试者的算法思维能力和实现能力。 其中,常见的数据结构包括链表、栈和队列、叉树、搜索树、哈希表等。在面试时,会常常涉及代码设计和实现,比如实现链表的插入、删除、查找操作,实现叉树的遍历、查找操作等。 此外,在数据结构面试中,还经常涉及排序和查找算法,如冒泡排序、快速排序、归并排序、分查找、哈希查找等。同时,面试者还需要解决一些较为复杂的算法问题,如图的最短路径问题,最小生成树问题等。 总之,C/C++数据结构面试题涵盖了运用数据结构的各种算法和实现方法,需要面试者具备扎实的编程基础和算法思维能力。在备战面试时,可以多做练习,熟悉常用的数据结构和算法,提高理解和实现能力,从而更好地应对面试挑战。 ### 回答3: 面试过程中常见的C/C++数据结构面试题有很多。以下就介绍几个常见的题目并给出解答。 1. 求两个有序数组的中位数 题目描述:给定两个升序排列的整形数组,长度分别为m和n。实现一个函数,找出它们合并后的中位数。时间复杂度为log(m+n)。 解答:这个问题可以使用分法求解。首先,我们可以在两个数组中分别选出所谓的中间位置,即(i+j)/2和(k+l+1)/2,其中i和j分别是数组A的起始和结束位置,k和l分别是数组B的起始和结束位置。判断A[i+(j-i)/2]和B[k+(l-k)/2]的大小,如果A的中间元素小于B的中间元素,则中位数必定出现在A的右半部分以及B的左半部分;反之,则必定出现在A的左半部分以及B的右半部分。以此类推,每一次都可以删去A或B的一半,从而达到对数级别的时间复杂度。 2. 堆排序 题目描述:对一个长度为n的数组进行排序,时间复杂度为O(nlogn)。 解答:堆排序是一种常用的排序算法,在面试中也经常被考察。堆排序的具体过程是首先将数组构建成一个最大堆或最小堆,然后不断将堆顶元素与最后一个元素交换,并将最后一个元素从堆中剔除。这样,每次剔除后,堆都会重新调整,使得剩下的元素仍然保持堆的性质,直到堆中只剩下一个元素为止。 3. 链表反转 题目描述:反转一个单向链表,例如给定一个链表: 1->2->3->4->5, 反转后的链表为: 5->4->3->2->1。 解答:链表反转题目也是非常常见,其思路也比较简单。遍历链表,将当前节点的next指针指向前一个节点,同时记录当前节点和前一个节点,直至遍历到链表末尾。 以上这三个问题分别从分法、堆排序和链表三个方面介绍了常见的C/C++数据结构面试题,希望能帮助面试者更好地准备面试

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值