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

news2024/11/13 23:24:57

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)

算法分析:

时间复杂度:O(nlogn)

空间复杂度为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 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;
}

2.9 给予比较和交换的排序算法总结

排序算法时间复杂度空间复杂度稳定性
冒泡排序O(n^2^)O(1)稳定
选择排序O(n^2^)O(1)不稳定
插入排序O(n^2^)O(1)稳定
希尔排序O(n1.5)O(1)不稳定
快速排序O(nlogn)O(logn)不稳定
归并排序O(nlogn)O(n)稳定
堆排序O(nlogn)O(1)不稳定

常用的时间复杂度关系:O(1)<O(logn)<O(n)<O(nlogn)<O(n^2^)<O(n^3^)<O(2^n^)<O(n!)<O(n^n^)

2.10 计算排序(Counting Sort)

计数排序不基于元素的比较,而是一种利用数组下标来确定元素正确位置的算法。

方法如下:在一个有确定范围的整数空间中,建立一个长度更大的辅助数组, 例如:当待排序的元素是n个o到k之间的整数时,建立一个长度为k+1的辅助数组,该数组的每一个下标位置的值,代表了原始数组中对应的整数出现的次数。根据这个统计结果,直接遍历辅助数组,输出数组元素的下标值,元素的值是几,就输出几次。

局限性:

  1. 当数组中最大值和最小值差距过大时,不适合使用计数排序,因为会造成空间浪费;

  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 countingSort(int arr[], int len)
{
	int max = arr[0];
	for (int i = 1; i < len; i++)
	{
		if (arr[i] > max)
		{
			max = arr[i];
		}
	}
	//建立一个辅助数组
	int* temp = new int[max + 1];
	for (int i = 0; i < max + 1; i++)
	{
		temp[i] = 0;
	}
	//统计每个元素出现的次数
	for (int i = 0; i < len; i++)
	{
		temp[arr[i]]++;
	}
	//排序数组,某个数出现了几次,就把它放到新数组中几次
	for (int i = 0, j = 0; i < max + 1; i++)
	{
		for (int k = 0; k < temp[i]; k++)
		{
           arr[j++] = i;
		}
	}
	delete[] temp;
    temp = nullptr;
}

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);
	countingSort(a, length);
	printArry(a, length);
	return 0;
}

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

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

相关文章

CentOS 7 YUM源不可用

CentOS 7 操作系统在2024年6月30日后将停止官方维护&#xff0c;并且官方提供的YUM源将不再可用。 修改&#xff1a;nano /etc/yum.repos.d/CentOS-Base.repo # CentOS-Base.repo [base] nameCentOS-$releasever - Base baseurlhttp://mirrors.aliyun.com/centos/$rel…

数据库管理-第243期 云栖有感:AI?AI!(20240922)

数据库管理243期 2024-09-22 数据库管理-第243期 云栖有感&#xff1a;AI&#xff1f;AI&#xff01;&#xff08;20240922&#xff09;1 AI2 干货3 数据库总结 数据库管理-第243期 云栖有感&#xff1a;AI&#xff1f;AI&#xff01;&#xff08;20240922&#xff09; 作者&am…

Apache 中间件漏洞

CVE-2021-41773 环境搭建 docker pull blueteamsteve/cve-2021-41773:no-cgid 访问172.16.1.4:8080 使⽤curl http://172.16.1.4:8080/cgi-bin/.%2e/.%2e/.%2e/.%2e/etc/passwd

Linux中的调度算法

nice值的范围有限&#xff0c;即为[-20, 19]&#xff0c;也就是40个数字&#xff0c;优先级为[60, 99]即一共40个优先级 目前谈论的Linux操作系统叫做分时操作系统&#xff0c;调度的时候主要强调公平&#xff0c;还有一种是实时操作系统&#xff0c;比如智能汽车里面必须装有这…

网站设计中安全方面都需要有哪些考虑

网站设计中的安全性是一个多方面的问题&#xff0c;需要从多个角度进行考虑和实施。以下是一些关键的安全考虑因素&#xff1a; 数据加密&#xff1a; 使用SSL&#xff08;安全套接字层&#xff09;证书来建立加密连接&#xff0c;确保数据在传输过程中不被截获。定期更新SSL证…

学习IEC 62055付费系统标准

1.IEC 62055 国际标准 IEC 62055 是目前关于付费系统的唯一国际标准&#xff0c;涵盖了付费系统、CIS 用户信息系统、售电系统、传输介质、数据传输标准、预付费电能表以及接口标准等内容。 IEC 62055-21 标准化架构IEC 62055-31 1 级和 2 级有功预付费电能表IEC 62055-41 STS…

【重学 MySQL】三十七、聚合函数

【重学 MySQL】三十七、聚合函数 基本概念5大常用的聚合函数COUNT()SUM()AVG()MAX()MIN() 使用场景注意事项示例查询 聚合函数&#xff08;Aggregate Functions&#xff09;在数据库查询中扮演着至关重要的角色&#xff0c;特别是在处理大量数据时。它们能够对一组值执行计算&a…

波分技术基础 -- Liquid OTN

什么是Liquid OTN 传统OTN技术主要定位于骨干网和城域网应用&#xff0c;主要用于承载大于1Gbits/s速率业务&#xff0c;在OTN下沉到城域/接入网后&#xff0c;面临如下问题&#xff1a;管道弹性不足&#xff08;最小管道ODU0&#xff09;、连接数少、带宽调整不够灵活等挑战。…

最强AI人脸高清修复

效果展示 大家好&#xff0c;今天给大家带来github上超火的人脸高清修复AI技术code former&#xff0c;算法不仅能够修复图像&#xff0c;还能够对视频进行修复 再上一些高模糊的图像&#xff0c;测试一下算法效果&#xff1a; 怎么样&#xff0c;效果是不是非常强大。 算…

正点原子RK3588(二)——lenet测试和modelzoo模型

文章目录 一、lenet二、modelzoo模型2.1 介绍2.2 测试 一、lenet import cv2 import numpy as np from rknnlite.api import RKNNLite RKNN_MODEL LeNet5_mnist_model.rknndef show_top5(result):output result[0].reshape(-1)output_sorted sorted(output, reverseTrue)to…

微信小程序教程:如何在个人中心实现头像贴纸功能

在微信小程序中&#xff0c;个性化设置是提升用户体验的重要手段。本文将详细介绍如何在个人中心模块中实现头像贴纸功能&#xff0c;让用户可以自由地装饰自己的头像。 头像贴纸功能允许用户在个人头像上添加装饰性贴纸&#xff0c;增加个性化表达。以下是实现该功能的主要步骤…

设计模式-行为型模式-命令模式

1.命令模式的定义 命令模式将请求封装为一个对象&#xff0c;这样可以使用不同的请求参数化其他对象&#xff08;将不同请求依赖注入到其他对象&#xff09;&#xff0c;并且能够支持请求的排队执行、记录日志、撤销等功能&#xff1b; 1.1 命令模式优缺点 优点 降低系统的耦…

独立站如何批量查收录,如何进行独立站的批量收录查询的详细操作

对于独立站&#xff08;即自主搭建的网站&#xff09;进行批量收录查询&#xff0c;是网站优化与管理中的一项重要工作&#xff0c;它有助于了解网站在搜索引擎中的表现情况&#xff0c;从而制定更有效的SEO策略。介绍如何进行独立站的批量收录查询&#xff1a; 一、准备阶…

C++:数组与字符串

一、数组 数组是一种存储若干元素的数据类型&#xff0c;在诸多编程语言中存在&#xff0c;其显著的特点是元素通常是在物理层面上连续存储的&#xff08;逻辑上的数组&#xff0c;比如链表&#xff0c;可能不是&#xff09;&#xff0c;并且具有极快的元素访问速度。 数组通常…

Nuxt Kit API :路径解析工具

title: Nuxt Kit API :路径解析工具 date: 2024/9/22 updated: 2024/9/22 author: cmdragon excerpt: 摘要:本文介绍了Nuxt Kit中用于解析路径的API工具,包括resolvePath、resolveAlias、findPath和createResolver。这些工具助力开发者处理模块路径、别名、文件扩展名,提…

跨站请求伪造(CSRF)漏洞详解

免责申明 本文仅是用于学习检测自己搭建的DVWA靶场环境有关CSRF的原理和攻击实验,请勿用在非法途径上,若将其用于非法目的,所造成的一切后果由您自行承担,产生的一切风险和后果与笔者无关;本文开始前请认真详细学习《‌中华人民共和国网络安全法》‌及其所在国家地区相关法…

你了解system V的ipc底层如何设计的吗?消息队列互相通信的原理是什么呢?是否经常将信号量和信号混淆呢?——问题详解

前言&#xff1a;本节主要讲解消息队列&#xff0c; 信号量的相关知识。 ——博主主要是以能够理解为目的进行讲解&#xff0c; 所以对于接口的使用或者底层原理很少涉及。 主要的讲解思路就是先讨论消息队列的原理&#xff0c; 提一下接口。 然后讲解ipc的设计——这个设计一些…

【Godot4.3】点数据简易表示法和Points2D

概述 在构造多点路径时我们会用到PackedVector2Array&#xff0c;并使用Vector2()来构造点。在手动创建多点数据时&#xff0c;这种写法其实很难看&#xff0c;有大量重复的Vector2()&#xff0c;比如下面这样&#xff1a; var points [Vector2(100,100),Vector2(200,200),V…

240922-MacOS终端访问硬盘

A. 最终效果 B. 操作步骤 在macOS中&#xff0c;可以通过命令行使用Terminal访问硬盘的不同位置。你可以按照以下步骤操作&#xff1a; 打开终端&#xff08;Terminal&#xff09;&#xff1a; 在应用程序中打开终端&#xff0c;或者使用 Spotlight 搜索“Terminal”来启动。 …

WebLogic 靶场攻略

后台弱⼝令GetShell 步骤一&#xff1a;环境部署 cd vulhub-master/weblogic/weak_password docker-compose up -d docker ps 步骤二&#xff1a;漏洞复现 默认账号密码&#xff1a;weblogic/Oracle123 步骤二&#xff1a;进行登录 http://192.168.10.190:7001/console/…