扬帆数据结构算法之舟,启航C++探索征途——LeetCode深度磨砺:顺序表技术精进实践

在这里插入图片描述

在这里插入图片描述

人无完人,持之以恒,方能见真我!!!
共同进步!!

文章目录

  • 顺序表练习
    • 1.移除数组中指定的元素
      • 方法1(顺序表)
      • 方法2(双指针)
    • 2.删除有序数组中的重复项
      • 方法1(顺序表)
      • 方法2(双指针)
    • 3.双指针练习之合并两个有序数组
      • 方法1(直接排序)
      • 方法2(双指针)

顺序表练习

知识就是拿来用的,我们前面刚刚学习了顺序表,我们这就来学习使用它

那么是不是就说明我们以后要用数据结构做题就必须把它手写一遍吗?这是不需要的,后面我们会介绍C++,在C++中的STL中有现成的可以用,现在在我们的初阶数据结构的学习过程中,就复制我们写过的数据结构来做题

在做题过程中,我也会穿插时间复杂度和空间复杂度的介绍,也是对之前内容的一个复习

1.移除数组中指定的元素

题目链接:移除元素

方法1(顺序表)

在这里插入图片描述

根据图片我们可以了解题意,就是让我们删除nums数组中和val相等的值,然后返回删除后的数组的有效元素个数

方法1的思路就是直接使用我们前面写的顺序表,在顺序表中我们写了一个查找函数和一个删除指定位置元素的函数,将这两个函数结合就可以在顺序表中查找指定的要删除的元素的下标,然后返回给删除函数

我们要做这道题的第一步就是先把我们前面写的顺序表拷贝过来,但是你要是有时间的话也可以手写一遍,复习一遍加深记忆,在使用顺序表之前,我们可以先把初始化函数和销毁函数协商,避免忘记销毁顺序表

随后把数组中的内容尾插到我们的顺序表,我们才能使用顺序表对数据进行操作,如下:

int removeElement(int* nums, int numsSize, int val) 
{
    int i = 0;
    SL sl;
    SLInit(&sl);
    for(i = 0; i<numsSize; i++)
    {
        SLPushBack(&sl,nums[i]);
    }

    SLDestroy(&sl);
}

随后我们就可以根据我们写的查找函数SLFind 来设计一个循环,让它能够遍历数组的元素,然后每找到一个指定的val元素,就使用删除函数删除,这个循环的结束条件就是,SLFind返回-1,如果返回的不是-1,就继续查找

首先如果没有元素那么久不进入循环,如果不是就进入循环将对应的元素删除,是的话就结束循环,如下:

	while(!SLEmpty(&sl) && SLFind(&sl,val) != -1)
    {
        int ret = SLFind(&sl,val);
        SLErase(&sl,ret);
    }
typedef int type;

typedef struct SL
{
	type* arr;
	int size;
	int capacity;
}SL;


void SLInit(SL* ps);
void SLDestroy(SL* ps);

void SLPushBack(SL* ps, type x);

void SLPrint(SL* ps);

void SLPushFront(SL* ps, type x);

void SLPopBack(SL* ps);

void SLPopFront(SL* ps);

void SLInsert(SL* ps, int pos, type x);

int SLFind(SL* ps, type x);

void SLErase(SL* ps, int pos);


void SLInit(SL* ps)
{
	ps->arr = NULL;
	ps->size = ps->capacity = 0;
}

//销毁
void SLDestroy(SL* ps)
{
	if (ps->arr)
		free(ps->arr);
	ps->arr = NULL;
	ps->size = ps->capacity = 0;
}

//如果空间不够,进行扩容
void SLcheckCapacity(SL* ps)
{
	if (ps->capacity == ps->size)
	{
		ps->capacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
		type* tmp = (type*)realloc(ps->arr, ps->capacity * sizeof(type));
		if (tmp == NULL)
		{
			perror("realloc");
			exit(1);
		}
		ps->arr = tmp;
		tmp = NULL;
	}
}

//打印
void SLPrint(SL* ps)
{
	for (int i = 0; i < ps->size; i++)
	{
		printf("%d ", ps->arr[i]);
	}
	printf("\n");
}

//尾插
void SLPushBack(SL* ps, type x)
{
	assert(ps);
	SLcheckCapacity(ps);
	ps->arr[ps->size++] = x;
}

//头插
void SLPushFront(SL* ps,type x)
{
	assert(ps);
	SLcheckCapacity(ps);
	memcpy(ps->arr + 1, ps->arr, ps->size * sizeof(type));
	ps->arr[0] = x;
	ps->size++;
}

//尾删
void SLPopBack(SL* ps)
{
	assert(ps);
	assert(ps->size);
	ps->size--;
}

//头删
void SLPopFront(SL* ps)
{
	assert(ps);
	assert(ps->size);
	memmove(ps->arr, ps->arr + 1, --ps->size * sizeof(type));
}

//指定位置前插入数据
void SLInsert(SL* ps, int pos, type x)
{
	assert(ps);
	assert(pos >=0 && pos <= ps->size);
	memmove(ps->arr + pos + 1, ps->arr + pos, (ps->size - pos) * sizeof(type));
	ps->arr[pos] = x;
	ps->size++;
}

int SLFind(SL* ps, type x)
{
	assert(ps);
	assert(ps->size);
	for (int i = 0; i < ps->size; i++)
	{
		if (ps->arr[i] == x)
			return i;
	}
	return -1;
}

//删除指定位置的数据
void SLErase(SL* ps, int pos)
{
	assert(ps);
	assert(ps->size);
	assert(pos >= 0 && pos <= ps->size);
	memmove(ps->arr + pos, ps->arr + pos + 1, (ps->size - pos - 1) * sizeof(type));
	ps->size--;
}

bool SLEmpty(SL*ps)
{
    return ps->size == 0;
}

int removeElement(int* nums, int numsSize, int val) 
{
    //如果为空就返回0
    if(numsSize == 0) return 0;
 
    //初始化
    SL sl;
    SLInit(&sl);
    //将元素放入顺序表
    int i = 0;
    for( i = 0;i < numsSize;i++)
    {
        SLPushBack(&sl,nums[i]);
    }

    //查找元素+删除元素
    while(!SLEmpty(&sl) && SLFind(&sl,val) != -1)
    {
        int ret = SLFind(&sl,val);
        SLErase(&sl,ret);
    }
    //将删除后的元素放回nums数组
    for(i = 0;i<sl.size;i++)
    {
        nums[i] = sl.arr[i];
    }
    
    //保存元素个数,销毁函数后,并返回
    int k = sl.size;
    SLDestroy(&sl);
    return k;
}

在这里插入图片描述

可以看到我们Ctrl + c v 在加上写的代码已经达到了一百六十行,这样写不仅繁琐,而且拷贝的时候还容易出错

可以看到最后通过了,我们来分析一下代码的时间复杂度和空间复杂度,第一个和第二个for循环都是O(N),关键在于第二个while循环的时间复杂度

由于我们的查找方法和删除方法时间复杂度都是O(N),外层有一个while循环,所以时间复杂度就是O(N^2)

接着来看空间复杂度,我们为了在顺序表中操作数组的数据,所以把数组中的数组全部放入了顺序表,所以顺序表会额外开辟数组元素大小的空间,空间复杂度为O(N)

我们知道这个时间复杂度和空间复杂度都不是很好,所以我们还有另外一种优化的方法

方法2(双指针)

这里的双指针并不是创建两个指针变量,而是数组中的下标,因为数组中我们能够通过下标来访问元素,与指针相似,所以叫做双指针

具体方法就是创建两个整型变量src和dest,,src代表源,dest代表目的地

在双指针算法中,我们的思路很简单,就是首先让src和dest都指向数组的第一个元素,随后我们就看src位置上的值是否是题目给出的val,如果是我们就直接让src往后走一步

如果不是,我们就把src位置上的值赋给dest,随后让dest和src都往后走一步,接下来我们就演示一下第一个示例的推演,如图:

在这里插入图片描述

在这里插入图片描述

有了上面的分析,我们就可以有一些思路了,首先创建两个整型变量src和dest作为我们的双指针,初始情况下让它们都为0

随后创建一个循环,循环条件就是src < numsSize,因为src是数组中的下标,要保证它不能越界,接着我们就进行判断,如果src位置的值等于dest位置的值,那么直接让src++

否则的话就是不等于,那么把src位置的值赋值到dest位置,随后让它们都++,如下:

int removeElement(int* nums, int numsSize, int val) 
{
    //循环条件 src < numsSize
    int src = 0,dest = 0;
    while(src < numsSize)
    {
        if(nums[src] == val)
        {
            src++;
        }
        else
        {
            nums[dest++] = nums[src++];
        }
    }
    //循环结束,dest 就是指向最后一个元素的后一个,就是元素个数
    return dest;
}

在这里插入图片描述

最后我们提交一下就通过了,在双指针方法中,我们只有一个循环,时间复杂度为O(N),申请的空间也是常数个,所以时间复杂度为O(1),可以看到比使用顺序表的方法简便多了,时间复杂度和空间复杂度都优化了一个档次

这道题的双指针法的本质就是 探路法 ,碰到了指定的元素就不管,直接跳过,然后把不是指定的元素放在前面,简单地说就是一个在前面探路,一个在后面接收

2.删除有序数组中的重复项

题目链接:删除有序数组的重复项

方法1(顺序表)

既然我们的标题是顺序表练习,那我们第一种方法还是用顺序表来做

在这里插入图片描述

在这里插入图片描述

根据上面图片的解释,我们大致明白了题目的要求,就是数组是相对有序的,然后我们要删除那些重复项,让数组里面的元素变得唯一且有序,最后返回唯一的元素个数,至于其它位置的元素就不管了

首先还是把顺序表拷贝过来,以及初始化,销毁,这些都不要忘记,然后把数组数据尾插到顺序表中上面讲过了,接着我们就来讲这道题不同的地方

由于数组是相对有序的,所以相同的元素肯定是放在一起的,所以我们就可以写一个循环来判断前后两个元素是否相等,如果相等我们就删除当前的元素,每删除一个元素numsSize就减1,要特别注意的一个点是要保证下标的有效性,如下:

for (i = 0; i < numsSize; i++)
{
    if (i + 1 < numsSize && sl.arr[i] == sl.arr[i + 1])
    {
        SLErase(&sl, i);
        numsSize--;
    }
}

但是我们的代码通过不了这个测试用例

在这里插入图片描述

来调试一下看看问题出在哪里

在这里插入图片描述

这个时候我们发现了问题,当i = 1时,将下标为1的元素删除了,所有后面的其它元素都往前挪动了一步,导致现在下标为1和2的元素又相等了

但是本次循环结束了,下一次在循环中i要++变成了2,i+1变成了3,比较不了下标为1和2的元素了,也就导致了下标为1和2的元素相等没有处理,如果看不懂这里的分析也没有关系,自行调试一下就明白了

那么怎么解决呢?其实也很简单,问题就出在删除一个相同元素后,其余元素都往前挪动了,但是循环结束了,i要++,就不会再比较移动后的i下标位置的元素和i+1下标的元素了

所以我们为了能够让它删除元素后,多比较一次原本移动后的i下标位置的元素和i+1下标的元素,我们可以在删除一个元素后让i- -,这样处理后下次进入循环i的值就不会变化,代码如下:

for (i = 0; i < numsSize; i++)
{
    if (i + 1 < numsSize && sl.arr[i] == sl.arr[i + 1])
    {
        SLErase(&sl, i);
        numsSize--;
        i--;
    }
}

当完成上面的步骤之后我们再把顺序表中的数据拷贝回原数组中,此时的numsSize就是有效的元素个数

int removeDuplicates(int* nums, int numsSize)
{
    SL sl;
    int i = 0;
    SLInit(&sl);
    for (i = 0; i < numsSize; i++)
    {
        SLPushBack(&sl, nums[i]);
    }
    for (i = 0; i < numsSize; i++)
    {
        if (i + 1 < numsSize && sl.arr[i] == sl.arr[i + 1])
        {
            SLErase(&sl, i);
            numsSize--;
            i--;
        }
    }
    for (i = 0; i < numsSize; i++)
    {
        nums[i] = sl.arr[i];
    }
    SLDestroy(&sl);
    return numsSize;
}

在这里插入图片描述

最后我们提交一下就可以通过了,在这个方法中,我们的时间复杂度也是很高的,达到了O(N^2),并且由于使用顺序表拷贝了原数组中的内容,空间复杂度也不低,达到了O(N),所以我们接下来介绍一个更好的方法

方法2(双指针)

这道题还是可以使用我们的双指针法来做,上一题的本质就是探路法

这道题也可以借鉴这样的思想,我们题目要求去掉相对有序的数组中的元素,说明相等的元素都是挨在一起的,我们还是可以定义两个变量src和dest,src在前面探路,dest在后面接收

具体思路就是:初始情况下让dest指向第一个元素,而src指向第二个元素,因为如果它们都指向第一个元素,那么它们肯定就相等了,没有比较的必要,所以让src走到第二个元素的位置上

然后我们就比较src和dest位置上的元素是否相等,如果相等我们就让让src++,其它什么都不做,如果不相等就让dest++,然后把src位置的值赋值到dest位置上,然后再让src++,听着可能有点懵,我们来画个图理解一下,就知道这个思路了

具体的例子我们就参考题目给出的第一个示例,如下:

在这里插入图片描述

有了上图的解析,我们现在就可以直接来写代码了,首先创建一个while循环,
循环条件就是src < numsSize

然后如果src指向的元素等于dest指向的元素,就让src++,其它什么都不做,如果不相等,就让dest先++,再把src位置的元素赋值给dest位置的元素,最后让src++,如下:

while (src < numsSize)
{
    if (nums[src] == nums[dest])
    {
        src++;
    }
    else
    {
        dest++;
        nums[dest] = nums[src];
        src++;
    }
}

最后循环结束返回 dest + 1

int removeDuplicates(int* nums, int numsSize) 
{
    int src = 0, dest = 0;
    while(src < numsSize)
    {
       if(nums[src] == nums[dest])
          src++;
       else
          nums[++dest] = nums[src++];
    }
    return dest+1;    
}

在这里插入图片描述

最后也是成功提交了代码,我们来分析一下双指针法的时间复杂度和空间复杂度,我们只用了一个循环,时间复杂度为O(N),也没有额外申请空间,所以空间复杂度为O(1)

3.双指针练习之合并两个有序数组

题目链接:合并两个有序数组

在这里插入图片描述
在这里插入图片描述

方法1(直接排序)

先来解读一下题目,题目给了我们两个数组,其中第一个nums1 的大小是两个数组的和(m+n),因为我们要将nums2合并到nums1中去,要保证空间足够,所以题目才给出这样的条件

首先题目告诉我们两个数组都是非递减的数组,所以有两种可能,一是递增,二就是可能是重复数据
,现在题目要求我们将他俩合并成有序数组返回

第一个和方法就是,直接将nums2拷贝到nums1中,再对nums1进行排序,我们可以使用qsort函数实现排序

代码的实现过程就不再多讲了,思路很简单,这里我们直接给出完整代码:

#include <stdlib.h>
//这个函数的意思就是比较数组中的元素大小
int int_compare(const void* e1,const void* e2)
{
    return *(int*)e1 - *(int*)e2;//前-后<0就是升序
}

void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n) 
{
    //将nums2放进nums1,所以循环条件应该小于n
    for(int i = 0;i<n;i++)
    {
        nums1[i + m] = nums2[i];//这里是将nums2放在nums1的m位置之后
    }
    qsort(nums1,m + n,sizeof(int),int_compare);
}

在这里插入图片描述

可以看到代码逻辑还是相对简单的,并且效率也很高,因为这里的qsort使用的是快排,时间复杂度仅仅是O(NlogN),空间复杂度是O(1),但是我们下面还有一个办法使它更快一点,那就还是我们的双指针法

方法2(双指针)

其实我们这三道题都很类似,所以双指针很吃香,只不过在这道题中,我们有两个数组需要合并,我们还是可以试着用之前的思路来思考一下这个题,上面的题是一个数组,而这一道题是两个数组,那我就可以定义两个指针src1 和 src2

之前的思路就是,如果碰到相同的元素,或者题目给出的元素,我们就把他给跳过,使用后面的元素把它覆盖,那这道题就有一些不一样的地方

首先第一个思路就是src1指向nums1的第一个元素,src2指向nums2的第一个元素,由于最后的结果要保存在nums1中,所以我们把dest定义在nums1中,指向nums1的第一个位置

随后我们比较src1指向的元素和src2指向的元素哪个小,哪个小就把它放到dest的位置上,然后让dest++,然后src1++或者src2++,最后循环这个步骤

但是我们最后发现这个思路有点问题,问题出在nums1数组中,因为如果我们直接把数据放在dest,也就是从nums1数组的开头开始存放数据,会导致原本的nums1数组的数据被覆盖,我们举个例子,如图:

在这里插入图片描述

这个时候我们发现问题了,nums1中的数据会被覆盖,所以第一种思路并不可行,那么应该怎样改进,避免数据被覆盖呢?这个时候我们不放换一个思路,倒过来想,既然放小的不行,那咱们就放大的,也就是 从nums1的最后开始放数据,也就是让我们的dest指向nums1的最后一个位置

因为我们要保证nums1最后排序成相对递增,最后一个位置就要放最大值,我们可以让src1 和 src2分别指向nums1 和 nums2的最后一个元素,然后比较谁大谁就放在dest的位置

然后让dest–,以及src1- -或者src2- -,就这样一直找大放在dest的位置,循环条件就是要让src1和src2都大于0,因为都是数组下标,都不能越界

在这里插入图片描述
在这里插入图片描述

可以看到我们这里将两个数组合并到了nums1中,让数组nums1有序了,但是其实有一个问题,如果我们最后是src2先越界,那么说明nums2数组中的数据已经全部放入nums1了,循环结束了,剩下的nums1的数据也已经保持了有序,所以src2首先越界代码没有问题

但是有可能下一次src1首先越界,说明nums1中原本的有效的元素已经放在nums1数组后面了,循环结束了,但是nums2数组还没有放完呀,这个时候就会出错,所以我们还需要在这个循环结束后用一个新的循环判断一下

如果src2还大于等于0,说明nums2数组中的内容还没有全部移动到nums1中,此时我们只需要循环地让数组nums2中的元素移动到nums1的dest位置,接下来我们的代码才能完全没问题,如下:

void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n) 
{
    int src1 = m - 1;
    int src2 = n - 1;
    int dest = m + n - 1;//nums1的最后一个位置
    while(src1 >= 0 && src2 >= 0)
    {
        //单纯的比大小,谁大就放谁
        if(nums1[src1] < nums2[src2])
        {
            nums1[dest--] = nums2[src2--];
        }
        else
        {
            nums1[dest--] = nums1[src1--];
        }
    }
    //如果nums2还没有走完
    while(src2 >= 0)
    {
        //将nums2的元素全部放进nums1的dest位置
        nums1[dest--] = nums2[src2--];
    }
}

在这里插入图片描述

成功拿下,我们再来分析一下这个代码的时间复杂度和空间复杂度,可以发现我们虽然有两个循环,但是实际上深入计算会发现两个循环的次数加起来为O(N),所以时间复杂度是标准的O(N)

并且由于申请的是有限个空间,所以空间复杂度是O(1),我们就可以看出来了,虽然我们的快排很快,时间复杂度仅为O(N * logN),但是我们的双指针更快,只有O(N)

那么今天就分享到这里,希望大家有所收获,也不枉博主码这么多字,bye~~

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

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

相关文章

【Linux网络编程】网络层 | IP协议 | 网段划分 | 私有IP和公有IP | NAT技术

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站 &#x1f308;个人主页&#xff1a; 南桥几晴秋 &#x1f308;C专栏&#xff1a; 南桥谈C &#x1f308;C语言专栏&#xff1a; C语言学习系…

Web基础之什么是HTTP协议

Q&#xff1a;什么是HTTP协议&#xff1f; 概念&#xff1a;Hyper Text Transfer Protocol&#xff0c;超文本传输协议&#xff0c;规定了浏览器和服务器之间数据传输的规则。 特点&#xff1a; 1&#xff0e;基于TCP协议&#xff1a;面向连接&#xff0c;安全 2&#xff0e;基…

小米路由器IPv6 功能使用指南

本文不限于多层路由使用IPv6 的情况&#xff0c;提供解决IPv6 无法获取的更硬核的方法&#xff0c;需要有ssh 工具。&#xff08;无安卓设备&#xff0c;测试环境win、mac、ios&#xff09; 首先明确一点&#xff0c;就是如果想让你的设备得到GUA 地址&#xff0c;即访问 6.i…

element plus 使用 upload 组件达到上传数量限制时隐藏上传按钮

最近在重构项目&#xff0c;使用了 element plus UI框架&#xff0c;有个功能是实现图片上传&#xff0c;且限制只能上传一张图片&#xff0c;结果&#xff0c;发现&#xff0c;可以限制只上传一张图片&#xff0c;但是上传按钮还在&#xff0c;如图&#xff1a; 解决办法&…

汽车基础软件AutoSAR自学攻略(三)-AutoSAR CP分层架构(2)

汽车基础软件AutoSAR自学攻略(三)-AutoSAR CP分层架构(2) 下面我们继续来介绍AutoSAR CP分层架构&#xff0c;下面的文字和图来自AutoSAR官网目前最新的标准R24-11的分层架构手册。该手册详细讲解了AutoSAR分层架构的设计&#xff0c;下面让我们来一起学习一下。 Introductio…

MBTiles 爬取,读取,数据转换,发布

MBTiles &#xff1a;https://github.com/mapbox/mbtiles-spec/blob/master/1.3/spec.md 1.MBTiles是什么 MBTiles是一个在SQLite 数据库存储瓦片地图数据的标准&#xff0c;该标准的目的是即时传输和使用数据。 作为一个容器格式&#xff0c;MBTiles可以存储任何瓦片数据,…

机器学习笔记合集

大家好&#xff0c;这里是好评笔记&#xff0c;公主 号&#xff1a;Goodnote。本笔记的任务是解读机器学习实践/面试过程中可能会用到的知识点&#xff0c;内容通俗易懂&#xff0c;入门、实习和校招轻松搞定。 笔记介绍 本笔记的任务是解读机器学习实践/面试过程中可能会用到…

java_将数据存入elasticsearch进行高效搜索

使用技术简介&#xff1a; (1) 使用Nginx实现反向代理&#xff0c;使前端可以调用多个微服务 (2) 使用nacos将多个服务管理关联起来 (3) 将数据存入elasticsearch进行高效搜索 (4) 使用消息队列rabbitmq进行消息的传递 (5) 使用 openfeign 进行多个服务之间的api调用 参…

Gitlab-Runner配置

原理 Gitlab-Runner是一个非常强大的CI/CD工具。它可以帮助我们自动化执行各种任务&#xff0c;如构建、测试和部署等。Gitlab-Runner和Gitlab通过API通信&#xff0c;接收作业并提交到执行队列&#xff0c;Gitlab-Runner从队列中获取作业&#xff0c;并允许在不同环境下进行作…

SpringBoot项目实战(41)--Beetl网页使用自定义函数获取新闻列表

在Beetl页面中可以使用自定义的函数从后台新闻列表中获取新闻数据展示到页面上。例如我们可以从后台新闻表中获取新闻按照下面的格式展示&#xff1a; <li><a href"#">东亚非遗展即将盛妆亮相 揭起盖头先睹为快</a></li><li><a hre…

LayaAir3.2来了:性能大幅提升、一键发布安装包、支持WebGPU、3D导航寻路、升级为真正的全平台引擎

前言 LayaAir3的每一个分支版本都是一次较大的提升&#xff0c;在3.1彻底完善了引擎生态结构之后&#xff0c;本次的3.2会重点完善全平台发布相关的种种能力&#xff0c;例如&#xff0c;除原有的安卓与iOS系统外&#xff0c;还支持Windows系统、Linux系统、鸿蒙Next系统&#…

【力扣热题100】—— Day18.将有序数组转换为二叉搜索树

期末考试完毕&#xff0c;假期学习开始&#xff01; —— 25.1.7 108. 将有序数组转换为二叉搜索树 给你一个整数数组 nums &#xff0c;其中元素已经按 升序 排列&#xff0c;请你将其转换为一棵平衡二叉搜索树。 示例 1&#xff1a; 输入&#xff1a;nums [-10,-3,0,5,9] …

C++ Qt练习项目 QChar功能测试

个人学习笔记 代码仓库 GitCode - 全球开发者的开源社区,开源代码托管平台 新建项目 设计UI 1、拖入group box去掉名字 2、拖入2个LineEdit 3、拖入两个Label 4、拖入两个PushButton 5、点栅格布局 1、拖入GroupBox 2、拖入4个PushButton 3、点栅格布局 1、拖入GroupBo…

保证Mysql数据库到ES的数据一致性的解决方案

文章目录 1.业务场景介绍1.1 需求分析1.2 技术实现方案 2.业界常用数据一致性方案分析2.1 同步双写方案2.2 MQ异步双写方案2.3 扫表定期同步方案2.4 监听binlog同步方案 1.业务场景介绍 1.1 需求分析 某知名的在线旅游平台&#xff0c;在即将到来的春季促销活动之前&#xff…

初学stm32 --- DAC模数转换器工作原理

目录 什么是DAC&#xff1f; DAC的特性参数 STM32各系列DAC的主要特性 DAC框图简介&#xff08;F1/F4/F7&#xff09; 参考电压/模拟部分电压 触发源 关闭触发时(TEN0)的转换时序图 DMA请求 DAC输出电压 什么是DAC&#xff1f; DAC&#xff0c;全称&#xff1a;Digital…

《HTTP协议与内外网划分:网络世界的基石知识》

http协议与内外网的划分 http协议的简介 HTTP&#xff08;超文本传输协议&#xff09;是互联网上应用最广泛的一种网络协议&#xff0c;用于从服务器传输超文本&#xff08;如HTML&#xff09;到本地浏览器的传输协议。以下是关于HTTP协议的简介&#xff1a; HTTP协议的基本…

二叉树层序遍历 Leetcode102.二叉树的层序遍历

二叉树的层序遍历相当于图论的广度优先搜索&#xff0c;用队列来实现 &#xff08;二叉树的递归遍历相当于图论的深度优先搜索&#xff09; 102.二叉树的层序遍历 给你二叉树的根节点 root &#xff0c;返回其节点值的 层序遍历 。 &#xff08;即逐层地&#xff0c;从左到右…

特制一个自己的UI库,只用CSS、图标、emoji图 第二版

图&#xff1a; 代码&#xff1a; index.html <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>M…

12工具篇(D3_Lombok)

目录 一、基本介绍 二、Lombok使用说明 1. 基本介绍 2. 安装插件 IDEA在线安装Lombok插件 IDEA离线安装Lombok插件 3. 引入依赖坐标 4. Lombok注解功能说明 NonNull Getter&Setter Cleanup ToString EqualsAndHashCode Constructor RequiredArgsConstructor …

STM32如何测量运行的时钟频率

前言 环境&#xff1a; 芯片&#xff1a;STM32F103C8T6 Keil&#xff1a;V5.24.2.0 一、简介STM32F103C8T6的时钟源 ①HSI 内部高速时钟,RC振荡器&#xff0c;频率为8MHz&#xff0c;精度不高。②HSE 外部高速时钟,可接石英/陶瓷谐振器&#xff0c;频率范围为4MHz~16MHz&…