算法-排序算法(冒泡选择插入希尔快速归并堆)

news2024/11/14 16:31:32

1.算法概述

1.1什么是算法

算法是特定问题的求解步骤的描述,是独立存在的一种解决问题的思想和方法。对于算法而言计算机编程语言并不重要,可以用任何计算机编程语言来编写算法。

程序=数据结构+算法

1.2数据结构和算法的区别和联系

数据结构只是静态的描述了数据元素之间的关系,高效的程序需要在数据结构的基础之上设计、选择合适的算法才能完成。数据结构是算法实现的载体,算法是为了解决实际问题提出的思想和方法。二者之间缺一不可,相辅相成。

1.3算法效率的度量

一般算法的效率是通过时间复杂度和空间复杂度来度量的。但是目前对于越来越便宜、越来越大的内存空间来说,我们更关注时间复杂度。但是,在某些特殊场景下,比如程序运行在单片机上时,就有可能要考虑算法的空间复杂度。同一个问题,我们可以用不同的算法来解决,我们应当根据具体实际需求来选取最合适的算法。例如,有的算法时间复杂度是O(n2),但是空间复杂度为O(1),另外一个算法时间复杂度为O(n),空间复杂度为O(n)。如果我们更加关注的是时间上的效率,肯定选择第二种算法,,这也就是所谓的空间换时间。如果在某些特殊场景下,存储空间非常紧缺,就要选取第一种算法。

一般情况下,我们更加关注时间复杂度。

算法效率度量,一般有两种方式:

1.3.1事后统计法

定义:事后统计法是在程序编制完成后,通过实际运行程序并收集运行时间、占用内存等数据来评估算法性能的方法。

这种方法可行,但是不是一个好方法,该方法有两个缺陷:

第一个缺陷,要想对某种算法进行评测,必须先依据该算法编制相应的程序,并实际运行;

第二个缺陷,算法运行的时间等效率的统计它依赖于所允许的计算机硬件、软件等环境因素这些环境因素有时候会掩盖算法本身的优劣。

1.3.2事前分析法

定义:事前分析法是在算法设计阶段,通过分析算法的逻辑结构和操作步骤,估算算法的时间和空间复杂度来评估算法性能的方法。

影响算法的主要因素就是该算法采用的策略和方法。

1.3.3求解算法时间复杂度的具体步骤

找出算法中的基本语句;算法中执行次数最多的那条语句就是基本语句,通常是最内层循环的循环体。

计算基本语句的执行次数的数量级;只要保证基本语句执行次数函数中的最高次幂正确即可,可以忽略所有低次幂和最高次幂的系数,这样的话,我们的注意力只集中在最重要的一点上:增长率。

1.3.4常见的时间复杂度

执行次数函数非正式术语

12

O(1)常数阶
2n+8O(n)线性阶
3n^2^+5n+19O(n^2^)平方阶
4n^3^+2n^2^+6n+22O(n^3^)立方阶
7logn+8O(logn)对数阶
9nlogn+15O(nlogn)nlogn阶
2^n^+5n^3^+7n^2^+66O(2^n^)指数阶

例子:

temp = i; i = j; j = temp;

上面代码段的执行时间是一个与问题规模n无关的常数,算法的时间复杂度为常数阶。如果一个算法的执行时间不随着问题规模n的增加而增长,即使算法中有上千条语句,其执行时间也不过是一个比较大的常数而已。此类算法的时间复杂度都为常数阶,即O(1)

for (i = 0; i < n; i++)
    cout << i << endl;

上面代码段的时间复杂度为O(n)

sum = 0;
for (i = 1; i <= n; i++)
    for (j = 1; j <= n; j++)
      sum++;

上面代码段的时间复杂度为O(n^2^)

count = 1;
while (count <= n)
    count = count * 2;

上面代码段的时间复杂度为O(logn)

1.3.5算法的空间复杂度定义

算法的空间复杂度是对一个算法在运行过程中临时占用的存储空间大小的度量,用大O(n)记号表示。

一个算法在计算机存储器上所占用的存储空间,包括存储算法本身所占用的空间、算法的输入、输出数据所占的存储空间、和算法在运行过程中临时占用的存储空间三部分。算法本身所占用的存储空间和算法的编写的长短成正比,要压缩算法本身所占用的空间,就必须编写出较短的程序。算法的输入输出数据所占的存储空间是由要解决的问题决定的,它不随着算法不同而改变。算法在运行过程中所需要的临时存储空间随着算法的不同而异,有的算法只需要较少的临时工作单元,而且不随着问题规模的大小而改变,我们称这种算法是就地执行的,有的算法需要的临时工作单元和要解决问题的规模n有关,它随着n的增大而增长,当n比较大的时候,就需要占用较多的存储空间。

2.排序算法

2.1概念

排序是计算机中非常重要的一个操作,其目的是将一组无序的数据元素通过某种算法调整为有序的数据元素。

在现实生活中,排序是基础而且重要的操作,例如,京东上按照条件(价格、销量、好评度等)进行排序。

排序过程中的关键操作:

比较:任意两个数据元素通过比较来确定先后次序;

交换:如果比较后顺序不对,需要交换。

排序的分类:

根据带排序的数据元素的多少和使用的内存类别,排序分为内部排序和外部排序。

内部排序:数据比较少,可以一次全部加载到内存中进行的排序称为内部排序。绝大部分排序都属于内部排序。

外部排序:数据量大到无法一次性全部加载到内存中,需要借助硬盘等辅助存储器进行排序,称为外部排序。

排序算法的稳定性

定义:如果在序列中有两个数据元素r[i]和r[j]其中r[i]==r[j],在排序前,r[i]在r[j]的前面,排序之后,如果r[i]仍然在r[j]的前面,则称该排序算法是稳定的,否则,则称该算法是不稳定的。

如果排序的内容是一个复杂对象的多个数字属性,且其原本的初始顺序存在意义,那么我们需要在二次排序的基础上保持原有排序的意义,才需要使用稳定性的排序算法。例如要排序的内容是一组原本按照价格排序的对象,现在需要按照销量进行排序,如果使用稳定性的排序算法,那么销量相同的对象依旧保持着价格的排序结果。当然,如果不需要保持初始排序的意义,那么使用不使用稳定性的排序算法都无所谓。

2.2冒泡排序(Bubble Sort)

(假设,从小到大排序)原理:从第一个元素开始,比较相邻两个元素的大小,若大小顺序有误,则交换。然后进行下一个元素的比较。如此扫描一趟后,可以确保最大的元素放到最后的位置,接着,进行第二次扫描。。。。。直到完成所有的排序。

算法分析:

时间复杂度:在冒泡排序中,第一趟需要进行n-1次比较,第二趟需要进行n-2次比较。。。。。第n-1趟需要1次比较,因此,总的比较次数为:(n-1)+(n-2)+(n-3)+......+3+2+1=n(n-1)/2

时间复杂度为O(n^2^)

空间复杂度:由于在冒泡排序的整个过程中,只有交换的时候需要一个单位的临时辅助空间,所以冒泡排序的空间复杂度为O(1)

稳定性:冒泡排序就是把大的元素往后调,如果相等不交换,所以冒泡排序是一种稳定的排序算法。

#include <iostream>
using namespace std;

void printArry(int arr[], int len)
{
	for (int i = 0; i < len; i++)
	{
		cout << arr[i] << " ";
	}
	cout << endl;
}


void bubbleSort(int arr[], int len)
{
	int count = 0;//记录比较次数
	for (int i = 0; i < len - 1; i++)
	{
		bool flag = false;
		for (int j = 0; j <len - i - 1; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				int temp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = temp;
				flag = true;
			}
			count++;
		}
		if (!flag)
		{
			break;
		}
	}
	//cout << "总共比较了" << count << "次" << endl;
}

int main()
{
	//int a[] = { 6,3,1,5,7,9,2,8,4 };
	int a[] = { 9,8,7,6,5,4,3,2,1 };
	int length = sizeof(a) / sizeof(int);
    cout << "这是冒泡排序" << endl;
	printArry(a, length);
	bubbleSort(a, length);
	printArry(a, length);
	return 0;
}

2.3选择排序(Selection Sort)

原理:从待排序的数据元素中选一个最小值,和当前序列最左边的元素进行交换。

算法分析:

时间复杂度:在选择排序中,需要进行n-1趟选择,第一趟需要进行n-1次比较,第二趟需要进行n-2次比较。。。。。第n-1趟需要1次比较,因此,总的比较次数为:(n-1)+(n-2)+(n-3)+......+3+2+1=n(n-1)/2

时间复杂度为O(n^2^)

空间复杂度:只需要两个临时存储单元,所以空间复杂度为O(1)

稳定性:选择排序是给每个位置选择当前最小值,比如给第一个位置选择最小的,在剩余的元素里面给第二位置选择第二小的。。。。以此类推,直到第n-1个元素,第n个元素就不用选择了,因为只剩下它一个最大的元素了。那么,在一趟选择中,如果一个元素比当前元素小,而该小的元素又出现在一个和当前元素相等的元素后面,那么交换后,稳定性就被破坏了。所以,选择排序是不稳定的排序算法。

#include <iostream>
using namespace std;

void printArry(int arr[], int len)
{
	for (int i = 0; i < len; i++)
	{
		cout << arr[i] << " ";
	}
	cout << endl;
}


void selectionsort(int arr[], int len)
{
	
	for (int i = 0; i < len - 1; i++)
	{
		int min = i;
		for (int j = i+1; j <len ; j++)
		{
			
			if (arr[min]> arr[j])
			{
				min = j;
			}
		}
		if (i != min)
		{
			int temp = arr[i];
			arr[i] = arr[min];
			arr[min] = temp;
		}
	}
}

int main()
{
	int a[] = { 6,3,1,5,7,9,2,8,4 };
	//int a[] = { 9,8,7,6,5,4,3,2,1 };
	int length = sizeof(a) / sizeof(int);
	cout << "这是选择排序:" << endl;
	printArry(a, length);
	selectionsort(a, length);
	printArry(a, length);
	return 0;
}

2.4 插入排序(Insertion Sort)

原理:将数据中的元素逐一和排好序的数据进行比较,再将元素插入到合适的位置。

详细过程如下:插入排序又叫直接插入排序(straight Insertion Sort),其基本操作是将一个元素插入到已经拍好序的有序表中,从而得到一个新的、元素数增1的有序表。

假定现在有序表中已经有多个元素了,我们取数组中的第i个元素,往0~i-1的有序表中进行插入。插入的过程如下:跟有序表中的最大值arr[i-1]比较,发现比最大值小,将最大值arr[i-1]往后挪一位,继续和有序表中的第二大值arr[i-2]进行比较,如果发现比它还小,有序表中的第二大值arr[i-2]继续往后挪一位。。。。。

直到找到有序表中第j个元素比当前元素要小或者将有序表找完了,就扎到合适的位置,进行插入位置。

算法分析:

时间复杂度:比较次数为1+2+3.。。。。+(n-2)+(n-1)=n(n-1)/2,时间复杂度为O(n^2^);;空间复杂度为O(1);

稳定性:判断的条件式待插入元素小于已经排好序的元素,因此,不小于是不会移动的,是稳定的排序算法。

#include <iostream>
using namespace std;

void printArry(int arr[], int len)
{
	for (int i = 0; i < len; i++)
	{
		cout << arr[i] << " ";
	}
	cout << endl;
}


void insertionSort(int arr[], int len)
{
	
	for (int i = 1; i < len; i++)
	{
		int j = i - 1;
		int temp = arr[i];
		
		while (j >= 0 && arr[j] > temp)
		{
			arr[j + 1] = arr[j];
			j--;
		}
		arr[j + 1] = temp;
	}
	
}

int main()
{
	int a[] = { 6,3,1,5,7,9,2,8,4 };
	//int a[] = { 9,8,7,6,5,4,3,2,1 };
	int length = sizeof(a) / sizeof(int);
	cout << "这是插入排序:" << endl;
	printArry(a, length);
	insertionSort(a, length);
	printArry(a, length);
	return 0;
}

2.5希尔排序(Shell Sort)

希尔排序算法又叫缩小增量排序算法,是一种更高效的插入排序算法。和普通的直接插入排序算法比较,希尔排序算法大大减少了移动元素的次数,从而提高了排序效率。

因为当数组很大时,使用直接插入排序有个弊端,就是如果小值放在末端的时候,直接插入排序需要从末端开始,逐个后移比小值大的这些元素,很低效,而希尔排序通过分组的方式,直接让末端的元素前端元素进行比较,可以吧这些小值很快的移动到前端。

希尔排序的实现思路:

将待排序列划分成多个子序列,使用普通的直接插入排序对每个子序列进行排序;

按照不同的分组标准,重复执行第一步;

使用普通的直接插入排序算法对整个序列进行排序。

具体方法:先选定一个小于n的整2数gap作为第一增量(n/2),然后将所有距离为gap 的元素分再同一组,并对同一组的元素进行直接插入排序。然后再取一个比第一增量小的整数作为第二增量,重复上述过程。。。。当增量减小到1时,就相当于整个序列被分到同一组,进行一次直接插入排序即可。

算法分析:

时间复杂度:希尔排序的时间复杂度取决于gap序列的设计,一般情况下,其时间复杂度不超过平方阶,平均时减复杂度为O(n1.5)

空间复杂度为:O(1);

稳定性:不稳定的排序算法,只能保证同一组稳定,不能保证其他组。

#include <iostream>
using namespace std;

void printArry(int arr[], int len)
{
	for (int i = 0; i < len; i++)
	{
		cout << arr[i] << " ";
	}
	cout << endl;
}


void shellSort(int arr[], int len)
{
	for (int gap = len / 2; gap > 0; gap /= 2)
	{
		for (int i = gap; i < len; i++)
		{
			int j = i - gap;
            int temp = arr[i];

			while (j >= 0 && arr[j] > temp)
			{
				arr[j + gap] = arr[j];
				j-=gap;
			}
			arr[j + gap] = temp;
		}
	}

}

int main()
{
	int a[] = { 6,3,1,5,7,9,2,8,4 };
	//int a[] = { 9,8,7,6,5,4,3,2,1 };
	int length = sizeof(a) / sizeof(int);
	cout << "这是希尔排序:" << endl;
	printArry(a, length);
	shellSort(a, length);
	printArry(a, length);
	return 0;
}

2.6快速排序(Quick Sort)

原理:通过一趟排序将待排序列分城独立的两部分,其中一部分的数据都比另外一部分要小,然后再按此方法对这两部分数据进行快速排序,整个排序过程是递归进行的,已达到整个序列有序。

具体方法如下:

受限设定一个分界值,通过改分界值分成左右两部分;将大于或者等于分界值的数据放在数组的右边,小于分界值的数据放在数组的左边,然后,左右两部分再进行处理,左半部分再取一个分界值,将该部分数据再分成左右两部分,右半部分数据也做相同处理。。。。重复上述过程,可以看出,这是一个递归定义。通过递归将左半部分的数据排序,再递归排好右半部分数据。当左右两个部分的数据排序好后,整个数组就排序完成了。

详细过程如下:

假设对 6 1 2 7 9 3 4 5 10 8这十个数进行快速排序,首先在这个序列中找一个数作为基准值(分界值),为了方便起见,就让第一个数6作为基准值,接下里,我们需要用某种方法将这个序列中比基准值小的数放在基准值左边,比基准值大的数放在基准值的右边,类似于这种结果:3 1 2 5 4 6 9 7 10 8

再初始状态下,元素6在第一位,目标是将6挪到中间的某个位置,假设这个位置为k,现在就需要寻找这个k。

分别从数组的两端开始探测,先从右边开始,往左找一个小于6 的数,再从左边往右边找一个大于6的数,然后交换它们。然后再从右到左,找一个小于6的数,从左到右找一个大于6的数。。。可以用两个变量i和j,分别指向序列的最左端和最右端,可以称i和j为哨兵。直到i和j碰头,这时,i或者j的位置就是上述所要找的那个k。

算法分析:

时间复杂度为:O(nlogn)

空间复杂度为:O(logn)

稳定性:不稳定的排序算法

#include <iostream>
using namespace std;

void printArry(int arr[], int len)
{
	for (int i = 0; i < len; i++)
	{
		cout << arr[i] << " ";
	}
	cout << endl;
}


void quickSort(int arr[], int left, int right)
{
	int i= left, j = right;
	int key = arr[left];
	if(i > j)
	{
		return;
	}
	while (i < j)
	{
		while (i < j && arr[j] >= key)
		{
            j--;
			/*if (i == j)
			{
				break;
			}*/
		}
		/*if (i < j)
		{
			arr[i] = arr[j];
			i++;
		}*/
		while (i < j && arr[i] <= key)
		{
			i++;
			/*if (i == j)
			{
				break;
			}*/
		}
		/*if (i < j)
		{
           arr[j] = arr[i];
		   j--;
		}*/
		int temp = arr[i];
		arr[i] = arr[j];
		arr[j] = temp;
	}
	arr[left] = arr[i];
    arr[i] = key;
	if (left < i - 1)
	{
        quickSort(arr, left, i - 1);
	}
	if (right > i + 1)
	{
        quickSort(arr, i + 1, right);
	}
}

int main()
{
	int a[] = { 6,3,1,5,7,9,2,8,4 };
	//int a[] = { 9,8,7,6,5,4,3,2,1 };
	int length = sizeof(a) / sizeof(int);
	cout << "这是快速排序:" << endl;
	printArry(a, length);
	quickSort(a, 0, length -1);
	printArry(a, length);
	return 0;
}

2.7 归并排序(Merge Sort)

归并排序算法有两个基本操作,一个是分,也就是把原始数组划分成两个子数组的过程。另一个就是治,他将两个有序数组合并成一个更大的有序数组。

算法分析:

时间复杂度:O(nlogn)

空间复杂度:因为归并排序过程中需要使用一个和原始数组相同大小的辅助数组,所以,归并排序的空间复杂度为O(n)

稳定性:稳定的排序算法

#include <iostream>
using namespace std;

void printArry(int arr[], int len)
{
	for (int i = 0; i < len; i++)
	{
		cout << arr[i] << " ";
	}
	cout << endl;
}

void merge(int arr[], int left, int mid, int right)
{
	
    int i = left, j = mid + 1, k = 0;
	// 1. 建立一个临时数组,大小为right - left + 1
	int *temp = new int[right - left + 1];
	// 2. 开始循环,把左右两个数组元素小值依次插入到临时数组中
	while (i < mid + 1 && j < right + 1)
	{
		if (arr[i] < arr[j])
		{
			temp[k++] = arr[i++];
		}
		else
		{
			temp[k++] = arr[j++];
		}
	}
	while (i < mid + 1)// j序列结束,如果左半部分还有剩余元素,则依次插入到临时数组中
	{
		temp[k++] = arr[i++];
	}
	while (j < right + 1)//i序列结束,如果右半部分还有剩余元素,则依次插入到临时数组中
	{
		temp[k++] = arr[j++];
	}
    // 3. 把临时数组中的数据拷贝到原数组中
	for (int i = 0; i < k; i++)
	{
		arr[left + i] = temp[i];
	}
	// 4. 释放临时数组
	delete []temp;
}

void mergeSort(int arr[], int left, int right)
{
	int i = left , j = right;
	if (i < j)
	{
        int mid = (i + j) / 2;
        mergeSort(arr, i, mid);//递归对左半部分进行排序
		mergeSort(arr, mid + 1, j);//递归对右半部分进行排序
		merge(arr, i, mid, j);//合并两个有序部分
	}
}

int main()
{
	int a[] = { 6,3,1,5,7,9,2,8,4 };
	//int a[] = { 9,8,7,6,5,4,3,2,1 };
	int length = sizeof(a) / sizeof(int);
	cout << "这是归并排序:" << endl;
	printArry(a, length);
	mergeSort(a, 0, length - 1);
	printArry(a, length);
	return 0;
}

2.8 堆排序(Heap Sort)

最大堆积数,又叫大顶堆,所有节点的值都大于或者等于其左右子节点的值,树根就是最大堆积树中的最大值。

最小堆积数,又叫小顶堆,所有节点的值都小于或者等于其左右节点的值,树根就是最小堆积树中的最小值。

堆排序的思路:(这里以升序排序为例,如果是降序排序,则需要构造小顶堆)将待排序列构造成一个大顶堆,此时,整个序列中的最大值就是大顶堆的根节点。将其与末尾元素进行交换,此时末尾元素即为最大值。然后将剩下的n-1个元素重新调整为大顶堆,这样就会得到n个元素中的次大值,再次与末尾元素进行交换。。。。如此,反复执行,最后得到一个有序序列。

堆排序的步骤:

1.建立一个大顶堆:将n个元素组成的无序序列构建成一个大顶堆;

2.将堆顶元素元素与末尾元素进行交换,将最大元素“沉到”数组末端;

3.调整大顶堆:将n-1个元素重新调整为大顶堆;

4.反复执行步骤2和步骤3,直到整个序列有序。

为什么第一个非叶子节点的索引为n/2-1呢?

因为索引为i的节点的父节点索引为(i-1)/2,而最后一个叶子节点索引为n-1,其父结点编号为(n-1-1)/2=n/2-1,恰恰最后一个叶子节点的父节点即为第一个非叶子节点。

通常堆是通过一维数组来实现的。在数组起始位置为0的情形中:

父节点i的左子节点在位置(2i+1)

父节点i的右子节点在位置(2i+2)

#include <iostream>
using namespace std;

void printArry(int arr[], int len)
{
	for (int i = 0; i < len; i++)
	{
		cout << arr[i] << " ";
	}
	cout << endl;
}

void heapify(int arr[], int index, int end)
{
	int maxindex = index;
	int left = index * 2 + 1;
	int right = index * 2 + 2;
	if (left < end && arr[left] > arr[maxindex])
	{
		maxindex = left;
	}
	if (right < end && arr[right] > arr[maxindex])
	{
		maxindex = right;
	}
	if (maxindex != index)
	{
		int temp = arr[index];
		arr[index] = arr[maxindex];
		arr[maxindex] = temp;
		heapify(arr, maxindex, end);
	}
}

void heapSort(int arr[], int len)
{
	//从第一个非叶子节点(len/2-1)开始,调整堆,使其称为一个大顶堆
	for (int i = len / 2 - 1; i >= 0; i--)
	{
		heapify(arr, i, len);
	}
	//将堆顶元素与末尾元素进行交换,重新调整堆,形成一个新的大顶堆
	for (int i = len - 1; i > 0; i--)
	{
		int temp = arr[i];
		arr[i] = arr[0];
		arr[0] = temp;
		heapify(arr, 0, i);
	}

}

int main()
{
	int a[] = { 6,3,1,5,7,9,2,8,4 };
	//int a[] = { 9,8,7,6,5,4,3,2,1 };
	int length = sizeof(a) / sizeof(int);
	cout << "这是堆排序:" << endl;
	printArry(a, length);
	heapSort(a, length);
	printArry(a, length);
	return 0;
}

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

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

相关文章

闲置物品交易系统小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;学生管理&#xff0c;卖家管理&#xff0c;广场管理&#xff0c;商品类型管理&#xff0c;商品信息管理&#xff0c;系统管理 微信端账号功能包括&#xff1a;系统首页&#xff0c;广场&#xff0c;商…

欧盟禁用PFAS材料!线材该如何应对?

了应对欧盟即将在2027年1月开始强制执行禁用PFAS材料的法规&#xff0c;一众线材厂商纷纷研发出了对应的替代材料&#xff0c;第二十三届华东磁性元件峰会现场&#xff0c;多家线材企业均带来了PFAS材料的替代产品。 宇盛电子 苏州宇盛电子有限公司&#xff08;下称“宇盛电子…

从Profinet到Ethernet IP网关技术重塑工业网络,数据传输更流畅

Profinet转Ethernet IP网关在未来工业领域可能产生以下重要影响并发挥关键作用&#xff1a;促进工业设备集成与互操作性&#xff1a;打破协议壁垒&#xff1a;在工业场景中&#xff0c;存在多种不同的工业以太网协议&#xff0c;设备往往因协议差异而难以直接通信。 Profinet转…

基于姿态估计的翻墙动作识别系统(姿态估计 mediapipe )

基于姿态估计的翻越动作识别系统是一个结合了计算机视觉、姿态估计技术和深度学习的项目&#xff0c;其目的是识别特定的动作&#xff0c;比如“翻越”动作。在这个项目中&#xff0c;我们使用MediaPipe姿态估计工具进行人体姿态估计&#xff0c;并结合深度学习模型来识别翻越动…

城市内涝积水监测预警系统助力智慧城市应急防汛

一、方案背景 近 20 年来&#xff0c;我国城市化进程加快&#xff0c;城市地区由于人口密集&#xff0c;物资财产密度不断加大等特点&#xff0c;高强度暴雨积水形成的洪涝灾害对城市化地区产生的威肋和带来的损失越来越大。由于城市的不断扩建&#xff0c;使工业区、商业区和居…

施耐德EcoStruxure Machine SCADA Expert(EMSE)用户管理进阶(十四)

之前简单介绍了用户管理的功能,这里详细展示要实现一套完整的用户管理功能需要做什么 如下图所示 符合GMP的用户管理功能一般可包含如下功能: 用户新建、用户密码修改、用户锁定/激活、用户删除、用户组策略配置(右下角可配置参数) 首先新建变量—结构体 1.cUser 2.cUser…

java日志框架之JUL(Logging)

文章目录 一、JUL简介1、JUL组件介绍 二、Logger快速入门三、Logger日志级别1、日志级别2、默认级别info3、原理分析4、自定义日志级别5、日志持久化&#xff08;保存到磁盘&#xff09; 三、Logger父子关系四、Logger配置文件 一、JUL简介 JUL全程Java Util Logging&#xff…

OSG开发笔记(三十):OSG加载动力学仿真K模型文件以及测试Demo

​ 若该文为原创文章&#xff0c;未经允许不得转载 本文章博客地址&#xff1a;https://blog.csdn.net/qq21497936/article/details/142340138 各位读者&#xff0c;知识无穷而人力有穷&#xff0c;要么改需求&#xff0c;要么找专业人士&#xff0c;要么自己研究 长沙红胖子Q…

地大信息-基础信息平台 GetImg 任意文件读取漏洞复现

0x01 产品简介 地大信息的基础信息平台,通过整合各类空间基础数据,包括地理、地质、气象等多源信息,构建了一个空-天-地一体化的自然灾害监测物联网和时空感知大数据平台。该平台不仅支持数据的集成、管理和共享,还提供了丰富的数据分析和应用服务,为政府决策、行业监管和…

C++对C的扩充

练习&#xff1a;提示输入字符串&#xff0c;统计字符串中空格&#xff0c;大写字母&#xff0c;小写字母&#xff0c;其他字符的个数 #include <iostream>using namespace std;int main() {cout << "请输入字符串&#xff1a;";string str;getline(cin…

[OpenCV] 数字图像处理 C++ 学习——14霍夫变换直线、圆检测 附完整代码

文章目录 前言1.霍夫变换原理(1)霍夫变换检测直线的原理(2)霍夫变换检测圆的原理 2.代码实现(1)霍夫直线检测(2)霍夫圆检测 3.完整代码 前言 霍夫变换是一种有效的检测图像中的几何形状&#xff08;如直线、圆等&#xff09;的算法。霍夫变换通过将几何形状的检测问题转化为参…

微分方程学习笔记

文章目录 一、基础概念二、重要结论1、一阶可分离变量的微分方程求解思路2、一阶非齐次线性微分方程解的结构3、可降阶的高阶微分方程4、线性微分方程解的结构1. 二阶齐次线性微分方程解的结构2. 二阶非齐次线性微分方程解的结构 参考 之前大学的时候学微分方程有点云里雾里的&…

【2024】前端学习笔记7-颜色-位置-字体设置

学习笔记 1.定义&#xff1a;css2.颜色&#xff1a;color3.字体相关属性&#xff1a;font3.1.字体大小&#xff1a;font-size3.2.字体风格&#xff1a;font - style3.3.字体粗细&#xff1a;font - weight3.4.字体族&#xff1a;font - family 4.位置&#xff1a;text-align 1.…

CMA第三方软件测试报告获取指南

在当今竞争激烈的市场环境中&#xff0c;企业对于产品质量的重视程度不断提高。为了确保软件产品的性能和安全性&#xff0c;许多企业开始寻求专业的第三方测试服务来获取第三方软件测试报告&#xff0c;而有无CMA认证资质对于软件测试报告来说至关重要。 CMA认证是中国国家认…

Web 服务器介绍 | 通过 Tomcat 说明其作用

1. 什么是 Web 服务器&#xff1f; Web服务器是一种用于存储、处理和提供网页或Web应用程序的服务器。它通过HTTP&#xff08;超文本传输协议&#xff09;与客户端&#xff08;通常是浏览器&#xff09;进行通信&#xff0c;接收客户端的请求&#xff08;如网页请求&#xff0…

一个很强大的离线IP地址定位库和IP定位数据管理框架,支持亿级别的数据段(附源码)

前言 随着互联网的发展&#xff0c;IP地址定位成为了诸多应用中的重要一环。然而&#xff0c;现有的许多定位库在面对大规模数据时&#xff0c;往往会遇到查询速度慢、内存消耗大等问题。有没有一款工具能够处理这些问题&#xff0c;为开发者提供一个更为高效、灵活的定位库呢…

光伏储能直流监测系统的研发

摘要&#xff1a;在“双碳”战略背景下&#xff0c;光伏技术成为光伏与建筑两大行业关注的焦点。在建设中引入光电、储能、微网是目前的一个发展趋势&#xff0c;受接入资源的多样性和不连续的限制,合理控制、优化分配资源成为一项难题。研究建立一个智能微电网控制系统,利用集…

django数字化时代的新能源汽车供应链优化与可视化-计算机毕业设计源码91188

摘要 本研究旨在探讨利用Django框架进行新能源汽车供应链优化与可视化的应用。随着数字化时代的到来&#xff0c;新能源汽车产业迅速发展&#xff0c;供应链管理变得至关重要。通过结合Django框架的强大功能&#xff0c;我们可以优化新能源汽车供应链的各个环节&#xff0c;实现…

Python基础 | 在虚拟环境中安装并在指定文件夹中打开Jupyter notebook

在虚拟环境中安装并在指定文件夹中打开Jupyter notebook 前言一、在虚拟环境下安装Jupyter notebook二、在指定路径下打开Jupyter notebook 前言 Jupyter Notebook 是一个基于 Web 的交互式计算环境&#xff0c;主要功能是将代码、文本、数学方程式、可视化和其他相关元素组合…

线性规划------ + 案例 + Python源码求解(见文中)

目录 一、代数模型&#xff08;Algebraic Models&#xff09;详解1.1什么是代数模型&#xff1f;1.2代数模型的基本形式1.3 安装所需要的Python包--运行下述案例1.4代数模型的应用案例案例 1&#xff1a;市场供需平衡模型Python求解代码Python求解结果如下图&#xff1a; 案例 …