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

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

一.冒泡排序(BubbleSort)

1.冒泡排序的核心思想

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

注意:

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

2.冒泡排序的实现步骤

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

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

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

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

c++ 复制代码
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.优化的代码如下(这里用方法一实现)
c++ 复制代码
#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.选择排序的代码实现如下

c++ 复制代码
#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.插入排序的代码实现如下

c++ 复制代码
#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.计数排序的代码如下

c++ 复制代码
#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.优化的计数排序的代码如下
c++ 复制代码
#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.快速排序的代码如下

c++ 复制代码
#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.此方法代码如下
c++ 复制代码
#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.代码如下

c 复制代码
#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.堆排序的代码如下

c 复制代码
#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.关于排序的时间复杂度,空间复杂度,稳定性的表格如下

相关推荐
良月澪二21 分钟前
CSP-S 2021 T1廊桥分配
算法·图论
wangyue41 小时前
c# 线性回归和多项式拟合
算法
&梧桐树夏1 小时前
【算法系列-链表】删除链表的倒数第N个结点
数据结构·算法·链表
QuantumStack1 小时前
【C++ 真题】B2037 奇偶数判断
数据结构·c++·算法
今天好像不上班2 小时前
软件验证与确认实验二-单元测试
测试工具·算法
wclass-zhengge2 小时前
数据结构篇(绪论)
java·数据结构·算法
Dylanioucn2 小时前
【分布式微服务云原生】探索Redis:数据结构的艺术与科学
数据结构·redis·分布式·缓存·中间件
何事驚慌2 小时前
2024/10/5 数据结构打卡
java·数据结构·算法
结衣结衣.2 小时前
C++ 类和对象的初步介绍
java·开发语言·数据结构·c++·笔记·学习·算法
大三觉醒push亡羊补牢女娲补天版2 小时前
数据结构之排序(5)
数据结构