数据结构----算法--排序算法

news2024/11/25 21:40:26

数据结构----算法–排序算法

一.冒泡排序(BubbleSort)

1.冒泡排序的核心思想

相邻两个元素进行大小比较,如果前一个比后一个大,就交换

注意:

在冒泡排序的过程中,促进了大的数往后去,小的数往前去

2.冒泡排序的实现步骤

1.从头开始将相邻两个元素进行大小比较,如果前一个比后一个大,就交换,直到全部比较完成,固定最右边的值(此时固定的就是最大的值)

2.重复操作1,直到最后剩一个元素了,排序结束

3.看下图冒泡排序的过程,加深理解

在这里插入图片描述

4.冒泡排序的代码实现如下

void BubbleSort(int a[],int length) {
	if (a == NULL || length <= 0) return;
	for (int i = 0; i < length -1; i++) {
		for (int j = 0; j < length -1-i; j++) {
			if (a[j] > a[j + 1]) {
				a[j] = a[j] ^ a[j + 1];
				a[j + 1] = a[j] ^ a[j + 1];
				a[j] = a[j] ^ a[j + 1];
			}
		}
	}
}

void Print(int a[], int length) {
	if (a == NULL || length <= 0) return;
	for (int i = 0; i < length; i++) {
		cout << a[i] << "  ";
	}
	cout << endl;
}

int main() {
	int a[] = { 3,11,9,2,16,8,20,28,31,44 };
	BubbleSort(a,sizeof(a)/sizeof(a[0]));
    //将排好序的数组进行输出
	Print(a,sizeof(a)/sizeof(a[0]));
	cout << endl;
	return 0;
}

5.冒泡排序的优化

在这里插入图片描述

我们发现第二趟完事之后,这个数组就已经有序了,我们就不需要再进行下面几次的操作了

1.优化的思路

我们在进行冒泡排序时,每一趟都找到最后交换的位置,那下一趟的右端范围到最后交换位置之前就可以了(用这种方法优化之后冒泡排序所需要的次数一般都会大幅减少),如果没有交换发生,就说明此数组已经有序了

2.对上面优化方法的两种实现方式
方式一:通过改变总趟数(改变总趟数的同时每一次的范围也会进行改变)来进行代码的优化(此方法需要用到数学进行计算),下面为方法一要注意的一些问题

当缩小范围(减少趟数)的时候,也就是对第一个循环进行的操作,我们控制的是左端点,右端点不动,并且通过数学的计算得出每次左端点要变为 数组长度 - 最后交换的位置在数组中的下标所得的值

方式二:通过直接对范围进行修改(不用管趟数,因为当当前范围中没有发生交换,那么冒泡排序就会结束)
3.优化的代码如下(这里用方法一实现)
#include<iostream>
using namespace std;

void BubbleSort(int a[],int length) {
	if (a == NULL || length <= 0) return;
	int flag = 0;
	for (int i = 0; i < length -1; i++) {//总趟数
		flag = 0;
		for (int j = 0; j < length -1-i; j++) {//每趟的范围
			if (a[j] > a[j + 1]) {
				a[j] = a[j] ^ a[j + 1];
				a[j + 1] = a[j] ^ a[j + 1];
				a[j] = a[j] ^ a[j + 1];
				flag = j + 1;
			}
		}
		if (flag == 0) break;//如果没有交换发生,就说明此数组已经有序了,结束操作
		i = length - flag - 1;//这里多减了一个一是因为每一次循环i都会自增1
	}
}

void Print(int a[], int length) {
	if (a == NULL || length <= 0) return;
	for (int i = 0; i < length; i++) {
		cout << a[i] << "  ";
	}
	cout << endl;
}

int main() {
	int a[] = { 3,9,2,11,8,16,20,28,31,44 };
	BubbleSort(a, sizeof(a) / sizeof(a[0]));
    //将排好序的数组进行输出
	Print(a, sizeof(a) / sizeof(a[0]));
	return 0;
}

二.选择排序(SelectSort)

1.选择排序的核心思想

每趟找最值放入到对应位置上(整体要保持一致性要找最大就只能找最大,反则亦然)

2.选择排序的实现步骤

1.假设数组的第一个元素就是最大值,此时最大值的下标为0,然后依次进行比较,如果遇到大的数,那么最大值的下标进行更新,直到所有的元素都比较完成了,将最大值与数组最右边的数进行互换(如果最大值的下标就是数组最右边元素的下标,那么就不交换),固定数组最右边的元素(之后此位置就不参与比较了)

2.重复操作1,直到数组中只剩一个元素了,排序结束

3.选择排序的代码实现如下

#include<iostream>
using namespace std;

void SelectSort(int a[], int length) {
     if (a == NULL || length <= 0) return;
	int i;
	int j;
	for ( i= 0; i < length - 1; i++) {
		int MaxIndex = 0;
		for (j = 0; j < length - i-1; j++) {
            //与最值进行比较
			if (a[MaxIndex] <= a[j]) {
				MaxIndex = j;
			}
		}
        //将最值放到数组的最右边(通过交换实现的)
		if (MaxIndex != j-1) {//如果最大值的下标不是数组最右边元素的下标,进行交换
			a[MaxIndex] = a[j-1] ^ a[MaxIndex];
			a[j-1] = a[j-1] ^ a[MaxIndex];
			a[MaxIndex] = a[j-1] ^ a[MaxIndex];
		}
		
	}
}


void Print(int a[], int length) {
	if (a == NULL || length <= 0) return;
	for (int i = 0; i < length; i++) {
		cout << a[i] << "  ";
	}
	cout << endl;
}

int main() {

	int a[] = { 9,1,3,7,98,58,15,4,45,5,2131,23 };
	SelectSort(a, sizeof(a) / sizeof(a[0]));
    //将排好序的数组进行输出
    Print(a, sizeof(a) / sizeof(a[0]));
    return 0;
}

三.插入排序(InsertSort)

1.插入排序的核心思想

将待排序数据分为两部分,一部分无序,一部分有序,将无序元素依次插入到有序中去,完成排序

2.插入排序的适用场合

1.元素很少的时候(元素小于16时就认为元素很少)

2.每个元素在排序前的位置距排好序的最终位置不远的时候

3.插入排序的实现步骤

注意:插入排序过程中没有交换

1.申请两个标记变量,一个指向有序的最后一个,另一个指向无序的第一个元素(最开始时我们把数组第一个元素看成是有序的,其他的元素是无序的,所以最开始有序的标记变量指向的是数组的第一个元素,无序的标记变量指向的是数组的第二个元素),再申请一个临时变量,临时变量用来存每一次要进行操作的无序元素(防止无序的这个元素被覆盖)

2.插入无序元素

(1)遍历无序的元素

(2)插入每一个无序的元素时进行倒叙遍历有序数组

如果遍历到的元素的值小于无序值,那么无序值就放到当前元素的下一个位置上继续遍历无序的元素(此时无序的标记变量向后移一位,有序元素的标记指向的位置变为无序标记的指向的位置的前一位,)

如果遍历到的元素的值大于无序值,那么当前元素后向右移一位,继续倒叙遍历有序数组

如果遍历到了边界(也就是数组的起始元素),那么该无序元素变为有序元素的第一个,继续倒叙遍历有序数组

4.插入排序的代码实现如下

#include<iostream>
using namespace std;

void InsertSort(int a[],int length) {
	int yesIndex = 0;
	int noIndex = 1;
	int temp = a[noIndex];
	//依次插入无序
	while (noIndex < length&& yesIndex>=-1) {
		if (yesIndex == -1) {
			a[0] = temp;
			noIndex++;
			yesIndex = noIndex - 1;
			temp = a[noIndex];
		}

		if (a[yesIndex] > temp) {
			a[yesIndex+1] = a[yesIndex];
			yesIndex--;
		}
		else {
			a[yesIndex+1] = temp;
			noIndex++;
			yesIndex = noIndex - 1;
			temp = a[noIndex];
		}

	}

}


int main() {
	int a[] = { 5,1,856,6,32,14,7,9,77 };
	InsertSort(a, sizeof(a) / sizeof(a[0]));
	for (int i = 0; i < sizeof(a) / sizeof(a[0]); i++) {
		cout << a[i] << "  ";
	}
	return 0;
}

四.希尔排序(ShellSort)(也叫缩小增量排序)

希尔排序可以理解成放大版的插入排序

1.希尔排序的实现步骤

1.定间隔(造图时用的时二分,实际生产应用中会和斐波那契数列有关系这里我们用二分来确定间隔)

(1)初始定间隔:我们用数组的总长度除以2得到的结果向下取整,然后我们以此结果作为间隔,执行第二步

(2)重新定间隔:间隔变为间隔减半的结果向下取整(这里除2了,但是真正使用的时候不是除2的)执行第二步

2.将数组中的元素根据间隔进行分组(例如如果所得的间距为6,那么下标为0的就和下标为6的一组,下标为1的就和下标为7的一组)

3.各组内进行插入排序

4.如果排序还没有完成的话,(我们需要重新定间隔,重新分组),从第一步的(2)重新开始

如果排序完成,结束

2.希尔排序的时间复杂度

希尔排序的时间复杂度是无法计算的,一般会有一个范围在n的1.2次方到n的1.5次方,我们一般取n的1.3次方

五.计数排序(CountingSort)

1.计数排序的核心思想

核心思想基于非比较

2.计数排序的使用场合

数据是有范围的(分配比较密集),数据重复出现次数比较多

3.计数排序的实现步骤

1.先遍历一遍数组找到该数组的最大值和最小值

2.申请一个计数数组,它的长度是上一步找的最大值和最小值的差值+1,并将计数数组所有元素初始化为0

3.遍历原数组,进行计数

遍历到的每一个元素的值减去步骤1中获得的最小值作为计数数组的下标,然后计数数组的对应下标进行+1操作,以此实现计数

4.放回操作

遍历计数数组,当遍历到的计数数组的元素不为0时,将当前元素所在的下标加上步骤1中获得的最小值得到的值依次放回到原数组中(覆盖操作)

4.计数排序的代码如下

#include<iostream>

using namespace std;

void CountingSort(int a[],int length) {
	//找到最大值和最小值
	int min = a[0];
	int max = a[0];
	for (int i = 0; i < length; i++) {
		if (min > a[i]) {
			min = a[i];
		}
		if (max < a[i]) {
			max = a[i];
		}
	}

	//申请array数组用来计数
	int* array = (int*)malloc(sizeof(int) * (max - min + 1));
	memset(array, 0, sizeof(int)*(max - min+1));
	
	//用array数组进行计数
	for (int i = 0; i < length; i++) {
		array[a[i] - min]++;
	}

	//开始排序,放回
	int t = 0;
	for (int i = 0; i < max - min + 1 ; i++) {
		while (array[i]) {
			a[t] = i + min;
			t++;
			array[i]--;
		}
	}
}

void Print(int a[], int length) {
	if (a == NULL || length <= 0) return;
	for (int i = 0; i < length; i++) {
		cout << a[i] << "  ";
	}
	cout << endl;
}

int main() {
	int a[] = { 2,9,4,1,8,9,3,1,4,7,6,9,4 };
	CountingSort(a, sizeof(a) / sizeof(a[0]));
	Print(a, sizeof(a) / sizeof(a[0]));
	return 0;
}

5.计数排序的优化

1.为什么要进行优化

因为如果元素时结构体的话,比如学生这个结构体,包括学生分数,学生姓名,学号等等,那么我们没进行优化之前只能把学生分数进行排序,那么就会出现信息不匹配的问题,所以我们要进行优化,优化的方法就是再申请一个数组,在这个数组中进行排序(此数组复制了一份原来数组中的元素),然后将排好序的元素放回到原数组中

2.优化的计数排序实现的步骤

1.先遍历一遍数组找到该数组的最大值和最小值

2.申请一个计数数组,它的长度是上一步找的最大值和最小值的差值+1,并将计数数组所有元素初始化为0

3.遍历原数组,进行计数

遍历到的每一个元素的值减去步骤1中获得的最小值作为计数数组的下标,然后计数数组的对应下标进行+1操作,以此实现计数

4.遍历一遍计数数组,计算名次

计数数组中的每一个元素都跟前一个元素相加,依次来得到名次

(计数数组的每一个元素记录的名次是当前元素最后一次出现时的排名)

5.申请一个新数组,在这个新数组中进行排序

倒叙遍历元素组,每一个元素减去第一步中的最小值获得的值找到每一个元素在计数数组中的位置,然后获得该元素的名次,从而找到它在新数组中的位置,最后复制这个元素到该位置(注意,每次获得名次后,该名次应进行减1操作,为了下一次有相同的值的元素,能够继续成功排序)

6.排好序的元素,放回到原数组中

7.释放申请的空间

3.优化的计数排序的代码如下
#include<iostream>
using namespace std;

void CountingSort(int a[],int length) {
	//找到最大值和最小值
	int min = a[0];
	int max = a[0];
	for (int i = 0; i < length; i++) {
		if (min > a[i]) {
			min = a[i];
		}
		if (max < a[i]) {
			max = a[i];
		}
	}

	//申请array数组用来计数
	int* array = (int*)malloc(sizeof(int) * (max - min + 1));
	memset(array, 0, sizeof(int)*(max - min+1));
	
	//用array数组进行计数
	for (int i = 0; i < length; i++) {
		array[a[i] - min]++;
	}


	//排名次
	for (int i = 1; i < max - min + 1; i++) {
		array[i] += array[i - 1];
	}

	//申请一个新空间
	int* Temp = (int*)malloc(sizeof(int) * length);
	memset(Temp, 0, sizeof(int) * (max - min + 1));

	//倒叙遍历原数组,进行排序
	for (int i = length - 1; i >= 0; i--) {
		Temp[array[a[i] - min] - 1] = a[i];
		array[a[i] - min]--;
	}
	//放回
	for (int i = 0; i < length; i++) {
		a[i] = Temp[i];
	}

	//释放
	free(Temp);
	Temp = NULL;
	free(array);
	array = NULL;
}


int main() {
	int a[] = { 2,9,4,1,8,9,3,1,4,7,6,9,4 };
	CountingSort(a, sizeof(a) / sizeof(a[0]));
	for (int i = 0; i < sizeof(a) / sizeof(a[0]); i++) {
		cout << a[i] << "  ";
	}
	return 0;
}

六.快速排序(QuickSort)

1.快速排序的核心思想

找一个标准值,将比标准值小的都放在其左侧,比标准值大的,都放在其右侧,根据标准值将数据分割成两部分,两部分分别重复以上操作

2.快速排序实现的方法是挖坑填补法

操作步骤如下:

1.选定标准值(标准值在某些算法里也叫枢轴),然后保存一下标准值,此处变为坑(注意这里我们选择数组的首元素作为标准值)

2.从右向左找比标准值小的,找到填入到前面的坑中,此处变为坑

从左向右找比标准值大的,找到之后填到后面的坑中,此处变为坑

直到从右向左和从左向右相遇的时候,此操作结束,此位置就是标准值的位置,放入标准值

3.根据标准值的部分,将这组数据分为两部分,左部分从起始位置到标准值左边的位置,右部分从标识值右边的位置到结束位置,然后将这两部分重新进行操作(注意如果某一部分只有一个元素或者没有元素那么就不用进行操作了,因为如果只有一个元素那它一定是有序的,而如果没有元素那就根本不需要进行排序)

3.快速排序的代码如下

#include<iostream>
using namespace std;


int Sort(int arr[],int begin, int end) {

	int temp = arr[begin];

	while (begin< end) {
		//从右向左找比标记值小的元素
		while (begin < end) {
			if (arr[end] < temp) {
				//填入左侧坑
				arr[begin] = arr[end];
				begin++;
				break;
			}
			end--;
		}

		//从左向右找比标记值大的元素
		while (begin < end) {
			if (arr[begin] > temp) {
				//填入右侧坑
				arr[end] = arr[begin];
				end--;
				break;
			}
			begin++;
		}
	}
	//将标准值放入
	arr[begin] = temp;
	return begin;

}

void QuickSort(int arr[], int begin, int end) {
	if (arr == NULL || begin >= end) return;
	//标准值
	int standard;
	standard = Sort(arr, begin, end);

	//拆分
	QuickSort(arr, begin, standard - 1);
	QuickSort(arr, standard + 1, end);
}

void Print(int a[], int length) {
	if (a == NULL || length <= 0) return;
	for (int i = 0; i < length; i++) {
		cout << a[i] << "  ";
	}
	cout << endl;
}

int main() {

	int a[] = { 5,6,9,74,5,2,3,84,39,6,21,65,1,77 };
	int begin = 0;
	int end = sizeof(a) / sizeof(a[0]);

	QuickSort(a, begin, end - 1);
	//遍历
	Print(a, sizeof(a) / sizeof(a[0]));
	return 0;
}

4.快速排序的另一种实现方法区间分割法

1.此方法的好处

较于瓦克填补法能够使程序编译期的所用时间变短

2.此方法的操作步骤如下:

1.选定标准值(标准值在某些算法里也叫枢轴),然后保存一下标准值(注意这里我们选择数组的尾元素作为标准值)

2.申请一个小空间变量,存的是小于标准值元素(小空间)的最后一个下标(最开始的值是起始位置-1所得到的值)

3.从小空间最后一个下标,遍历,如果遍历到的元素比标准值小的话,那么元素与小空间最后一个元素的下一个元素进行交换,小空间变量进行+1操作,当遍历完所有元素(除最后一个元素)之后,最后一个元素(标准值)与小空间最后一个元素的下一个元素进行交换,小空间变量进行+1操作

4.根据标准值的部分,将这组数据分为两部分,左部分从起始位置到标准值左边的位置,右部分从标识值右边的位置到结束位置,然后将这两部分重新进行操作(注意如果某一部分只有一个元素或者没有元素那么就不用进行操作了,因为如果只有一个元素那它一定是有序的,而如果没有元素那就根本不需要进行排序)

3.此方法代码如下
#include<iostream>
using namespace std;


int Sort(int arr[], int begin, int end) {

	int small = begin - 1;

	//遍历
	for (begin; begin < end; begin++) {
		//比较
		if (arr[begin] < arr[end]) {
			//小区间扩张
			if (++small != begin) {
				arr[small] = arr[small] ^ arr[begin];
				arr[begin] = arr[small] ^ arr[begin];
				arr[small] = arr[small] ^ arr[begin];
			}
		}
	}
	//放入
	if (++small != end) {
		arr[small] = arr[small] ^ arr[end];
		arr[end] = arr[small] ^ arr[end];
		arr[small] = arr[small] ^ arr[end];
	}
	return small;
}

void QuickSort(int arr[], int begin, int end) {
	if (arr == NULL || begin >= end) return;
	//标准值
	int standard;
	standard = Sort(arr, begin, end);

	//拆分
	QuickSort(arr, begin, standard - 1);
	QuickSort(arr, standard + 1, end);
}

int main() {

	int a[] = { 5,6,9,74,5,2,3,84,39,6,21,65,1,77 };
	int begin = 0;
	int end = 14;

	QuickSort(a, begin, end - 1);
	//遍历
	for (int i = 0; i < 14; i++) {
		cout << a[i] << "  ";
	}

	return 0;
}

5.快速排序的的优化(基于挖坑填补法和区间分割法的优化)

1.标准值选取的优化
1.如何进行标准值的选取
方法一

3选1(选起始位置,中间位置,结束位置,然后找到三者中的中间值作为标准值)

方法二(适用于数据量大的)

9选1(选九个位置,然后每三个再选一个中间值,然后得到的这三个中间值作为一组再在其中选一个中间值作为标准值)

2.针对于数据比较大,且标准值重复率很高时使用的优化
1.如何进行优化

将标准值进行聚集

2.如何进行聚集

如果遍历到的元素的值和标准值一样大的话,就放在最前/最后的位置,最后确定完标准值的位置之后,将这些元素放到标准值左侧/右侧,形成聚集

3.当数据元素分割到较少的时候使用插入排序(元素<16时认为元素较少)

6.快速排序的再次优化

1.采用循环+额外存储空间的方式取代快排时的递归
2.使用尾递归(了解)

7.数据有序对于快排来说是最坏的场合

七.归并排序(MergeSort)

1.归并排序的核心思想

将多个有序数组进行合并

2.归并排序的实现步骤

1.找到数组中间位置

2.根据中间位置进行拆分,使其变成有序的

左半部分从起始部分到中间位置

右半部分从中间向右一位的位置到结束位置

注意:某一部分中的元素只有一个时,那这一部分就是有序的

3.将两个有序的部分合并

注意:这里我们会申请数组,进行元素的处理,当全部处理完成后,我们把处理完的元素放入到原数组中

(1)从头开始遍历两个部分,进行比较,谁小谁先放进申请的数组中

(2)当有一个部分遍历完成后,看另外一部分是否遍历完成,没完成就将还没遍历的部分放进数组中

(3)把在申请的数组中处理完的元素放入到原数组中

3.代码如下

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void Sort(int arr[],int res[],int begin, int end) {

	if (begin == end) {
		res[0] = arr[begin];
		return;
	}
	int mid = (begin + end) >> 1;

	int* res1 = (int*)malloc(sizeof(int) * (mid - begin + 1));
	memset(res1, 0, sizeof(int) * (mid - begin + 1));
	int* res2 = (int*)malloc(sizeof(int) * (end - mid));
	memset(res1, 0, sizeof(int) * (end - mid));
	Sort(arr, res1, begin, mid);
	Sort(arr, res2, mid+1, end);

	//合并
	int i = 0;
	int index1 = 0;
	int index2 = 0;
	int mid1 = mid;
	while (begin <= mid1 && end >= mid + 1) {
		if (res1[index1] < res2[index2]) {
			res[i] = res1[index1];
			i++;
			begin++;
			index1++;
		}
		else {
			res[i] = res2[index2];
			i++;
			mid++;
			index2++;
		}
	}
	
	if (begin > mid1) {
		for (int j = index2; j <= end-mid1+1; j++) {
			res[i] = res2[j];
			i++;
		}
	}

	if (mid + 1 > end) {
		for (int j = index1; j <= mid1; j++) {
			res[i] = res1[j];
			i++;
		}
	}

	//释放空间
	free(res1);
	free(res2);
}

void MergeSort(int arr[],int begin,int end) {
	if (arr == NULL||begin >= end) return;
	//申请一个数组
	int* a = (int*)malloc(sizeof(int) * (end - begin + 1));
	memset(a, 0, sizeof(int) * (end - begin + 1));
	//排序
	Sort(arr,a,begin,end);
	for (int i = begin; i <= end; i++) {
		arr[i] = a[i];
	}
	free(a);
}


void Print(int a[], int length) {
	if (a == NULL || length <= 0) return;
	for (int i = 0; i < length; i++) {
		printf("%d ", a[i]);
	}
	printf("\n");
}

int main() {
	int a[] = { 1,57,56,3,23,4,65,123,19,46,88,5};
	int begin = 0;
	int end = sizeof(a) / sizeof(a[0]);
	MergeSort(a, begin, end-1);
	Print(a, end);
	return 0;
}

八.堆排序(HeapSort)

1.堆排序的功能

堆排序可以动态维护一组数据内的最值

2.堆的分类

1.大顶堆
定义

在整棵二叉树中,父亲和左右孩子三者中,父亲都是最大值,就被称为大顶堆

2.小顶堆
定义

在整棵二叉树中,父亲和左右孩子三者中,父亲都是最小值,就被称为小顶堆

3.堆排序的实现步骤

注意:

堆排序中我们用到了完全二叉树的一些逻辑,一颗完全二叉树按照从上到下从左至右从0开始编号(0~n-1),编号为i的节点

如果满足2*i+1<=n-1,则其有左孩子,否则没有

如果满足2*i+2<=n-1,则其有右孩子,否则没有

父亲节点的编号是0~2分之n-1

操作步骤:

1.建初始堆(这里建大堆)

从数组的n/2-1到0依次调整各个父亲节点

每个父亲节点调整的方式:看父亲节点和左右孩子中的最大值进行比较,如果比最大值小的话,父亲节点的值就和左右孩子中那个较大的那个进行交换,然后那个进行交换的孩子节点变为新的父亲节点继续讨论,如果新的父亲节点没有左右孩子或者没有发生交换那么结束

2.进行排序

(1)堆顶元素与末尾元素进行交换

(2)调整堆顶

调整堆顶的方式和上面的调整方式相同

4.堆排序的代码如下

#include<stdio.h>

void adjust(int arr[], int index, int length) {
	if (index * 2 + 1 >= length && index * 2 + 2 >=length) {
		return;
	}

	int weizhi = 0;
	//看当前节点左孩子,和右孩子,取它们大的那个
	int Max = 0;
	//如果有左孩子和右孩子
	if (index * 2 + 1 < length&& index * 2 + 2 < length) {
		if (arr[index * 2 + 1] > arr[index * 2 + 2]) {
			Max = arr[index * 2 + 1];
			weizhi = index * 2 + 1;
		}
		else {
			Max = arr[index * 2 + 2];
			weizhi = index * 2 + 2;
		}

	}
	//如果只有左孩子
	if (index * 2 + 1 <= length && index * 2 + 2 > length) {
		Max = arr[index * 2 + 1];
		weizhi = index * 2 + 1;
	}

	//看这个值是否比当前节点大,大的话进行交换,然后讨论交换的那个节点
	if (Max > arr[index]) {
		arr[index] = arr[index] ^ arr[weizhi];
		arr[weizhi] = arr[index] ^ arr[weizhi];
		arr[index] = arr[index] ^ arr[weizhi];
		adjust(arr, weizhi, length);
	}

}

void HeapSort(int arr[],int length) {
	if (arr == NULL || length <= 0) return;
	//1.建初始堆
	for (int i = length / 2 - 1; i >=0 ; i--) {
		adjust(arr, i, length);
	}

	//2.排序
	for (int i = length-1; i > 0; i--) {
		//进行交换
		arr[0] = arr[0] ^ arr[i];
		arr[i] = arr[0] ^ arr[i];
		arr[0] = arr[0] ^ arr[i];
		adjust(arr, 0, i );
	}
}

void Print(int a[], int length) {
	if (a == NULL || length <= 0) return;
	for (int i = 0; i < length; i++) {
		printf("%d ", a[i]);
	}
	printf("\n");
}

int main() {
	int arr[] = { 4, 6, 2, 1, 8, 78, 2123, 48, 49, 58, 11, 9 };
	int length = sizeof(arr) / sizeof(arr[0]);
	HeapSort(arr, length);
	Print(arr, length);
	return 0;
}

九.桶排序(BucketSort)

1.桶排序的实现步骤

1.找到这组数据的最大值和最小值

2.确定分组方式

3.申请一个指针数组

4.进行入组(头插法放入节点)

5.组内进行排序

6.放回到原数组中

7.释放

十.基数排序(RadixSort)

1.计数排序分为两种

1.MSD(高位优先)

2.LSD (低位优先)

2.基数排序的实现步骤

1.找到这组数据中的最大值

2.计算最大值的位数

3.按位处理(这是个循环,循环的次数取决于位数)(这里用的是LSD)

(1)申请组(申请10组,因为十进制是0~9)

(2)拆位(先个位在十位再百位…)

(3)数据进行入组操作(数据根据 当前拆位(个位,十位,百位等)的值进行分组)

(4)将数据放回到原数组中

(5)释放申请组的空间

十一.关于排序的时间复杂度,空间复杂度,稳定性

1.排序稳定性

1.稳定性的概念

数值相同的元素,在排序前后,其相对位置未发生改变

2.关于排序的时间复杂度,空间复杂度,稳定性的表格如下

在这里插入图片描述

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

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

相关文章

2023年中国功率半导体分立器件发展趋势分析:产品向高效率、低成本发展[图]

功率半导体分立器件是指被规定完成某种基本功能&#xff0c;并且本身在功能上不能再细分的半导体器件。功率半导体分立器件的应用几乎覆盖所有的电子制造业&#xff0c;传统应用领域包括消费电子、网络通讯等&#xff1b;近年来&#xff0c;汽车电子及充电系统、新能源发电等领…

log4j2同步日志引发的性能问题 | 京东物流技术团队

1 问题回顾 1.1 问题描述 在项目的性能测试中&#xff0c;相关的接口的随着并发数增加&#xff0c;接口的响应时间变长&#xff0c;接口吞吐不再增长&#xff0c;应用的CPU使用率较高。 1.2 分析思路 谁导致的CPU较高&#xff0c;阻塞接口TPS的增长&#xff1f;接口的响应时…

零基础实战部署云服务器项目-服务器部署(window server2012)

目录 什么是服务器呢&#xff1f; 项目服务器运行环境 一、申请服务器 1.找店铺 2.1选配置 型号 2.2配置&#xff08;具体配置&#xff09; 3.下单 4.收货 情况一&#xff1a;弹窗有进入控制台 情况二&#xff1a;手快关闭了窗口 所以下一步我们远程连接 方法二&#xff1a;win…

实验室安全巡检管理系统—全面安全检查

安全巡检管理系统可以代替传统的线下纸质巡查&#xff0c;微信扫码进行巡检记录、隐患上报、设备保养维护等工作&#xff0c;管理人员可远程实时查看巡查情况&#xff0c;同时在巡查结束后对现场问题进行闭环整改管理。大大提高现场巡查效率&#xff0c;辅助用户对巡查工作实时…

代码随想录二刷 Day 35

122.买卖股票的最佳时机 II 数组两两求差&#xff0c;然后把正数加起来 class Solution { public:int maxProfit(vector<int>& prices) {int sum0;int diff0;for(int i0;i<prices.size()-1;i){diff prices[i1] - prices[i];if(diff>0){sumdiff;}} return su…

智慧公厕设计选哪家?智慧公厕厂家创新性智慧公厕建设方案揭秘

随着人们生活水平的提高&#xff0c;公共卫生设施的建设也变得越来越重要。其中&#xff0c;智慧公厕作为一种创新的建设方案&#xff0c;备受关注。那么&#xff0c;在众多智慧公厕厂家中&#xff0c;广州中期科技有限公司的智慧公厕怎么建设&#xff1f;其中的技术方案有何独…

【广州华锐互动】利用VR开展高压电缆运维实训,提供更加真实、安全的学习环境

VR高压电缆维护实训系统由广州华锐互动开发&#xff0c;应用于多家供电企业的员工培训中&#xff0c;该系统突破了传统培训的限制&#xff0c;为学员提供了更加真实、安全的学习环境&#xff0c;提高了培训效率和效果。 传统电缆井下运维培训通常是在实际井下环境中进行&#x…

Protocols/面向协议编程, DependencyInjection/依赖式注入 的使用

1. Protocols 定义实现协议&#xff0c;面向协议编码 1.1 创建面向协议实例 ProtocolsBootcamp.swift import SwiftUI/// 颜色样式协议 protocol ColorThemeProtocol {var primary: Color { get }var secondary: Color { get }var tertiary: Color { get } }struct DefaultCol…

MySQL JSON_TABLE() 函数

JSON_TABLE()函数从一个指定的JSON文档中提取数据并返回一个具有指定列的关系表。 应用&#xff1a;数据库字段以JSON 存储后&#xff0c;实际应用需要对其中一个字段进行查询 语法 JSON_TABLE(json,path COLUMNS(column[,column[,...]]))column:name参数 json必需的。一个 …

2023年全球半导体零部件市场发展现状分析:半导体零部件行业集中度高[图]

半导体零部件指的是在材料、结构、工艺、精度和品质、稳定性及可靠性等性能方面符合半导体设备技术要求的零部件&#xff0c;如O-Ring密封圈、精密轴承、射频电源、静电吸盘&#xff08;ESC&#xff09;、MFC流量计、石英件、硅及碳化硅件等。 半导体零部件分类 资料来源&…

【JVM面试】从JDK7 到 JDK8, JVM为啥用元空间替换永久代?

系列文章目录 【JVM系列】第一章 运行时数据区 【面试】第二章 从JDK7 到 JDK8, JVM为啥用元空间替换永久代&#xff1f; 大家好&#xff0c;我是青花。拥有多项发明专利&#xff08;都是关于商品、广告等推荐产品&#xff09;。对广告、Web全栈以及Java生态微服务拥有自己独到…

【京东开源项目】微前端框架MicroApp 1.0正式发布

介绍 MicroApp是由京东前端团队推出的一款微前端框架&#xff0c;它从组件化的思维&#xff0c;基于类WebComponent进行微前端的渲染&#xff0c;旨在降低上手难度、提升工作效率。MicroApp无关技术栈&#xff0c;也不和业务绑定&#xff0c;可以用于任何前端框架。 源码地址…

如何优雅的实现接口统一调用

耦合问题 有些时候我们在进行接口调用的时候&#xff0c;比如说一个push推送接口&#xff0c;有可能会涉及到不同渠道的推送&#xff0c;以我目前业务场景为例&#xff0c;我做结算后端服务的&#xff0c;会与金蝶财务系统进行交互&#xff0c;那么我结算后端会涉及到多个结算…

第二证券:国际油价大幅上涨 后市恐难持续走高

上个买卖周&#xff0c;受巴以冲突影响&#xff0c;原油商场成为各方关注的焦点。到上星期五收盘&#xff0c;布伦特原油周内涨幅达7%以上&#xff0c;为本年2月以来最大周涨幅&#xff0c;WTI原油周内累计上涨近6%。业内人士认为&#xff0c;其时地缘要素是导致油价出现异动的…

3分钟,快速上手Postman接口测试!

Postman是一个用于调试HTTP请求的工具&#xff0c;它提供了友好的界面帮助分析、构造HTTP请求&#xff0c;并分析响应数据。实际工作中&#xff0c;开发和测试基本上都有使用Postman来进行接口调试工作。有一些其他流程的工具&#xff0c;也是模仿的Postman的风格进行接口测试工…

DDD之上下文映射图(Context Mapping)

领域驱动设计系列文章&#xff0c;点击上方合集↑ 1. 开头 在DDD中&#xff0c;限界上下文与限界上下文之间需要相互集成&#xff0c;这种集成关系在DDD中称为上下文映射&#xff08;Context Mapping&#xff09;&#xff0c;也就是子域与子域之间的集成关系。 所以首先我们…

2023年中国微创手术器械及耗材发展趋势分析:发展呈现集成化、智能化[图]

随着微创外科手术渗透率的提升&#xff0c;国内微创外科手术器械及配件&#xff08;MISIA&#xff09;市场迎来爆发&#xff0c;预计2023年微创手术器械及耗材&#xff08;MISIA&#xff09;市场规模将达到238亿元&#xff0c;其中一次性MISIA市场规模约为204亿元&#xff0c;可…

网络安全——自学(黑客技术)

前言 前几天发布了一篇 网络安全&#xff08;黑客&#xff09;自学 没想到收到了许多人的私信想要学习网安黑客技术&#xff01;却不知道从哪里开始学起&#xff01;怎么学&#xff1f;如何学&#xff1f; 今天给大家分享一下&#xff0c;很多人上来就说想学习黑客&#xff0c…

【OpenVINO】OpenVINO C# API 常用 API 详解与演示

OpenVINO C# API 常用 API 详解与演示 1 安装OpenVINO C# API2 导入程序集 3 初始化OpenVINO 运行时内核4 加载并获取模型信息4.1 加载模型4.2 获取模型信息 5 编译模型并创建推理请求6 张量Tensor6.1 张量的获取与设置6.2 张量的信息获取与设置 7 加载推理数据7.1 获取输入张量…

vue2 引用3D模型

一、首先安装对应的插件&#xff08;前往查看model-viewer&#xff09; # install peer dependency ThreeJS npm install three # install package npm install google/model-viewer二、页面直接引用组件&#xff08;例子&#xff09; <model-viewer style"width: 5…