数据结构———一万字手撕八大排序算法

news2024/11/24 19:34:27

在这里插入图片描述

常见的排序算法

  • 1.排序算法的作用
    • 1.1列如我们在购物时
    • 1.2玩游戏时英雄战力的排行,都得用到排序算法
  • 2.常见排序算法的实现
    • 2.1冒泡排序
      • 时间复杂度计算:
    • 2.2直接插入排序
      • 时间复杂度计算:
    • 2.3选择排序
      • 时间复杂度计算:
    • 2.4希尔排序⭐
      • 时间复杂度计算:
    • 2.5堆排序⭐
    • 2.6快速排序⭐(排序界大哥)
      • 2.6.1hoare版本
        • 问题1.
        • 问题2.
      • 2.6.2挖坑法
      • 2.6.3前后指针版本
      • 快速排序的时间复杂度:
        • 解决数组有序的方法
      • 2.6.4快排非递归
    • 2.7归并排序
      • 2.7.1递归思想
      • 2.7.2非递归
      • 时间复杂度计算:
    • 计数排序
  • 3.排序算法复杂度及稳定性总结

1.排序算法的作用

1.1列如我们在购物时

在这里插入图片描述
筛选价格时,排序就会帮我们按价格由低到高排序或由高到低排序

1.2玩游戏时英雄战力的排行,都得用到排序算法

在这里插入图片描述
所以在我们的生活中排序算法无处不在

2.常见排序算法的实现

我们所有的排序算法均为升序

2.1冒泡排序

先将大家最熟悉的冒泡排序
在这里插入图片描述

这个排序算法我在之前的数组初阶博客中讲到过
在这里插入图片描述
当 i == 0时是第一躺排序,所以每一轮内比较n-1-(比较的轮数-1)

//交换
void Swap(int* e1,int* e2)
{
	int tmp = *e1;
	*e1 = *e2;
	*e2 = tmp;
}


//冒泡排序
 void BubbiSort(int* a,int n)
 {
     for(int i = 0; i < n-1; i++)
     {
         //标记,如果数据本来有序,就不会进入Swap函数
         int flag = 0;
         for(int j = 0; j < n-1-i; j++)
         {
             if(a[j] > a[j+1])
             {
                 Swap(&a[j],&a[j+1]);
                 flag = 1;
             }
         }
         if (flag == 1)
             break;
     }
 }

时间复杂度计算:

在这里插入图片描述

冒泡排序时间复杂度:O(N^2)
在加上我们上面的限制条件,最好情况下时间复杂度为:O(N)

2.2直接插入排序

直接插入排序在我们生活中还是很常见的
比如我们在打扑克时,在搬起一张排后,一定是将这张排插入它对应的位置
例如斗地主中,一般我们都是把王,A,2放在最左边
在这里插入图片描述
那我们每次摸牌都是一次插入排序
比如
在这里插入图片描述
我们要把这张 2 插入到这堆牌中,就要依次与后面元素比较,最后插入到A的中间
那我们要给一个数组排序的话
可以先把前两数看作是一组进行插入排序
然后再把前三个数看作是一组进行插入排序
……这样下去整个数组就会接近有序,例如下图
在这里插入图片描述

代码如下:

void InsertSort(int* a,int n)
{
	for (int i = 1; i < n; i++)
	{
		//end作为i的前一个元素
		int end = i - 1;
		//用来保存i的位置
		int tmp = a[i];
		while (end >= 0)
		{
			//排升序,如果end位置大于tmp
			//就把end位置的元素赋给end+1的位置
			if (tmp < a[end])
			{
				a[end + 1] = a[end];
				--end;
			}
			else
			{
				break;
			}
		}
		//程序走到这里有两种情况
		// 1.a[end] > tmp 就把tmp放在end的后面(a[end+1])
		// 2.tmp < a[end] tmp一直小与a[end],end减到 -1 ,
		//说明此时的tmp最小,就把他放在对头 a[end+1] 的位置
		a[end + 1] = tmp;
	}
}

时间复杂度计算:

在这里插入图片描述
直接插入排序时间复杂度:O(N^2)
如果数据是有序的话,最好情况下时间复杂度为:O(N)

那如果是数据接近有序,我们插入排序的时间复杂度是多少呢?
这就引出了接下来的希尔排序

2.3选择排序

每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完
在这里插入图片描述
排序思想:每次选出一个最小的数

void SelectSort(int* a, int n)
{
	for (int i = 0; i < n; i++)
	{
		//默认i位置为最小的数,依次与后面的数相比较
		int mini = i;
		for (int j = i + 1; j < n; j++)
		{
			if (a[mini] > a[j])
			{
				mini = j;
			}
		}
		Swap(&a[i], &a[mini]);
	}	
}

时间复杂度计算:

在这里插入图片描述
所以选择排序时间复杂度为:O(N^2)
选择排序时间复杂度最好也是:O(N^2)


前面的排序时间复杂度都为O(N^2),排序思想也是比较简单的
接下来我们将几个效率高,比较复杂的排序算法

2.4希尔排序⭐

希尔排序就相当于插入排序的升级版
它的思想是先使数组逐渐变的有序,最后使用插入排序使数组变的有序

插入排序
插入排序是每次与前面一个数字比较,使数组有序如果当前数小于前面的数字,就继续再向前比较,如果大于或等于前面的数字就不用比较了
插入排序是先比较前两个然后比较前三,前四个······
在这里插入图片描述
那希尔排序又有哪些不同呢?
它是把每次数字的跳跃的间隔扩大
比如:我们让他间隔着三个数来比较,使数组有序
在这里插入图片描述

//gap为3的 插入排序
void Sort(int* a, int n)
{
	int gap = 3;
	//gap为3,我们就有三组数据,每次跳过3个数据
	//我们用下标来表示,最后一个数的下标是 7
	//第一组  0 3 6
	//第二组  1 4 7
	//第三组  2 5 
	for (int j = 0; j < gap; j++)
	{
		for (int i = j; i < n - gap; i += gap)
		{
			//end作为要被比较的数的下标
			int end = i;
			//tmp要向前比较的元素
			int tmp = a[i + gap];
			while (end >= 0)
			{
				if (a[end] > tmp)
				{
					a[end + gap] = a[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}
			a[end + gap] = tmp;
		}
	}
}

上面代码我们可以把两个for循环写到一起,代码如下:

void Sort(int* a, int n)
{
	int gap = 3;
	for (int i = 0; i < n-gap; i++)
	{
		int end = i;
		int tmp = a[i + gap];
		while (end >= 0)
		{
			if (a[end] > tmp)
			{
				a[end + gap] = a[end];
				end -= gap;
			}
			else
			{
				break;
			}
		}
		a[end + gap] = tmp;
	}
}

在这里插入图片描述
这样排序后,数组就接近有序了
那我们把gap设置为45呢?

仔细观察我们可以发现:
gap越大,跳的越快,越不接近有序
gap越小,跳的越慢,越接近有序

这就引出了我们的希尔排序
我们让gap == 数组元素个数,每次给gap /= 2
任何一个数除2最后都会等于1,当我们gap为1的时候就相当于是插入排序了,只是排序这个数组已经接近有序了。 当数组接近有序时使用插入排序来排序,效率就会很高
希尔排序代码如下:

void ShellSort(int* a, int n)
{
	int gap = n;
	while (gap > 1)
	{
		//gap /= 2;
		gap = gap / 3 + 1;
		for (int i = 0; i < n - gap; i++)
		{
			int end = i;
			int tmp = a[i + gap];
			while (end >= 0)
			{
				if (a[end] > tmp)
				{
					a[end + gap] = a[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}
			a[end + gap] = tmp;
		}
	}
}

大家发现代码中我们对gap的调整有两种方法
其实gap = gap / 3 + 1;最终也可以使gap等于1。

时间复杂度计算:

在这里插入图片描述

如果gap == 2,那么数组的排序次数为 (n/2需要调整的次数) + (n/4需要调整的次数) + (n/8)需要调整的次数)……;
这里每次调整都会对之后的排序进行优化,经计算大约时间复杂度为n^1.3
平均时间复杂度:O(N^{1.3}次方)
时间复杂度:O(NlogN)

2.5堆排序⭐

想要学会堆排序要先去学一下二叉树哦!
否则你根本不知道它的向下调整排序在干什么!
这里有我之前讲的关于堆排序的方法及时间复杂度

2.6快速排序⭐(排序界大哥)

快速排序的基本思想为,以一个数为参照,一趟排序下来,确定这个数在数组中的位置,并且这个数确定之后,它左边的所有数一定小于等于它,它右边的所有数一定大于等于它。

2.6.1hoare版本

先把gif图给大家看,参照着这个图来理解我下面的这段话
在这里插入图片描述

首先大家要知道快速排序是hoare大佬发明的,所以先讲hoare大佬的方法。
他是以左边第一个数为参照数,再从右边开始向左找比参照数小的数,再从左开始开始向右找比参照数大的数,然后交换这两个数,直到左边的下标 >= 右边的下标。
此时左右下标的相交点就是参照数应该在的位置,把相交点位置的数与参照位置的数相交换,然后把数组再分成两部分。
左边是开始位置到(相交点位置-1)
右边是(相交点位置+1)到 尾部

这样下去数组最终就会有序。
看到上面的思想,我们就知道了,快排用递归的方法写是比较简单的。

我们先写出它的一趟排序

void QuickSort1(int* a, int left, int right)
{
	//让参照的数为左边的数
	int keyi = left;
	int begin = left;
	int end = right;
	while (left < right)
	{
		//从右向左找小
		while (a[right] >= a[keyi] && left < right)
			right--;

		//从左向右找大
		while (a[left] <= a[keyi] && left < right)
			left++;

		Swap(&a[left], &a[right]);
	}

	//到这里a[left],a[right]一定是指向同一个数的
	//所以a[keyi]与谁交换都可以
	Swap(&a[keyi], &a[left]);
	keyi = left;

}

先来讲一下上段代码中我们可能出现的问题:

问题1.

在这里插入图片描述

上图中的两个红框,我们可能会丢掉这里的判断。
假设数组是有序的,可能你会出现这样的情况
在这里插入图片描述

问题2.

在这里插入图片描述

这里的等于号也是我们容易忘记写的。
有的同学可能会说,把begin=left+1,就可以了。
那请看一下,下面这种情况
在这里插入图片描述
这样还是会死循环,所以我们必须要加上 = 号。
hoare的排序算法代码完整版如下:

void QuickSort1(int* a, int left, int right)
{
	if (left >= right)
		return;

	//让参照的数为左边的数
	int keyi = left;
	int begin = left;
	int end = right;
	while (left < right)
	{
		//从右向左找小
		while (a[right] >= a[keyi] && left < right)
			right--;

		//从左向右找大
		while (a[left] <= a[keyi] && left < right)
			left++;

		Swap(&a[left], &a[right]);
	}

	//到这里a[left],a[right]一定是指向同一个数的
	//所以a[keyi]与谁交换都可以
	Swap(&a[keyi], &a[left]);
	keyi = left;

	QuickSort1(a, begin, keyi - 1);
	QuickSort1(a, keyi + 1, end);
}

2.6.2挖坑法

挖坑法实际是在hoare大佬的版本上改动了一点
它是把之前的保存参照下标,改成了保存参照数本身
然后设置一个坑位,坑位最初为参照数的下标
当右边的数小于参照数,就把右边的数放到上一个坑位,坑位放到小于参照数的这个位置
当左边的数大于参照数,就把左边的数放到上一个坑位,坑位放到大于参照数的这个位置
观察整个数组其实挖坑法其实是把小于key的放在数组左边,大于key的放在右边,最后把key放在最后的坑位
在这里插入图片描述
挖坑法代码如下:

void QuickSort2(int* a, int left, int right)
{
	if (left >= right)
		return;

	int key = a[left];
	int hole = left;
	int begin = left, end = right;
	while (left < right)
	{
		while (a[right] >= key && left < right)
			right--;
		a[hole] = a[right];
		hole = right;
		while (a[left] <= key && left < right)
			left++;
		a[hole] = a[left];
		hole = left;
	}
	a[hole] = key;

	QuickSort2(a, begin, hole - 1);
	QuickSort2(a, hole + 1, end);
}

2.6.3前后指针版本

在这里插入图片描述

前后指针法,prev指向第一个元素,cur指向prev的下一个元素,keyi记录第一个元素下标
cur一直向后走,直到它超出数组的长度就停下
如果下标为cur的数 大于 下标为keyi的数,cur就继续向后走
如果下标为cur的数 小于 下标为keyi的数,先++prev ,再与下标为的数cur交换。

void QuickSort3(int* a, int left, int right)
{
	if (left >= right)
		return;

	int keyi = left;
	int prev = left;
	int cur = left + 1;
	int begin = left, end = right;
	while (cur <= right)
	{
		if (a[cur] < a[keyi] && ++prev != cur)
			Swap(&a[prev], &a[cur]);
		cur++;
	}
	Swap(&a[prev], &a[keyi]);

	QuickSort3(a, begin, prev - 1);
	QuickSort3(a, prev + 1, end);
}

快速排序的时间复杂度:

在这里插入图片描述
如果每次都可以找到正中间的那个数,确定它的位置后,接下来的递归图如下
在这里插入图片描述
如果每次都是最好的情况(参照数的位置在中间),我们只需要递归logn次(因为层数越靠后,每趟确定的元素个数越多,呈指数被增长),每次访问的元素给数大约都可以看作n,所以

那如果遇到有序的数组用快排思想来排序呢?
在这里插入图片描述
这样我们的时间复杂度,就变成了O(N^2)

时间复杂度为:N*logN
最坏情况为:O(N^2)
那我们的标题都说了,快速排序是最强的排序,如果遇到最坏情况(数组是有序的)该怎么办呢?

解决数组有序的方法

方法一:
用随机函数在数组中随机抽取一个数,与左边第一个元素交换
代码如下:

srand((unsigned int)time(NULL));
int randi = left + rand() % (right - left);
Swap(&a[randi],&a[left]);

方法二:
三数取中法(用左边数,中间数,有边数比大小,谁在中间,谁就与左边的数交换)
代码如下:

//三数取中
int GetMid(int* a, int left, int right)
{
    int mid = (right + left) / 2;
    if (a[left] < a[mid])
    {
        if (a[mid] < a[right])
        {
            return mid;
        }
        else if (a[left] < a[right])
        {
            return right;
        }
        else
        {
            return left;
        }
    }
    else
    {
        //a[left] > a[mid]
        if (a[mid] > a[right])
        {
            return mid;
        }
        else if (a[left] < a[right])
        {
            return left;
        }
        else
        {
            return right;
        }
    }
}


int mid = GetMid(a, left, right);
if (left != mid)
    Swap(&a[mid], &a[left]);

2.6.4快排非递归

快排非递归的思想是借助栈来帮助排序。
每次在栈中保存右边与左边的下标这里先保存右边再保存左边,出栈时接收返回值就先用begin接收右边下标,再用end接收左边下标
把begin和end下标传给之前写好的QuickSqrt的任意一个版本,返回被调整好后的数的下标
然后再判断是否入栈,具体逻辑看代码
再写一个循环判断栈是否为空,如果栈区为空就说明排序完成。

完整代码如下,栈的代码在我之前的文章中讲过,这里就不提了。

void Swap(int* e1, int* e2)
{
    int tmp = *e1;
    *e1 = *e2;
    *e2 = tmp;
}

//三数取中
int GetMid(int* a, int left, int right)
{
    int mid = (right + left) / 2;
    if (a[left] < a[mid])
    {
        if (a[mid] < a[right])
        {
            return mid;
        }
        else if (a[left] < a[right])
        {
            return right;
        }
        else
        {
            return left;
        }
    }
    else
    {
        //a[left] > a[mid]
        if (a[mid] > a[right])
        {
            return mid;
        }
        else if (a[left] < a[right])
        {
            return left;
        }
        else
        {
            return right;
        }
    }
}


int QuickSort(int* a, int left, int right)
{
    //三数取中
    int mid = GetMid(a, left, right);
    if (left != mid)
        Swap(&a[mid], &a[left]);

    int keyi = left;
    int prev = left;
    int cur = left + 1;
    int begin = left, end = right;
    while (cur <= right)
    {
        if (a[cur] < a[keyi] && ++prev != cur)
            Swap(&a[prev], &a[cur]);
        cur++;
    }
    Swap(&a[prev], &a[keyi]);

    return prev;
}

void QuickSortNonR(int* a, int left, int right)
{
	Stack st;
	STInit(&st);
	STPush(&st, right);
	STPush(&st, left);
	while (!STEmpty(&st))
	{
		int begin = STTop(&st);
		STPop(&st);
		int end = STTop(&st);
		STPop(&st);

		int keyi = QuickSort(a, begin, end);

        if (keyi + 1 < end)
        {
            STPush(&st, end);
            STPush(&st, keyi + 1);
        }
        if (keyi - 1 > begin)
        {
            STPush(&st, keyi - 1);
            STPush(&st, begin);
        }
	}
	STDestroy(&st);
}

int main()
{
	int arr[] = { 9,12,5,8,21,7,8,6 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	QuickSortNonR(arr, 0, sz - 1);
	return 0;
}

2.7归并排序

归并排序的思想是,先使数组的左边与右边有序,再把左右两边的数排成有序数组
在这里插入图片描述

2.7.1递归思想

归并排序的思路有点类似二叉树的后序遍历
我们先要递归把数组分成两部分,分到左右两边就剩一个数的时候就停下来,然后左右两边进行归并,谁大谁放在前面。
因为是要归并整个数组,如果我们直接把大的数放在前面,会覆盖小的值,我们应该再创建一个数组,用来存放归并后的有序数组,然后再拷贝回原来的数组
在这里插入图片描述

void _MergeSort(int* a,int left,int right, int* tmp)
{
    if (left >= right)
        return;
    int mid = (left + right) / 2;
    _MergeSort(a, left, mid, tmp);
    _MergeSort(a, mid + 1, right, tmp);

    int begin1 = left, end1 = mid;
    int begin2 = mid + 1, end2 = right;
    int j = left;
    while (begin1 <= end1 && begin2 <= end2)
    {
        if (a[begin1] <= a[begin2])
        {
            tmp[j++] = a[begin1++];
        }
        else
        {
            tmp[j++] = a[begin2++];
        }
    }

    while (begin1 <= end1)
    {
        
        tmp[j++] = a[begin1++];
        
    }

    while (begin2 <= end2)
    {
        tmp[j++] = a[begin2++];
    }

    memcpy(a + left, tmp + left, sizeof(int) * (right - left + 1));
}

void MergeSort(int* a, int n)
{
    int* tmp = (int*)malloc(sizeof(int) * n);
    if (tmp == NULL)
    {
        perror("malloc fail\n");
        return;
    }

    _MergeSort(a, 0, n - 1, tmp);

    free(tmp);
}

int main()
{
	int arr[] = { 9,12,5,8,21,7,8,6 };
	int sz = sizeof(arr) / sizeof(arr[0]);
    MergeSort(arr, sz);
	return 0;
}

2.7.2非递归

通过观察我们之前画的图,可以发现,最下层的每组都一个元素,往上一层每组元素就是当前层每一组元素的个乘2,相当于我们直接先去调整最下层的排序,然后依次向上,直到整个数组。
在这里插入图片描述

void MergeSortNonR(int* a,int n)
{
    int* tmp = (int*)malloc(sizeof(int) * n);
    if (tmp == NULL)
    {
        perror("malloc fail\n");
        return;
    }
    int gap = 1;
    while (gap < n)
    {
        for (int i = 0; i < n; i += 2 * gap)
        {
            int begin1 = i, end1 = i + gap - 1;
            int begin2 = i + gap, end2 = i + 2 * gap - 1;
            //begin1不可能越界,因为有循环条件限制
            //end1只要越界,begin2就一定越界,当这两个有越界时就不用继续排序了直接break
            //如果begin2没有越界end2越界就还可以继续排序,把end2的下改成数组的最后一个数的下标
            if (end1 >= n || begin2 >= n)
            {
                break;
            }
            if (end2 >= n)
            {
                end2 = n - 1;
            }

            int j = i;
            while (begin1 <= end1 && begin2 <= end2)
            {
                if (a[begin1] <= a[begin2])
                {
                    tmp[j++] = a[begin1++];
                }
                else
                {
                    tmp[j++] = a[begin2++];
                }
            }

            while (begin1 <= end1)
            {

                tmp[j++] = a[begin1++];

            }

            while (begin2 <= end2)
            {
                tmp[j++] = a[begin2++];
            }
            memcpy(a + i, tmp + i, sizeof(int) * (end2 - i + 1));
        }
        gap *= 2;

    }

    free(tmp);

}

int main()
{
	int arr[] = { 9,12,5,8,21,7,8,6 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	MergeSortNonR(arr, sz);
	return 0;
}

时间复杂度计算:

在这里插入图片描述
数组每次除 2 直到每组为一个数时停止,所以我们分组需要分logN(默认以2为底)次,所以
时间复杂度为:O(NlogN)次
最坏和最好情况时间复杂度都为:O(NlogN)次
空间复杂度为:O(N)

计数排序

基数排序就是再新建一个数组,这个数组用来记录需要排序的数组中每个元素出现的个数。
没出现过的元素就是 0 次
如下图:
在这里插入图片描述
只需要打印数组中不为0的元素的下标就可以了。
但这样有一个很明显的缺陷,如果我们数组中最小元素很大怎么办呢?
比如数组中最小元素是 1000,那我们前面就要浪费1000个空间吗?
相信聪明的你一定想到了解决办法!

我们可以用新数组中第一个元素来代表最小值。
新建数组的元素个数就是要排序数组中的最大值-最小值

所以计数排序适合待排序数据是集中的一些数,否则会浪费大量空间。
计数排序代码如下:

void CountSort(int* a, int n)
{
    int max = a[0], min = a[0];
    for (int i = 0; i < n; i++)
    {
        if (max < a[i])
        {
            max = a[i];
        }
        if (min > a[i])
        {
            min = a[i];
        }
    }

    int range = max - min + 1;
    int* countA = (int*)calloc(range,sizeof(int));
    for (int i = 0; i < n; i++)
    {
        countA[a[i] - min]++;
    }

    int j = 0;
    for (int i = 0; i < range; i++)
    {
        while (countA[i]--)
            a[j++] = i + min;
    }

    free(countA);
}

再数据集中的情况下:
时间复杂度为:O(N)
空间复杂度为;O(N)

3.排序算法复杂度及稳定性总结

在这里插入图片描述
复杂度我们前面都讲到了,这里我们说一下稳定性是什么。
假设我们有下面这样一组数:
在这里插入图片描述相同元素顺序不发生改变的视为稳定。
这里我讲以一下选择排序,我们每次选最小的数放左边。如果最小的数与左边的数相等可以不交换,这样大家可能会认为选择排序是稳定的。但事实上不是。如下图:
在这里插入图片描述
数组中最小数为1与左边元素交换,这样前面的8就与后面8的前后顺序交换了,所以说选择排序不稳定。
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Qt音视频开发28-ffmpeg解码本地摄像头(yuv422转yuv420)

一、前言 一开始用ffmpeg做的是视频流的解析,后面增加了本地视频文件的支持,到后面发现ffmpeg也是支持本地摄像头设备的,只要是原则上打通的比如win系统上相机程序、linux上茄子程序可以正常打开就表示打通,整个解码显示过程完全一样,就是打开的时候要传入设备信息,而且…

Prophet学习(五)季节性、假日效应和回归因子

​ 编辑目录 假期和特殊事件建模&#xff08;Modeling Holidays and Special Events&#xff09; 内置国家假日&#xff08;Built-in Country Holidays&#xff09; 季节性的傅里叶级数&#xff08;Fourier Order for Seasonalities&#xff09; 指定自定义季节&#xff08…

启动neo4j备忘录

做个备忘录 Neo4j下载、安装、环境配置 优秀教程&#xff1a;https://blog.csdn.net/zeroheitao/article/details/122925845 Neo4j环境变量配置 1、安装JDK 由于Neo4j是基于Java的图形数据库&#xff0c;运行Neo4j需要启动JVM进程&#xff0c;因此必须安装JAVA SE的JDK。配置…

【数据结构】反射

文章目录&#x1f337; 1 定义&#x1f337; 2 用途(了解)&#x1f337; 3 反射基本信息&#x1f337; 4 反射相关的类&#x1f333; 4.1 Class类(反射机制的起源)Class类中的相关方法&#x1f333; 4.2 反射示例4.2.1 获得Class对象的三种方式4.2.2 反射的使用⭐️反射使用1&a…

3个宝藏级软件,每一个都超级好用,少装一个跟你急

分区助手 下载地址&#xff1a;https://www.disktool.cn/ 很多新手小白使用电脑都不懂&#xff0c;把所有软件都安装到了C盘&#xff0c;时间久了储存的东西变多&#xff0c;C盘空间着实不够用&#xff0c;这个免费的工具可以帮你重新分区&#xff0c;无损数据地执行。除了无损…

腾讯云8核16G18M轻量服务器CPU带宽流量性能测评

腾讯云轻量应用服务器8核16G18M带宽&#xff0c;18M公网带宽下载速度峰值可达2304KB/秒&#xff0c;相当于2.25M/s&#xff0c;系统盘为270GB SSD盘&#xff0c;3500GB月流量&#xff0c;折合每天116GB流量。腾讯云百科分享腾讯云轻量服务器8核16G18M配置、CPU型号、公网带宽月…

ChatGPT资讯—2023.4.3

一、 最新资讯 1. UC伯克利开源大语言模型Vicuna又来了 Vicuna-13b只需要花费300美刀&#xff08;比Alpaca的600美元便宜一半&#xff09;就能搞出来接近ChatGPT的水平。如何用小资源大模型让个人普通者与中小微企业也能用上高科技一直是开源社区孜孜追求的目标 Vicuna开源代…

ERP与CRM、MRP、PLM、APS、MES、WMS、SRM的关系

数字化转型中少不了ERP系统的存在&#xff0c;CRM/MRP/PLM/APS/MES/WMS/SRM这些都需要一起上吗&#xff1f; 如下图所示&#xff0c;是某企业IT系统集成架构流图。 先了解一下ERP是做什么的&#xff0c;ERP就是企业资源管理系统&#xff0c;从企业的价值链分析&#xff0c;企业…

用机器学习sklearn+opencv-python过古诗文网4位数字+字母混合验证码

目录 获取验证码图片 用opencv-python处理图片 制作训练数据集 训练模型 识别验证码 编写古诗文网的登录爬虫代码 总结与提高 源码下载 在本节我们将使用sklearn和opencv-python这两个库过掉古诗文网的4位数字字母混合验证码&#xff0c;验证码风格如下所示。 验证码获…

分库分表介绍以及shardingjdbc实现分库分表

分库分表概念 一、什么是分库分表 分库分表是在海量数据下&#xff0c;由于单库、表数据量过大&#xff0c;导致数据库性能持续下降的问题&#xff0c;演变出的技术方案。 分库分表是由分库和分表这两个独立概念组成的&#xff0c;只不过通常分库与分表的操作会同时进行&…

还不懂怎么设计超时关单?一文告诉你!

背景介绍 ​ 提交订单&#xff0c;是交易领域避不开的一个话题。在提交订单设计时&#xff0c;会涉及到各种资源的预占&#xff1a;如商品库存、优惠券数量等等。但是&#xff0c;下单的流程并不能总是保证成功的&#xff0c;如商品库存异常的时候被拦截、优惠券数量不足的时候…

3月更新 | Visual Studio Code Python

我们很高兴地宣布&#xff0c;2023年3月版 Visual Studio Code Python 和 Jupyter 扩展现已推出&#xff01; 此版本包括以下改进&#xff1a; 后退按钮和取消功能添加到创建环境命令默认情况下&#xff0c;Python 扩展不再附带 isortJupyter 笔记本中内核选择的改进Python P…

Modbus 协议详解

Modbus 协议详解 通信协议是指双方实体完成通信或服务所必须遵循的规则和约定&#xff0c;例如我们为实现人与人之间的交流需要约定统一的语言&#xff0c;统一的文字&#xff0c;规定语速等等。 而对于设备之间&#xff0c;协议定义了数据单元使用的格式&#xff08;例如大端…

四、数组、切片,映射

一、一维数组 //声明一个包含5个元素的整型数组 var array [5]int //具体数值填充数组 array : [5]int{1, 2, 3, 4, 5} //容量由初始化值的数量决定 array : [...]int{1, 2, 3, 4, 5) //只初始化索引为1和2的元素 array : [5]int{1: 10, 2: 20} //修改索引为2的元素的值 array…

Linux文件系统、虚拟内存、进程与线程、锁

文章目录文件系统suLinux 中默认没有 super 命令/proc/etc/var/root/home/bin/dev/lib/sbintmp句柄maxfdPWDpathhomeexportwdfdu虚拟内存jobsLinux下一切皆文件swaponmkswap进程与线程nohup子进程与父进程unix进程间的通信方式线程的同步方式sedtarhistory硬链接ln&#xff08;…

Go分布式爬虫笔记(二十一)

文章目录21 切片和哈希表切片底层结构截取扩容哈希表原理哈希碰撞拉链法开放寻址法&#xff08;Open Addressing&#xff09;读取重建原理删除原理思考题Go 的哈希表为什么不是并发安全的&#xff1f;在实践中&#xff0c;怎么才能够并发安全地操作哈希表&#xff1f;拉链法开放…

软件设计师笔记-----程序设计语言与语言处理程序基础

文章目录七、程序设计语言与语言处理程序基础7.1、编译与解释&#xff08;低频&#xff09;7.2、文法&#xff08;低频&#xff09;7.3、有限自动机与正规式&#xff08;几乎每次都会考到&#xff09;有限自动机正规式7.4、表达式&#xff08;偶尔考到&#xff09;7.5、传值和传…

2023-详解实时数仓建设

一、实时数仓建设背景 1. 实时需求日趋迫切 目前各大公司的产品需求和内部决策对于数据实时性的要求越来越迫切&#xff0c;需要实时数仓的能力来赋能。传统离线数仓的数据时效性是 T1&#xff0c;调度频率以天为单位&#xff0c;无法支撑实时场景的数据需求。即使能将调度频…

网狐大联盟增加账号登陆功能

1. UI设计 2. 发布CSB文件,并添加到前端工程资源目录下 打开已发布csb文件所有目录 复制到工程目录 如果有用到其它目录的资源也要同步复制到工程资源对应目录中: 2.脚本功能编写 增加前端结构: -- 帐号登录 login.CMD_MB_LogonAccounts= {{t = "word", k = &#

企业电子招标采购系统源码—企业战略布局下的采购寻源

​ 智慧寻源 多策略、多场景寻源&#xff0c;多种看板让寻源过程全程可监控&#xff0c;根据不同采购场景&#xff0c;采取不同寻源策略&#xff0c; 实现采购寻源线上化管控&#xff1b;同时支持公域和私域寻源。 询价比价 全程线上询比价&#xff0c;信息公开透明&#xff…