单链表——OJ题(一)

fe594ea5bf754ddbb223a54d8fb1e7bc.gif

目录

​一.前言

二.移除链表元素

三.返回链表中间节点

四.链表中倒数第K个节点

五.合并两个有序链表

六.反转链表

七.链表分割

八.链表的回文结构

九.相交链表

十.环形链表

十一.环形链表(二)

​六.结语


8fb442646f144d8daecdd2b61ec78ecd.png一.前言

本文主要对平时的链表OJ进行解析,帮助大家更加深入理解关于链表的性质特点。码字不易,希望大家多多支持我呀!(三连+关注,你是我滴神!)

二.移除链表元素

链接:203.移除链表元素

第一种思路:遍历删除

遍历变量cur:用于查找符合val的节点。再添加一个前置变量,用于连接删除过后的节点。

但其实这样子演示还是有弊端存在的~

当开头就出现符合val的节点,那么这两个指针变量又该如何指向呢?

我们可以把前面符合的都先删掉,最后再让head重新指向,另外两个变量也重新指向。

情况分析完毕,现在开始代码部分:

struct ListNode* removeElements(struct ListNode* head, int val) 
{
    struct ListNode* pre = NULL, * cur = head;
    while (cur)
    {
        if (cur->val == val)//开始分析情况,如果找到就删除
        {
            //开始删除
            if (cur == head)//刚好要头删
            {
                head = cur->next;
                free(cur);
               
                cur = head;//cur需要重新用head赋值,以便遍历
            }
            else//中间部分删除。意味着有pre变量了
            {
                free(cur);
                pre->next = cur->next;
                cur = pre->next;//因为free后cur没指向了,需要重新赋值
            }
        }
        else//找不到就让cur往下走,随便标记pre
        {
            pre = cur;
            cur = cur->next;
        }
    }
    return head;
}

其实本质就是头删与中间删而已,只不过我们需要为其添加特定条件来应对各种情况~

第二种思路:遍历原链表,把不是val的节点尾插到新链表

需要用next变量来保存cur的下一个节点,这样方便cur指向。(当然不用next变量也可以,因为我们并没有改变cur的下一个变量)

在新链表中创造一个节点tail用来尾插。

代码部分:

struct ListNode* removeElements(struct ListNode* head, int val)
{
    struct ListNode* cur = head;
    struct ListNode* newhead = NULL, * tail = NULL;
    while (cur)
    {
        if (cur->val == val)//当遇到val时,消除再重新指向
        {
            struct ListNode* del = cur;
            cur = cur->next;
            free(del);
        }
        else//没遇到val时,移动至新链表进行尾插
        {
            //当新链表为空时
            if (newhead == NULL)
            {
                newhead = tail = cur;
            }
            else//当新链表不为空时
            {
                tail->next = cur;
                tail = tail->next;
                //tail->next = NULL;//尾插后记得置空//但是注意,不要在这里置空 
            }
            cur = cur->next;
        }
        
    }
    if (tail)
    {
        tail->next = NULL;
    }
    return newhead;
}

三.返回链表中间节点

链接:876.返回链表中间节点

常规思路:

遍历一遍算出链表长度,再遍历一遍找到中间节点。

第二种思路:快慢指针

一开始两指针起点一致,然后遍历的时候让慢指针(slow)走一步,快指针(fast)走两步

快指针的速度是慢指针的一倍,那么当快指针指向结尾的时候,慢指针的指向就是中间节点了。

奇数个节点和偶数个节点的情况可能会不一样,所以我们还需要再来分析一遍。

对于奇数个而言是走到尾节点,对于偶数个而言是走向空。

struct ListNode* middleNode(struct ListNode* head) 
{
	struct ListNode* fast = head, * slow = head;
	while (fast && fast->next)//偶数个时fast指向空结束,奇数时fast指向尾节点结束
	{
		slow = slow->next;
		fast = fast->next->next;
	}
	return slow;
}

四.链表中倒数第K个节点

链接:链表中倒数第K个节点

这里我们还是可以使用多次遍历的方式来找到目标节点,所以我们额外添加一个条件:只能遍历一遍,那又要怎么做到呢?

还是老规矩,采用快慢指针的方法,只不过步数需要调整。

比如我们要找倒数第3个节点,我们先让fast走3步。

然后再让2个节点同时走,走到fast为空时结束。从空开始算起。

还有另外一种方式,就是我们可以先走k-1步。

走到尾节点结束,从尾节点算起。

struct ListNode* FindKthToTail(struct ListNode* head, int k)
{
	struct ListNode* fast = head, * slow = head;
	while (k--)
	{
//也要考虑例如走倒数第1000个节点这种极端情况,一旦越界立马返回
		if (fast==NULL)
		{
			return NULL;
		}
		else
		{
			fast = fast->next;
		}
	}
	while (fast)
	{
		fast = fast->next;
		slow = slow->next;
	}
	return slow;
}

五.合并两个有序链表

链接:21.合并两个有序链表

一般思路:构建一个新的链表,然后在两个原链表之间取小的尾插。 (其实这个跟之前的合并数组想法很像)

持续到其中一个链表头节点指向空为止~

代码部分:

 struct ListNode {
    int val;
    struct ListNode* next;
    
};

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) 
{
    if (list1 == NULL)
    {
        return list2;
    }
    if (list2 == NULL)
    {
        return list1;
    }

    struct ListNode* tail = NULL, * head = NULL;
    while (list1 && list2)
    {
        if (list1->val > list2->val)
        {
            if (head == NULL)//当新链表为空时
            {
                head = list2;
                
                tail = list2;
            }
            else//当新链表不为空时
            {
                tail->next = list2;
                
                tail = tail->next;
            }
            list2 = list2->next;
        }
        else
        {
            if (head == NULL)//当新链表为空时
            {
                head = list1;
               
                tail = list1;
            }
            else//当新链表不为空时
            {
                tail->next = list1;
               
                tail = tail->next;
            }
            list1 = list1->next;
        }

    }
    if (list1)
    {
        tail->next = list1;
    }
    if (list2)
    {
        tail->next = list2;
    }
    return head;

 另一种思路:使用带头哨兵位

无带头节点(哨兵位)时还得判断是不是第一次插入,而有了带头节点后就减少了一步判断。

当单链表改成哨兵位时,还可以不用二级指针。

运行代码:

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2)
{
    if (list1 == NULL)
    {
        return list2;
    }
    if (list2 == NULL)
    {
        return list1;
    }
    struct ListNode* tail = NULL, * head = NULL;
    tail = head = (struct ListNode*)malloc(sizeof(struct ListNode));//创造哨兵位
    while (list1 && list2)
    {
        if (list1->val > list2->val)
        {
                tail->next = list2;
                tail = tail->next;
                list2 = list2->next;
        }
        else
        {
                tail->next = list1;
                tail = tail->next;
                list1 = list1->next;
        }

    }
    if (list1)
    {
        tail->next = list1;
    }
    if (list2)
    {
        tail->next = list2;
    }
    //返回的时候删除掉哨兵位
    struct ListNode* del = head;
    head = head->next;
    free(del);
    return head;
}

六.反转链表

链接:206.反转链表

第一种思路:三指针

双指针还不能够实现反转。当n2指向n1时你就找不到下一个了。前两个是反转,后面是为了寻找下一个。

反转结束后再整体往后移动继续反转。

当n2等于空时结束。

但是这样是会有问题的,在我们执行最后一步时,n2已经是执行空了,所以不能让n3执行空的下一位,这样是错误的。

当解决这个问题后还有一个问题,我们没有考虑过链表为空的情况,所以还是跟上面一样的错误。所以很麻烦。

第二种思路:把节点拿下来头插

在第一个节点取下来头插指向newhead后,让newhead指向它,cur继续指向第二节点。

以此类推,直到cur指向空时停止。

运行代码:

struct ListNode* reverseList(struct ListNode* head) 
{
	struct ListNode* newnode = NULL;
	struct ListNode* cur = head;
	while (cur)
	{
		struct ListNode* next = cur->next;

		//进行头插
		cur->next = newnode;
		newnode = cur;
		cur = next;
	}
	return newnode;

}

七.链表分割

链接:CM11.链表分割

本题难点在于不能改变原有顺序(即相对顺序),所以我们开始的思路是再弄两条新链表。

做好尾插后再把两条链表链接起来,最后返回头节点即可。但本题坑点很多,其中不带哨兵位的话会比带哨兵位难很多。

当我们要链接两条链表的时候,要作出很多的判断~

  • 当lesstail为空的时候,就不能用链接了,而是直接返回greaterhead.
  • 当lesstail不为空时,得确保lesshead是头
  • 等等

当我们设置哨兵位的时候,就不用考虑哪个链表是否为空的情况,直接链接即可。

最终指向如上图所示,最后我们只需要消除哨兵位即可。

代码部分:

class Partition 
{
public:
    ListNode* partition(ListNode* head, int x)
    {
        struct ListNode* ghead, * gtail, * lhead, ltail;
        ghead = gtail = (struct ListNode*)malloc(sizeof(struct ListNode));
        lhead = ltail = (struct ListNode*)malloc(sizeof(struct ListNode));
        struct ListNode* cur = head;
        while (cur)//拆分链表
        {
            if (cur->val < x)
            {
                ltail->next = cur;
                ltail = ltail->next;
            }
            else
            {
                gtail->next = cur;
                gtail = gtail->next;
            }
            cur = cur->next;
        }
        //链接新链表
        ltail->next = ghead->next;
        struct ListNode* Head = lhead->next;

        free(ghead);
        free(lhead);
        return Head;



    }
};

这样还不够完整,还会出现错误。 

最后补充一个小细节,当我们的cur指向5并成功尾插的时候,按理来说5后面应该指向空,因为5后序的数据都不符合第二链表条件,但我们会发现5还会指向1造成了循环。所以我们要及时置空。

完整代码:

class Partition 
{
public:
    ListNode* partition(ListNode* head, int x)
    {
        struct ListNode* ghead, * gtail, * lhead, ltail;
        ghead = gtail = (struct ListNode*)malloc(sizeof(struct ListNode));
        lhead = ltail = (struct ListNode*)malloc(sizeof(struct ListNode));
        struct ListNode* cur = head;
        while (cur)//拆分链表
        {
            if (cur->val < x)
            {
                ltail->next = cur;
                ltail = ltail->next;
            }
            else
            {
                gtail->next = cur;
                gtail = gtail->next;
            }
            cur = cur->next;
        }
        gtail->next = NULL;//及时置空
        //链接新链表
        ltail->next = ghead->next;
        struct ListNode* Head = lhead->next;

        free(ghead);
        free(lhead);
        return Head;



    }
};

八.链表的回文结构

链接:OR36.链表的回文结构

回文结构也分奇数回文与偶数回文:

想法:找到中间节点,再逆置中间节点后面的节点。最后再来与头节点与指向中间节点的数据一一对比。

我们可以套用前面已经写好的寻找中间节点代码与反转链表来实现找到中间节点与逆置中间节点后面的节点,最后再来处理对比。

逆置完后链表会变成这样,跟前面链表分割一个性质,如果没有刻意去切断链接,那就会藕断丝连。不过这种情况问题不大,应该这两个指针只要任意一个指向空就结束,例如在上图中1与1相等,指向下一位,2与2相等,一个指向3,另一个也指向3还是相等,最后一个指向空结束。

运行代码:

class PalindromeList {
public:

	struct ListNode* middleNode(struct ListNode* head)
	{
		struct ListNode* fast = head, * slow = head;
		while (fast && fast->next)//偶数个时fast指向空结束,奇数时fast指向尾节点结束
		{
			slow = slow->next;
			fast = fast->next->next;
		}
		return slow;
	}

	struct ListNode* reverseList(struct ListNode* head)
	{
		struct ListNode* newnode = NULL;
		struct ListNode* cur = head;
		while (cur)
		{
			struct ListNode* next = cur->next;

			//进行头插
			cur->next = newnode;
			newnode = cur;
			cur = next;
		}
		return newnode;

	}

    bool chkPalindrome(ListNode* head) {
		struct ListNode* mid = middleNode(head);
		struct ListNode* rmid = reverseList(mid);

		while (head && rmid)//一方指向空就停止

		{
			if (head->val != rmid->val)
			{
				return false;
			}
			else
			{
				head = head->next;
				rmid = rmid->next;
			}
		}
		return true;
    }
};

九.相交链表

链接:160.相交链表

经典误区:

接下来我们应该如何来判断相交呢?——寻找尾节点。记住!比的是地址而不是数值,如果本来就相交,那么一定是共用一个地址的!数值相等的话并不意味着两个节点是同一个地址~

尾节点相等就说明相交。

真正麻烦的是如何找到相交的点~

一种思路是:暴力遍历,依次拿a的节点跟b所有的节点进行地址对比。时间复杂度O(N^2)

优化思路:在寻找交点的时候,想办法让两链表长度一致开始遍历对比。时间复杂度O(N)

运行代码:

 struct ListNode* getIntersectionNode(struct ListNode* headA, struct ListNode* headB)
 {
	 struct ListNode* curA = headA;
	 struct ListNode* curB = headB;
	 //题目说明链表不为空,而且设置为1也没事,反正我们只需要差值
	 int lenA = 1;
	 int lenB = 1;
	 //寻找尾节点看是否相交
	 while (curA->next)
	 {
		 lenA++;
		 curA = curA->next;
	 }
	 while (curB->next)
	 {
		 lenB++;
		 curB = curB->next; 
	 }
	 if (curA!= curB)
	 {
		 return NULL;
	 }
	 //开始寻找第一个相交点
	 //先算绝对值

	 int gap = abs(lenA - lenB);
	 struct ListNode* lonlist = headA;//假设A是长的
	 struct ListNode* shortlist = headB;//假设B是短的
	 if (lenA < lenB)
	 {
		 lonlist = headB;
		 shortlist = headA;
	 }
	 //让长的提前走gap步

	 while (gap--)
	 {
		 lonlist = lonlist->next;
	 }

	 //长度一致时开始一一对比
	 while (lonlist != shortlist)
	 {
		 lonlist = lonlist->next;
		 shortlist = shortlist->next;
	 }
	 return lonlist;

}

十.环形链表

链接:141.环形链表

本题难点在于不清楚哪个节点是环内的,哪个节点是环外的。

所以我们只能看是否会相遇,如果不是环是不会相遇的,而且fast只会走向尾节点。

运行代码:

 bool hasCycle(struct ListNode* head) 
 {
	 struct ListNode* fast = head;
	 struct ListNode* slow = head;

	 //很关键的一步之所以设置两个条件,就是为了预防链表不为环的情况
	 //当链表奇数个节点时,用fast-next来跳出循环
	 //当链表偶数个节点时,用fast来跳出循环
	 while (fast && fast->next)
	 {
		 fast = fast->next->next;
		 slow = slow->next;
		 //如果是带环链表,那么迟早会相遇
		 if (fast == slow)
		 {
			 return true;
		 }
	 }
	 //如果是不带环链表,那么fast或fast->next会到空节点,所以直接false
	 return false;
 }

  • 思考:
  • 如果slow走1步,fast走2步,一定能追上吗?会不会错过了。
  • 如果slow走1步,fast走n步(n>=3),一定能追上吗?会不会错过?

死循环

所以用快慢指针2步走的方法是最稳健的。多于3步的都是要考虑很多情况的。

十一.环形链表(二)

链接:142.环形链表(二)

思路一:

先假设它们在不断地追击

这里我们是否需要判断slow会多走一圈C(圈长)才会和fast相遇呢?——不需要,因为我们在分析走2的时候已经得出结论:它们不会错过。

如果是这样想那结果是经不起推敲的,漏洞百出。

当环很短,L很长的时候,那么L=C-X的公式是不成立的。

我们前面做了这么多的测试就是为了推出这个公式。

struct ListNode* detectCycle(struct ListNode* head) 
{
	struct ListNode* slow, * fast;
	slow = fast = head;
	while (fast && fast->next)
	{
		slow = slow->next;
		fast = fast->next->next;

		if (slow == fast)
		{
			struct ListNode* meet = slow;
			while (head != meet)
			{
				head = head->next;
				meet = meet->next;
			}
			return meet;
		}
	}
	return NULL;
}

因为在最后的meet和head相遇的原理就是 L = n*C-x,正是因为我们有这个公式,在后续才可以用whiel循环让head与meet重合找到第一个环入口点。

首先slow与fast相遇时slow从入口点到相遇点已经走过了x的距离,所以从相遇点开始走到入口点就需要C-X的距离,另外还得考虑到无法相遇时要多走的(n-1)C圈,但归根到底,二者相遇的地点一定是入口点,所以我们最后的总结就是让head等于meet即可,因为它们相当于 L(head) = (n*C-x)(meet)

方法二:

把当前meet结点搞成尾,让newnode新结点变成头。

复制相交链表接口

一个从newnode开始走,一个从head开始走,找交点。

因为通过条件slow==fast可以得出只有环才可以相遇,那么接下来只需要找相交结点即可.

那我们又为什么要进行断尾呢?因为套用相交链表的前提是要用到尾部结点的,如果不主动断尾,就会陷入死循环。

struct ListNode* detectCycle(struct ListNode* head)
{
	struct ListNode* slow, * fast;
	slow = fast = head;
	while (fast && fast->next)
	{
		slow = slow->next;
		fast = fast->next->next;

		if (slow == fast)
		{
			struct ListNode* meet = slow;
			struct ListNode* newnode = meet->next;
			meet->next = NULL;
			return getIntersectionNode(newnode, head);
		}
	}
	return NULL;
}

4b12323f94834afd9ec146a3c10df229.jpeg六.结语

如果大家能够把这些经典的链表OJ题融会贯通,那你接下来关于链表的题型大部分都会迎刃而解的啦~最后感谢大家的观看,友友们能够学习到新的知识是额滴荣幸,期待我们下次相见~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/174780.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Vue2+Vue3

文章目录 第 1 章&#xff1a;Vue 核心1、 Vue 简介1.官网2.介绍与描述3. Vue 的特点4. 与其它 JS 框架的关联5. Vue 周边库 2、初始Vue3、模板语法1、Vue模板语法有2大类:2、插值语法和指令语法 4、数据绑定1. 单向数据绑定2. 双向数据绑定 5、el与data的两种写法1.e1有2种写法…

专访特斯拉工程师杨硕:跟着机器人上天入地、探索地外行星丨智源独家

导读 十几岁时&#xff0c;他痴迷《终结者》&#xff0c;曾在百科全书中窥见卡内基梅隆大学机械臂的介绍&#xff0c;从而得知了研究机器人「圣地」的存在。 在CMU&#xff0c;他深耕足式机器人感知定位算法&#xff0c;期待未来涉足太空&#xff0c;走上火星。 在大疆&#xf…

水果音乐制作软件FL Studio21.2中文版新功能介绍

FL Studio21.2中文版&#xff0c;一般又称水果音乐制作软件。 FL Studio 21.2简称FL&#xff0c;全称FruityLoopsStudio&#xff0c;因此国人习惯叫它"水果"。它让你的计算机就像是全功能的录音室&#xff0c;大混音盘&#xff0c;非常先进的制作工具&#xff0c;让…

【C语言】数据结构——栈和队列实例探究

&#x1f497;个人主页&#x1f497; ⭐个人专栏——数据结构学习⭐ &#x1f4ab;点击关注&#x1f929;一起学习C语言&#x1f4af;&#x1f4ab; 目录 导读&#xff1a;一、 栈1. 栈的概念及结构2. 栈的实现3. 实现代码3.1 定义结构体3.2 初始化栈3.3 销毁栈3.4 入栈3.5 出栈…

java io流中为什么使用缓冲流就能加快文件读写速度

FileInputStream的read方法底层确实是通过调用JDK层面的read方法&#xff0c;并且这个JDK层面的read方法底层是使用C语言编写的&#xff0c;以实现高效的文件读取功能。但是它会涉及多次内核态与操作系统交互。当我们使用FileInputStream的read方法读取文件时&#xff0c;首先会…

微服务 Spring Cloud 8,开源RPC框架如何选型?

目录 一、开源RPC框架有哪些&#xff1f;1、跟语言平台绑定的开源RPC框架2、跨语言平台的开源RPC框架 二、跟语言平台绑定的开源RPC框架 -- Dubbo1、Dubbo的架构主要包含四个角色2、Dubbo的调用框架是如何实现的&#xff1f; 三、如何选择&#xff1f;四、跨语言平台的开源RPC框…

继承【C++】

继承【C】 一.什么是继承&#xff1f;二. 继承的方式与权限三. 继承中的成员3.0 基类和派生类中的重名成员i. 限定符ii. 隐藏 3.1 继承与默认成员函数i. 默认构造ii. 析构函数 3.2 继承与友元函数3.3 继承与静态成员变量 四. 基类和派生类的赋值五. 多继承5.1 菱形继承5.2 菱形…

CFCA证书——基于SM2/3算法的安全信任

在中国金融认证中心&#xff08;CFCA&#xff09;发行的证书中&#xff0c;采用了最新的国密SM2/3算法来提供更高的安全保障。这一创新举措进一步增强了我国网络安全能力&#xff0c;并为用户提供了一种更可靠、更安全的选择。 SM2/3算法是中国自主研发的非对称加密算法&#…

瑞格心理咨询系统设置多个管理员的操作方法

使用瑞格心理咨询系统&#xff0c;需要设置多个admin权限的管理员账号来管理&#xff0c;咨询厂家答复只能有1个管理员&#xff0c;个人觉得不可能&#xff0c;于是开始折腾。 解决办法&#xff1a; 在没有数据字典的情况下&#xff0c; 通过遍历数据库&#xff0c;发现用户信…

python趣味编程-5分钟实现一个石头剪刀布游戏(含源码、步骤讲解)

Python 中的石头剪刀布代码是 使用Tkinter和图形用户界面(GUI)设计的。 Python 石头剪刀布游戏是使用Python 编程语言开发的简单桌面应用程序。 项目系统文件包含资源文件和Python脚本。游戏画面流畅,用户控制起来很容易。

大数据:SAS数据分析1,数据步,和过程步

大数据&#xff1a;SAS数据分析 2022找工作是学历、能力和运气的超强结合体&#xff0c;遇到寒冬&#xff0c;大厂不招人&#xff0c;可能很多算法学生都得去找开发&#xff0c;测开 测开的话&#xff0c;你就得学数据库&#xff0c;sql&#xff0c;oracle&#xff0c;尤其sql…

2023年DevOps国际峰会暨BizDevOps企业峰会(DOIS北京站)-核心PPT资料下载

一、峰会简介 在数字化转型的大背景下&#xff0c;企业选择实践 DevOps 来提升 IT 效能成为常态&#xff0c;BizDevOps 作为企业自身数字化变革的重要主题之一&#xff0c;需要全行业共同努力促进繁荣和发展。从 DevOps 到 BizDevOps&#xff0c;业务与技术如何融合&#xff1…

JAVAEE---多线程

内核 内核时操作系统的核心 操作系统有内核态和用户态&#xff0c;像我们平时所用到的qq音乐&#xff0c;微信等都属于用户态执行的程序。那么qq音乐播放音乐需要用到扬声器&#xff0c;扬声器的操作就是在内核空间进行操作&#xff0c;用户态不能对其进行操作。 操作系统 …

移远通信推出六款新型天线,为物联网客户带来更丰富的产品选择

近日&#xff0c;移远通信重磅推出六款新型天线&#xff0c;覆盖5G、非地面网络&#xff08;NTN&#xff09;等多种新技术&#xff0c;将为物联网终端等产品带来全新功能和更强大的连接性能。 移远通信COO张栋表示&#xff1a;“当前&#xff0c;物联网应用除了需要高性能的天线…

基于卷尾猴算法优化概率神经网络PNN的分类预测 - 附代码

基于卷尾猴算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于卷尾猴算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于卷尾猴优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要&#xff1a;针对PNN神经网络…

雷电模拟器报错:g_bGuestPoweroff.fastpipeapi. cpp_1153_1161

文章目录 一、报错详情&#xff1a;二、解决&#xff1a;【1】设置Windows功能【2】设置cmd&#xff08;管理员身份&#xff09;【3】重启电脑 三、windows10其中1809版本出现1153、1161&#xff0c;需要关闭内核隔离 一、报错详情&#xff1a; 二、解决&#xff1a; 【1】设置…

909-2014-T2

文章目录 1.原题2.算法思想3.关键代码4.完整代码5.运行结果 1.原题 二叉树采用二叉链表存储结构&#xff0c;设计算法&#xff0c;判断二叉树是否为满二叉树。叙述算法思想并给出算法实现。 2.算法思想 通过一次遍历&#xff0c;得到结点个数和树的高度。用结点个数和树的高…

什么是高防CDN?有什么优势?

德迅高防CDN技术概述 随着互联网的快速发展&#xff0c;网络安全问题越来越受到人们的关注。高防CDN(Content Delivery Network)作为一种有效的网络安全防御手段&#xff0c;在企业和个人网站中得到了广泛应用。本文将详细介绍高防CDN的技术原理、防御原理、优点和应用场景&am…

TransmittableThreadLocal - 线程池中也可以传递参数了

一、InheritableThreadLocal的不足 InheritableThreadLocal可以用于主子线程之间传递参数&#xff0c;但是它必须要求在主线程中手动创建的子线程才可以获取到主线程设置的参数&#xff0c;不能够通过线程池的方式调用。 但是现在我们实际的项目开发中&#xff0c;一般都是采…

用 HLS 实现 UART

用 HLS 实现 UART 介绍 UART 是一种旧的串行通信机制&#xff0c;但仍在很多平台中使用。它在 HDL 语言中的实现并不棘手&#xff0c;可以被视为本科生的作业。在这里&#xff0c;我将通过这个例子来展示在 HLS 中实现它是多么容易和有趣。 因此&#xff0c;从概念上讲&#xf…