排序(完整版)

news2024/11/14 23:28:24

目录

一、基本概念

 二、排序的分类

 三、排序算法的好坏的衡量

四、五类内排序 

 插入排序

直接插入排序 

 演示:

关键代码:

 完整代码

结果: 

 插入排序算法分析:

带哨兵的插入排序 

举例: 

 关键代码

 完整代码

折半插入排序

演示

 关键代码

完整代码

 结果

折半插入排序(监视哨)

关键代码 

完整代码

 折半插入排序的算法分析

希尔排序 (Shell Sort) 

演示                                                                                                                          

举例                                                                                                                                  

关键代码 

完整代码

 结果

 希尔排序算法分析

交换排序 

 冒泡排序(Bubble Sort)

演示  

举例 

 关键代码

 完整代码

 结果

 冒泡排序算法分析

 快速排序

关键代码 

 完整代码

 结果

 选择排序

简单选择排序 

关键代码 

完整代码

 堆积树排序法

 演示

 关键代码1

 完整代码1

 结果1 ​编辑

改进关键代码2 

 完整代码2

归并排序

演示

关键代码1 

 完整代码1

关键代码2

 完整代码2

 基数排序

关键代码 


一、基本概念

  • 排序(Sorting):排序就是将一个数据元素(或记录)的任意序列,重新排列成一个按关键字有序的序列。
  • 数据表(Data List) : 待排序的数据对象的有限集合。
  • 关键字(Key):数据元素(或记录)中某个数据项的值,用它可以标识(识别)一个数据元素(或记录)。
  • 主关键字(Primary Key) :若此关键字可以唯一地标识一个记录,则称此关键字为主关键字。
  • 次关键字(Secondary Key) :用以识别若干记录的关键字为次关键字。
  • 注意:当数据元素只有一个数据项时,其关键字即为该数据项 

 二、排序的分类

  • 增排序和减排序: 如果排序的结果是按关键字从小到大的次序排列的,就是增排序,否则就是减排序。
  • 稳定排序和不稳定排序:如果待排序的表中存在多个关键字相同的元素,采用某种排序方法排序后这些具有相同关键字的元素之间的相对次序始终保持不变,则称这种排序方法是稳定的;反之,若具有相同关键字的元素之间的相对次序发生变化,则称这种排序方法是不稳定的

  • 内部排序与外部排序:在排序中,若数据表中的所有记录的排列过程都是在内存中进行的,称为内部排序。由于待排序的记录数量太多,在排序过程中不能同时把全部记录放在内存,需要不断地通过在内存和外存之间交换数据元素来完成整个排序的过程,称为外部排序

 三、排序算法的好坏的衡量

  • 时间效率 —— 排序速度(即排序所花费的全部比较次数和移动次数,一般都按平均时间复杂度进行估算;对于那些受数据表中记录的初始排列及记录数目影响较大的算法,按最好情况和最坏情况分别进行估算。 )
  • 空间效率 ——  占内存辅助空间的大小(若排序算法所需的辅助空间不依赖问题的规模n,即空间复杂度是O(1) ,则称排序方法是就地排序,否则是非就地排序。)
  • 稳 定 性 —— 若两个记录A和B的关键字值相等,但排序后A、B的先后次序保持不变,则称这种排序算法是稳定的。

四、五类内排序 

  • 插入排序(直接插入排序、希尔排序)
  • 交换排序(冒泡排序、快速排序)
  • 选择排序(简单选择排序、堆排序)
  • 归并排序
  • 基数排序 

 插入排序

直接插入排序 

  • 基本思想:当插入第i个对象时,前面的R[1], R[2], …, R[i - 1]已经排好序,此时,用R[i]的关键字与R[i - 1], R[i - 2], …的关键字顺序进行比较,找到插入位置即将R[i]插入,原来位置上的对象向后顺移。
  •  演示:

  • 关键代码:

template <typename T>
void Swap(T& a, T& b)//交换
{
	T tmp;
	tmp = a;
	a = b;
	b = tmp;
}

template <typename T>
void Inser(T data[])
{
	for (int i = 1; i < size; i++)//i为扫描次数
	{
		int tmp = data[i];
		for (int j = i-1; j >=0; j--)//用j定位比较的元素
		{
			if (tmp < data[j])
			{
				Swap(data[j+1], data[j]);
			}
		}
		cout << "第" << i << "次扫描:";
		print(data);
	}
}
  •  完整代码

#include<iostream>
#include<stdio.h>
using namespace std;
#define size 5
template <typename T>
void print(T data[])
{
	for (int i = 0; i < size; i++)
	{
		cout << data[i] << " ";
	}
	cout << endl;
}
template <typename T>
void Swap(T& a, T& b)//交换
{
	T tmp;
	tmp = a;
	a = b;
	b = tmp;
}

template <typename T>
void Inser(T data[])
{
	for (int i = 1; i < size; i++)//i为扫描次数
	{
		int tmp = data[i];
		for (int j = i - 1; j >= 0; j--)//用j定位比较的元素
		{
			if (tmp < data[j])
			{
				Swap(data[j + 1], data[j]);
			}
		}
		cout << "第" << i << "次扫描:";
		print(data);
	}
}

int main()
{
	int data[] = { 55,23,87,62,16 };
	cout << "排序前的顺序:" << endl;
	print(data);
	Inser(data);
	cout << "排序后的顺序:" << endl;
	print(data);
	return 0;
}
  • 结果: 

  •  插入排序算法分析:

  • 1.最坏情况和平均情况需比较(n-1)+(n-2)+....+2+1=n(n-1)/2次,时间复杂度为O(n^{2})
  • 最好情况是扫描n次,移动0次,时间复杂度为O(n)
  • 2.插入排序是稳定排序法
  • 3.因为只需要一个额外的空间,所有空间复杂度为最佳

带哨兵的插入排序 

  • 举例: 

  •  关键代码

template <typename T>
void Insert2(T data[])
{
	for (int i = 2; i < size; i++)
	{
		data[0] = data[i];
		for (int j = i - 1; data[0] < data[j]; j--)
		{
			Swap(data[j + 1], data[j]);
		}
		cout << "第" << i-1 << "次扫描:";
		print(data);
	}
}
  •  完整代码

#include<iostream>
#include<stdio.h>
using namespace std;
#define size 6
template <typename T>
void print(T data[])
{
	for (int i = 1; i < size; i++)
	{
		cout << data[i] << " ";
	}
	cout << endl;
}
template <typename T>
void Swap(T& a, T& b)//交换
{
	T tmp;
	tmp = a;
	a = b;
	b = tmp;
}
template <typename T>
void Insert2(T data[])
{
	for (int i = 2; i < size; i++)
	{
		data[0] = data[i];
		for (int j = i - 1; data[0] < data[j]; j--)
		{
			Swap(data[j + 1], data[j]);
		}
		cout << "第" << i-1 << "次扫描:";
		print(data);
	}
}

int main()
{
	int data[size];
	cout << "请依次输入5个数据:";
	for (int i = 1; i < size; i++)
	{
		cin >> data[i];
	}
	cout << "排序前的顺序:" << endl;
	print(data);
	Insert2(data);
	cout << "排序后的顺序:" << endl;
	print(data);
	return 0;
}
  • 算法中引入附加记录data[0]有两个作用:
  • 其一是进入查找循环之前,它保存了data[i]的副本,使得不至于因记录的后移而丢失data[i]中的内容;
  • 其二是在for循环“监视”下标变量j是否越界,一旦越界(即j < 1),data[0]自动控制for循环的结束,从而避免了在for循环内的每一次都要检测j是否越界(即省略了循环条件j >= 1)。 
  • data[0]称为“监视哨” 

折半插入排序

  • 基本思想:在已形成的有序表中折半查找,并在适当位置插入,把原来位置上的元素向后顺移
  • 演示

  •  关键代码

template <typename T>
void BinSort(T data[])
{
	for (int i = 1; i < size; i++)//扫描次数(需要排序的元素个数)
	{
		T tmp = data[i];//先将data[i]保存在tmp中
		int low = 0, high = i - 1;
		int mid = 0;
		while (low <= high)//寻找插入位置
		{
			mid = (low + high) / 2;
			if (tmp <= data[mid])
			{
				high = mid - 1;
			}
			else
			{
				low = mid + 1;
			}
		}//循环结束,high+1为插入位置
		for (int j = i - 1; j >= high + 1; j--)//移动元素
		{
			data[j + 1] = data[j];
		}
		data[high + 1] = tmp;
		cout << "第" << i << "次扫描:" ;
		print(data);
	}
}
  • 完整代码

#include<iostream>
#include<stdio.h>
using namespace std;
#define size 11
template <typename T>
void print(T data[])
{
	for (int i = 0; i < size; i++)
	{
		cout << data[i] << " ";
	}
	cout << endl;
}
template <typename T>
void BinSort(T data[])
{
	for (int i = 1; i < size; i++)//扫描次数(需要排序的元素个数)
	{
		T tmp = data[i];//先将data[i]保存在tmp中
		int low = 0, high = i - 1;
		int mid = 0;
		while (low <= high)//寻找插入位置
		{
			mid = (low + high) / 2;
			if (tmp <= data[mid])
			{
				high = mid - 1;
			}
			else
			{
				low = mid + 1;
			}
		}//循环结束,high+1为插入位置
		for (int j = i - 1; j >= high + 1; j--)//移动元素
		{
			data[j + 1] = data[j];
		}
		data[high + 1] = tmp;
		cout << "第" << i << "次扫描:" ;
		print(data);
	}
}

int main()
{
	//int data[] = { 3,5,7,10,16,23,29,32,54,83,96 };
	int data[] = { 96,83,54,32,29,23,16,10,7,5,3 };
	cout << "排序前的顺序:" << endl;
	print(data);
	BinSort(data);
	cout << "排序后的顺序:" << endl;
	print(data);
	return 0;
}
  •  结果

折半插入排序(监视哨)

  • 关键代码 

template <typename T>
void BinSort(T data[])
{
	for (int i = 2; i < size; i++)
	{
		data[0] = data[i];//下标为0的位置放即将插入的元素(哨兵)
		int low = 0, high = i - 1;
		while (low <= high)
		{
			int mid = (low + high) / 2;//折半
			if (data[0]<=data[mid])
			{
				high = mid - 1;//插入点在低半区
			}
			else
			{
				low = mid + 1;
			}
		}
		for (int j = i - 1; j >= low; j--)
		{
			data[j + 1] = data[j];
		}
		data[low] = data[0];
	}
}
  • 完整代码

#include<iostream>
#include<stdio.h>
using namespace std;
#define size 12
template <typename T>
void print(T data[])
{
	for (int i = 1; i < size; i++)
	{
		cout << data[i] << " ";
	}
	cout << endl;
}

template <typename T>
void BinSort(T data[])
{
	for (int i = 2; i < size; i++)
	{
		data[0] = data[i];//下标为0的位置放即将插入的元素(哨兵)
		int low = 0, high = i - 1;
		while (low <= high)
		{
			int mid = (low + high) / 2;//折半
			if (data[0]<=data[mid])
			{
				high = mid - 1;//插入点在低半区
			}
			else
			{
				low = mid + 1;
			}
		}
		for (int j = i - 1; j >= low; j--)
		{
			data[j + 1] = data[j];
		}
		data[low] = data[0];
	}
}
int main()
{
	int data[size] ;
	cout << "请依次输入"<<size-1<<"个数据元素:"<<endl;
	//测试数据:96 83 54 32 29 23 16 10 7 5 3
	for (int i = 1; i < size; i++)
	{
		cin >> data[i];
	}
	cout << "排序前的顺序:" << endl;
	print(data);
	BinSort(data);
	cout << "排序后的顺序:" << endl;
	print(data);
	return 0;
}

 折半插入排序的算法分析

  • 折半查找比顺序查找快,所以折半插入排序就平均性能来说比直接插入排序要快。 
  • 在插入第 i个对象时,需要经过[]log_{2}i]+1次关键码比较,才能确定它应插入的位置。 
  • 折半插入排序是一个稳定的排序方法。 

希尔排序 (Shell Sort) 

  • 基本思想:先将整个待排序记录序列分割成为若干个子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行一次直接插入排序
  • 演示        

                                                                                                                                 

  • 举例                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          

  • 关键代码 

void ShellSort(int *data, int n, int delta)//一趟希尔排序
{
	for (int i = delta; i <n; i++)//用i表示扫描次数
	{
		int tmp = data[i];
		for (int j = i-delta; j < n&&tmp<data[j]; j = j - delta)//用j定位需要比较的元素
		{
			swap(data[j], data[j + 1]);
		}
	}
}
void ShellSert(int* data, int n, int delta)
{
	int k = 1;//打印计数
	while (delta != 0)
	{
		ShellSort(data, n, delta);
		cout  << "第" << k++ << "次排序:";
		print(data, n);
		delta = delta / 2;
	}
}
  • 完整代码

#include<iostream>
#include<stdio.h>
using namespace std;
void swap(int& a, int& b)//交换
{
	int temp = a;
	a = b;
	b = temp;
}
void print(int* data, int n)//打印
{
	for (int i = 0; i < n; i++)
	{
		cout << data[i] << " ";
	}
	cout << endl;
}
void ShellSort(int *data, int n, int delta)//一趟希尔排序
{
	for (int i = delta; i <n; i++)//用i表示扫描次数
	{
		int tmp = data[i];
		for (int j = i-delta; j < n&&tmp<data[j]; j = j - delta)//用j定位需要比较的元素
		{
			swap(data[j], data[j + 1]);
		}
	}
}
void ShellSert(int* data, int n, int delta)
{
	int k = 1;//打印计数
	while (delta != 0)
	{
		ShellSort(data, n, delta);
		cout  << "第" << k++ << "次排序:";
		print(data, n);
		delta = delta / 2;
	}
}
int main()
{
	int a[] = { 5,65,1,45,32,67,89,12 };
	cout << "排序前:";
	print(a, 8);
	ShellSert(a, 8, 4);
	cout << "排序后:";
	print(a, 8);
	return 0;
}
  •  结果

 希尔排序算法分析

  • 希尔排序是一种不稳定的排序方法(原因:关键字相同的两个对象,在排序过程中,由于所处的分组不同,会发生位置交换)
  • 因为只需要一个额外的空间,所有空间复杂度最佳

交换排序 

 冒泡排序(Bubble Sort)

  •  基本思想:从第一个元素开始,比较相邻元素的大小,若大小有误,则对调再进行下一个元素的比较,如此扫过依次之后就可以确保最后一个元素位于正确的顺序。接着进行第二次扫描....
  • 演示  

  • 举例 

  •  关键代码

void  BubbleSorta(int* data, int n)//冒泡排序
{
	for (int i = n - 1; i > 0; i--)//扫描次数
	{
		for (int j = 0; j < i; j++)//比较,交换次数
		{
			if (data[j] > data[j + 1])
			{
				swap(data[j], data[j + 1]);
			}
		}
		cout << "第" << n - i << "次扫描:";
		print(data, n);
	}
}
  •  完整代码

#include<iostream>
#include<stdio.h>
using namespace std;
void swap(int& a, int& b)//交换
{
	int tmp = a;
	a = b;
	b = tmp;
}
void print(int* data, int n)//打印
{
	for (int i = 0; i < n; i++)
	{
		cout << data[i] << " ";
	}
	cout << endl;
}
void  BubbleSorta(int* data, int n)//冒泡排序
{
	for (int i = n - 1; i > 0; i--)//扫描次数
	{
		for (int j = 0; j < i; j++)//比较,交换次数
		{
			if (data[j] > data[j + 1])
			{
				swap(data[j], data[j + 1]);
			}
		}
		cout << "第" << n - i << "次扫描:";
		print(data, n);
	}
}

int main()
{
	int a[] = { 6,5,9,7,2,8 };
	cout << "排序前:";
	print(a, 6);
	BubbleSorta(a, 6);
	cout << "排序后:";
	print(a, 6);
	return 0;
}
  •  结果

  • 改进冒泡排序(增加flag判断在此次扫描中是否发生过交换) 
void  BubbleSorta(int* data, int n)//冒泡排序
{
	for (int i = n - 1; i > 0; i--)//扫描次数
	{
		int flag = 0;//flag用来判断是否执行了数据交换
		for (int j = 0; j < i; j++)//比较,交换次数
		{
			if (data[j] > data[j + 1])
			{
				swap(data[j], data[j + 1]);
				flag++;
			}
		}
		cout << "第" << n - i << "次扫描:";
		print(data, n);
		if (flag == 0)//当执行完依次扫描判断是否执行过数据交换操作,如果没有执行过数据交换操作
			//表示此时数列已经完成了排序,故可直接跳出循环
			break;
	}
}

 冒泡排序算法分析

  • n个元素的冒泡排序必须执行n-1次扫描
  • 最坏情况和平均情况需比较:(n-1)+(n-2)+...+2+1=n(n-1)/2次,时间复杂度为O(n^{2});最好情况只需扫描一次,若发现没有进行数据交换的操作,则表示已经排序完成,所以只进行n-1次比较,时间复杂度为 O(n)
  • 冒泡排序算法是稳定的
  • 因为只需要一个额外的空间,所有空间复杂度为最佳 

 快速排序

  • 基本思想:先在数据中找中间值,并按次中间值将所有数据分成两部分,其中小于中间值的数据放在左边,大于中间值的数据放在右边,再以同样的方式处理左右两边的数据,直到排序完为止 
  • 关键代码 

int partition(int* data, int i, int j)
{
	int tmp = data[i];
	while (i < j)
	{
		while (i<j && data[j]>=tmp)//从右边找比基准数小的数字
		{
			j--;
		}
		if (i < j)
		{
			data[i] = data[j];
		}
		while (i < j && data[i] <= tmp)
		{
			i++;
		}
		if (i < j)
		{
			data[j] = data[i];
		}
	}
	data[i] = tmp;//将tmp的值赋值给i和j重合的位置
	return i;
}
void QuickSort(int* data, int i, int j)
{
	if (i < j)
	{
		int x = partition(data, i, j);
		QuickSort(data, i, x - 1);//给基准数左边数据排序
		QuickSort(data, x + 1, j);//给右边排序
	}
}
  •  完整代码

#include<iostream>
#include<stdio.h>
using namespace std;
int partition(int* data, int i, int j)
{
	int tmp = data[i];
	while (i < j)
	{
		while (i<j && data[j]>=tmp)//从右边找比基准数小的数字
		{
			j--;
		}
		if (i < j)
		{
			data[i] = data[j];
		}
		while (i < j && data[i] <= tmp)
		{
			i++;
		}
		if (i < j)
		{
			data[j] = data[i];
		}
	}
	data[i] = tmp;//将tmp的值赋值给i和j重合的位置
	return i;
}
void QuickSort(int* data, int i, int j)
{
	if (i < j)
	{
		int x = partition(data, i, j);
		QuickSort(data, i, x - 1);//给基准数左边数据排序
		QuickSort(data, x + 1, j);//给右边排序
	}
}
void print(int* data,int n)
{
	for (int i = 0; i < n; i++)
	{
		cout << data[i] << " ";
	}
	cout << endl;
}
int main()
{
	int a[] = { 35,10, 42, 3, 79, 12, 62, 18, 51, 23 };
	cout << "排序前:";
	print(a, 10);
	QuickSort(a, 0, 9);
	cout << "排序后:";
	print(a, 10);
	return 0;
}
  •  结果

 选择排序

  • 基本原理: 从n个关键字序列中找到一个最小值,并把它放到序列首端,再从剩下的n-1个关键字中选择最小值,仍然放到这n-1个关键字的序列首端,以此类推。 

简单选择排序 

  •  基本思想:通过n - i次关键字间的比较,从n - i + 1个记录中选出关键字最小的纪录,并和第i(1<=i<=n)个记录交换。
  • 关键代码 

void SelectionSort(int* data, int n)
{
	for (int i = 0; i <=n-1; i++)//扫描次数
	{
		int min = data[i];
		int k = i;//用k保存最小值的下标
		for (int j = i; j < n; j++)
		{
			if (data[j] < min)
			{
				min = data[j];
				k = j;
			}
		}
		swap(data[i], data[k]);
	}
}
  • 完整代码

#include<iostream>
#include<stdio.h>
using namespace std;
void swap(int& a, int& b)
{
	int tmp;
	tmp = a;
	a = b;
	b = tmp;
}
void SelectionSort(int* data, int n)
{
	for (int i = 0; i <=n-1; i++)//扫描次数
	{
		int min = data[i];
		int k = i;//用k保存最小值的下标
		for (int j = i; j < n; j++)
		{
			if (data[j] < min)
			{
				min = data[j];
				k = j;
			}
		}
		swap(data[i], data[k]);
	}
}
void print(int* data, int n)
{
	for (int i = 0; i < n; i++)
	{
		cout << data[i] << " ";
	}
	cout << endl;
}
int main()
{
	int a[] = { 55,23,87,62,16 };
	cout << "排序前:";
	print(a, 5);
	SelectionSort(a, 5);
	cout << "排序后:";
	print(a, 5);
	return 0;
}

 堆积树排序法

  • 堆的定义:堆是一棵完全二叉树,其中每一个非终端结点的元素均大于等于(或小于等于)其左、右孩子结点的元素值。

  • 堆排序的基本思想是:对一组待排序记录,首先把它们的关键字按堆定义排列成一个序列(称为初始建堆),堆顶元素为最大关键字的记录,将堆顶元素输出;然后对剩余的记录再建堆(调整堆),得到次最大关键字记录;如此反复进行,直到全部记录有序为止,这个过程称为堆排序。 
  •  演示

  •  关键代码1

void HeadAdjust(int* data, int k, int n)//将以k为根的子树调整为大堆积根
{
	int tmp = data[k];
	int j ;
	if (2 * k + 2 < n)
	{
		if (data[k] >= MAX(data[2 * k + 1], data[2 * k + 2]))//根结点大于其两个孩子结点
		{
			;
		}
		else if (data[2 * k + 1] > data[2 * k + 2])//左孩子结点的值大于右孩子结点
		{
			j = 2 * k + 1;
			swap(data[k], data[2 * k + 1]);
			for (int i = j; i < n; i = i * 2 + 1)
			{
				HeadAdjust(data, i, n);
			}
		}
		else
		{
			j = 2 * k + 2;
			swap(data[k], data[2 * k + 2]);
			for (int i = j; i < n; i = i * 2 + 2)
			{
				HeadAdjust(data, i, n);
			}
		}
	}
	else if (2 * k + 1 < n)//以k为根结点的时候,只有左孩子结点的时候
	{
		if (data[k] < data[2 * k + 1])
		{
			swap(data[k], data[2 * k + 1]);
		}
	}
	
}

void BulidHeap(int* data, int n)//建立大堆积树
{
	for (int i = (n - 2) / 2; i >= 0; i--)//(n-2)/2是树中第一个非叶子结点的下标
	{
		HeadAdjust(data, i, n);
	}
}
void HeadSort(int* data, int n)//基于大堆积树排序
{
	BulidHeap(data, n);//建立初始大堆积树
	//print(data, n);
	cout << "排序后:";
	for (int i = n - 1; i > 0; i--)
	{
		cout << data[0] << " ";
		swap(data[0], data[i]);
		BulidHeap(data, i);
		//print(data, i);
	}
	cout << data[0];
}
  •  完整代码1

#include<iostream>
#include<stdio.h>
using namespace std;
void swap(int& a, int& b)//交换
{
	int tmp;
	tmp = a;
	a = b;
	b = tmp;
}
int MAX(int& a, int& b)//求最大值
{
	if (a < b)
		return b;
	else
		return a;
}
void print(int* data, int n)
{
	for (int i = 0; i < n; i++)
	{
		cout << data[i] << " ";
	}
	cout << endl;
}
void HeadAdjust(int* data, int k, int n)//将以k为根的子树调整为大堆积根
{
	int tmp = data[k];
	int j ;
	if (2 * k + 2 < n)
	{
		if (data[k] >= MAX(data[2 * k + 1], data[2 * k + 2]))//根结点大于其两个孩子结点
		{
			;
		}
		else if (data[2 * k + 1] > data[2 * k + 2])//左孩子结点的值大于右孩子结点
		{
			j = 2 * k + 1;
			swap(data[k], data[2 * k + 1]);
			for (int i = j; i < n; i = i * 2 + 1)
			{
				HeadAdjust(data, i, n);
			}
		}
		else
		{
			j = 2 * k + 2;
			swap(data[k], data[2 * k + 2]);
			for (int i = j; i < n; i = i * 2 + 2)
			{
				HeadAdjust(data, i, n);
			}
		}
	}
	else if (2 * k + 1 < n)//以k为根结点的时候,只有左孩子结点的时候
	{
		if (data[k] < data[2 * k + 1])
		{
			swap(data[k], data[2 * k + 1]);
		}
	}
	
}

void BulidHeap(int* data, int n)//建立大堆积树
{
	for (int i = (n - 2) / 2; i >= 0; i--)//(n-2)/2是树中第一个非叶子结点的下标
	{
		HeadAdjust(data, i, n);
	}
}
void HeadSort(int* data, int n)//基于大堆积树排序
{
	BulidHeap(data, n);//建立初始大堆积树
	//print(data, n);
	cout << "排序后:";
	for (int i = n - 1; i > 0; i--)
	{
		cout << data[0] << " ";
		swap(data[0], data[i]);
		BulidHeap(data, i);
		//print(data, i);
	}
	cout << data[0];
}


int main()
{
	int a[] = { 53,17,78,9,45,65,87,32 };
	cout << "排序前:";
	print(a, 8);
	BulidHeap(a, 8);
	cout << "建立的大堆积树:";
	print(a, 8);
	cout << endl;
	HeadSort(a, 8);
	return 0;
}
  •  结果

  • 改进关键代码2 

void HeadAdjust(int* data, int k, int len)//将以k为根的子树调整大根堆
{
	int tmp = data[k];//保存根结点
	int j = 2 * k;//下标为j的结点是下标为k的结点的左孩子
	while (j <= len)
	{
		if (j < len && data[j] < data[j + 1])//右孩子较大,j指向右孩子
		{
			j++;//j变为2*i+1
		}
		if (data[k] < data[j])
		{
			data[k] = data[j];
			data[j] = tmp;
			k = j;//修改k和j的值,继续向下筛选
			j = 2 * k;
		}
		else
			break;
	}
}
void HeapSort(int* data, int len)//堆排序
{
	for (int i = len / 2; i >= 1; i--)//循环建立初始堆
	{
		HeadAdjust(data, i, len);
	}
	for (int j = len; j >= 2; j--)
	{
		swap(data[1], data[j]);
		HeadAdjust(data, 1, j-1);
	}
}
  •  完整代码2

#include<iostream>
#include<stdio.h>
using namespace std;
void swap(int& a, int& b)//交换
{
	int tmp;
	tmp = a;
	a = b;
	b = tmp;
}
void HeadAdjust(int* data, int k, int len)//将以k为根的子树调整大根堆(data数组必须从1开始存放)
{
	int tmp = data[k];//保存根结点
	int j = 2 * k;//下标为j的结点是下标为k的结点的左孩子
	while (j <= len)
	{
		if (j < len && data[j] < data[j + 1])//右孩子较大,j指向右孩子
		{
			j++;//j变为2*i+1
		}
		if (data[k] < data[j])
		{
			data[k] = data[j];
			data[j] = tmp;
			k = j;//修改k和j的值,继续向下筛选
			j = 2 * k;
		}
		else
			break;
	}
}
void HeapSort(int* data, int len)//堆排序
{
	for (int i = len / 2; i >= 1; i--)//循环建立初始堆
	{
		HeadAdjust(data, i, len);
	}
	for (int j = len; j >= 2; j--)
	{
		swap(data[1], data[j]);
		HeadAdjust(data, 1, j-1);
	}
}
void print(int* data, int n)
{
	for (int i = 1; i <= n; i++)
	{
		cout << data[i] << " ";
	}
	cout << endl;
}
int main()
{
	//int a[] = { 53,17,78,9,45,65,87,32 };
	int a[100];
	int len;
	cout << "输入元素总数:";
	cin >> len;
	cout << "依次输入各个数据:";
	for (int i = 1; i <= len; i++)
	{
		cin >> a[i];
	}
	cout << "排序前:";
	print(a, 8);
	HeapSort(a, 8);
	cout << "排序后:";
	print(a, 8);
	return 0;
}

归并排序

  • 归并:把两个或多个已经有序的序列合并成一个 
  • 演示

  • 关键代码1 

void Merge(int *data, int low, int mid, int high)//二路归并
{
	int* tmp = (int*)malloc((high - low + 2) * sizeof(int*));//辅助数组
	if (tmp==NULL)
	{
		return;
	}
	int i = low, j = mid + 1, k = 0;
	while (i <= mid && j <= high)//在第一段和第二段均未扫描完时循环
	{
		if (data[i] <= data[j])
		{
			tmp[k++] = data[i++];//将前半段元素放入tmp[]数组中
		}
		else
		{
			tmp[k++] = data[j++];//将后半段元素放入tmp[]数组中
		}
	}
	while (i <= mid)//将前半段剩下元素复制到data[]数组中
	{
		tmp[k++] = data[i++];
	}
	while (j <= high)//将后半段剩下元素复制到data[]数组中
	{
		tmp[k++] = data[j++];
	}
	for (i = low, k = 0; i <= high; i++, k++)
	{
		data[i] = tmp[k];//将tmp数组元素复制回data[]数组中
		cout << tmp[k] << " ";
	}
	cout << endl;
	free(tmp);
}

void MergeSort(int *data, int low, int high)
{
	if (low < high)
	{
		int mid = (low + high) / 2;
		MergeSort(data, low, mid);  //对前半部分进行归并排序
		MergeSort(data, mid + 1, high);//对后半部分进行归并排序
		Merge(data, low, mid, high);//合并两段
	}
}
  •  完整代码1

#include<iostream>
#include<stdio.h>
#include<malloc.h>
using namespace std;
#define n 7//元素个数
void Merge(int *data, int low, int mid, int high)//二路归并
{
	int* tmp = (int*)malloc((high - low + 2) * sizeof(int*));//辅助数组
	if (tmp==NULL)
	{
		return;
	}
	int i = low, j = mid + 1, k = 0;
	while (i <= mid && j <= high)//在第一段和第二段均未扫描完时循环
	{
		if (data[i] <= data[j])
		{
			tmp[k++] = data[i++];//将前半段元素放入tmp[]数组中
		}
		else
		{
			tmp[k++] = data[j++];//将后半段元素放入tmp[]数组中
		}
	}
	while (i <= mid)//将前半段剩下元素复制到data[]数组中
	{
		tmp[k++] = data[i++];
	}
	while (j <= high)//将后半段剩下元素复制到data[]数组中
	{
		tmp[k++] = data[j++];
	}
	for (i = low, k = 0; i <= high; i++, k++)
	{
		data[i] = tmp[k];//将tmp数组元素复制回data[]数组中
		cout << tmp[k] << " ";
	}
	cout << endl;
	free(tmp);
}

void MergeSort(int *data, int low, int high)
{
	if (low < high)
	{
		int mid = (low + high) / 2;
		MergeSort(data, low, mid);  //对前半部分进行归并排序
		MergeSort(data, mid + 1, high);//对后半部分进行归并排序
		Merge(data, low, mid, high);//合并两段
	}
}
void print(int* data)
{
	for (int i = 0; i < n; i++)
	{
		cout << data[i] << " ";
	}
	cout << endl;
}
int main()
{
	int a[] = { 49,38,65,97,76,13,27 };
	MergeSort(a,0,n-1);
	print(a);
	return 0;
}
  • 关键代码2

void Merge(int data[], int n)
{
	int* tmp = (int*)malloc(n * sizeof(int*));//辅助数组
	if (tmp == NULL)
	{
		return;
	}
	int mid = (n - 1) / 2;
	int i = 0, j = mid+1, k = 0;
	while (i <= mid && j <= n-1)//在第一段和第二段均未扫描完时循环
	{
		if (data[i] <= data[j])
		{
			tmp[k++] = data[i++];//将前半段元素放入tmp[]数组中
		}
		else
		{
			tmp[k++] = data[j++];//将后半段元素放入tmp[]数组中
		}
	}
	while (i <= mid)//将前半段剩下元素复制到data[]数组中
	{
		tmp[k++] = data[i++];
	}
	while (j <= n-1)//将后半段剩下元素复制到data[]数组中
	{
		tmp[k++] = data[j++];
	}
	for (i = 0;i<n;i++)
	{
		data[i] = tmp[i];//将tmp数组元素复制回data[]数组中
		cout << tmp[i] << " ";
	}
	cout << endl;
	free(tmp);
}
void MergeSort(int data[], int n)
{
	if (n <= 1)
	{
		return;
	}
	if (n>1)
	{
		int mid = (n-1) / 2;
		MergeSort(data, mid+1);  //对前半部分进行归并排序
		MergeSort(data+mid+1, n-(mid+1));//对后半部分进行归并排序(后半段起始位置元素下标是数组地址向后移动mid+1个位置)
		Merge(data, n);//合并两段
	}
}
  •  完整代码2

#include<iostream>
#include<stdio.h>
#include<malloc.h>
using namespace std;
void Merge(int data[], int n)
{
	int* tmp = (int*)malloc(n * sizeof(int*));//辅助数组
	if (tmp == NULL)
	{
		return;
	}
	int mid = (n - 1) / 2;
	int i = 0, j = mid+1, k = 0;
	while (i <= mid && j <= n-1)//在第一段和第二段均未扫描完时循环
	{
		if (data[i] <= data[j])
		{
			tmp[k++] = data[i++];//将前半段元素放入tmp[]数组中
		}
		else
		{
			tmp[k++] = data[j++];//将后半段元素放入tmp[]数组中
		}
	}
	while (i <= mid)//将前半段剩下元素复制到data[]数组中
	{
		tmp[k++] = data[i++];
	}
	while (j <= n-1)//将后半段剩下元素复制到data[]数组中
	{
		tmp[k++] = data[j++];
	}
	for (i = 0;i<n;i++)
	{
		data[i] = tmp[i];//将tmp数组元素复制回data[]数组中
		cout << tmp[i] << " ";
	}
	cout << endl;
	free(tmp);
}
void MergeSort(int data[], int n)
{
	if (n <= 1)
	{
		return;
	}
	if (n>1)
	{
		int mid = (n-1) / 2;
		MergeSort(data, mid+1);  //对前半部分进行归并排序
		MergeSort(data+mid+1, n-(mid+1));//对后半部分进行归并排序(后半段起始位置元素下标是数组地址向后移动mid+1个位置)
		Merge(data, n);//合并两段
	}
}
void print(int* data,int n)
{
	for (int i = 0; i < n; i++)
	{
		cout << data[i] << " ";
	}
	cout << endl;
}
int main()
{
	int n = 7;//元素个数
	int a[] = { 49,38,65,97,76,13,27 };
	MergeSort(a, n);
	print(a,n);
	return 0;
}

 基数排序

  • 基数排序(Radix Sort)是一种借助于多关键字排序的思想对单逻辑关键字进行排序的方法,即先将关键字分解成若干部分,然后通过对各部分关键字的分别排序,最终完成对全部记录的排序。
  • 关键代码 

typedef int ElemType;
#define MAX 20//线性表最多的元素个数
#define MAXR 10//基数的最大取值
#define MAXD 8//关键字位数最大取值
typedef struct node
{
	char data[MAX];
	struct node* next;
}RecType;
void RadixSor(RecType*& p, int r, int d)//p存放待排序序列的单链表(无头结点),r为基数,d为关键字位数
{
	RecType* head[MAX], * tail[MAX],*t;
	int i, j, k;
	for (int i = d - 1; i >= 0; i--)//初始化各链队的首,尾指针
	{
		for (int j = 0; j < r; j++)
		{
			head[j] = tail[j] = NULL;

		}
		while (p != NULL)
		{
			k = p->data[i] - '0';//找第k个链队
			if (head[k] == NULL)//进行分配,尾插建单链表
			{
				head[k] = p;
				tail[k] = p;
			}
			else
			{
				tail[k]->next = p;
				tail[k] = p;
			}
			p = p->next;//取下一个待排序的元素
		}
		
		p = NULL;
		for (int j = 0; j < r; j++)//进行收集
		{
			if (head[j] != NULL)
			{
				if (p == NULL)
				{
					p = head[j];
					t = tail[j];
				}
				else
				{
					t->next = head[j];
					t = tail[j];
				}
			}
		}
		t->next = NULL;
		cout << "第" << i - d + 1 << "趟";
		print(p);
	}
}

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

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

相关文章

365天深度学习训练营-第P4周:猴痘病识别

&#x1f368; 本文为&#x1f517;365天深度学习训练营 内部限免文章&#xff08;版权归 K同学啊 所有&#xff09;&#x1f366; 参考文章地址&#xff1a; &#x1f517;第P4周&#xff1a;猴痘病识别 | 365天深度学习训练营&#x1f356; 作者&#xff1a;K同学啊 | 接辅导…

Java 发送邮件

使用Java应用程序发送 E-mail 十分简单&#xff0c;但是首先你应该在你的机器上安装 JavaMail API 和Java Activation Framework (JAF) 。 您可以从 Java 网站下载最新版本的 JavaMail&#xff0c;打开网页右侧有个 Downloads 链接&#xff0c;点击它下载。您可以从 Java 网站…

从业10多年,我总结了这几款程序员必备实用工具

优秀程序员之所以优秀的原因并不一定是他写代码的速度比别人快&#xff0c;而是他解决事情的效率比别人高、副业渠道比别人广&#xff0c;所以提供工作效率的方法并不需要我们样样精通&#xff0c;提高副业收入的办法也不需要我们挖空心思&#xff0c;有时候使用好的工具就能帮…

微信小程序开发实战11_1 微信支付下单

微信支付流程图 微信支付存在多个业务流程&#xff0c;包括微信支付流程、退款流程等。本章节主要介绍微信的支付下单流程&#xff0c;图12-1是微信支付流程的交互图&#xff1a; 重点环节说明 步骤1&#xff1a;小程序端用户向商户服务器发起支付请求&#xff0c;重点是提供…

Appium基础 — webview操作(重点)

我们之前说过的所有操作&#xff0c;都是对原生页面的操作。 在手机APP中&#xff0c;除了原生页面&#xff0c;还是有webview页面&#xff08;也就是H5页面&#xff09;&#xff0c;下面我们就说说对webview页面的操作。 1、先了解什么是Hybrid&#xff08;混合&#xff09;…

【Web安全】Ysoserial 简单利用

Ysoserial 简单利用1. Java 反序列化特征2. Ysoserial 流量特征3. Ysoserial 攻击流程3.1 找到序列化接口3.2 漏洞利用3.2.1 常用命令3.2.2 使用案例4. Ysoserial 攻击原理问题参考1. Java 反序列化特征 在日志中&#xff0c;特征通常表现为 请求格式 Json、xml、soap、二进制…

拓扑排序的java代码实现过程详解

拓扑排序 在现实生活中&#xff0c;我们经常会同一时间接到很多任务去完成&#xff0c;但是这些任务的完成是有先后次序的。以我们学习java学科为例&#xff0c;我们需要学习很多知识&#xff0c;但是这些知识在学习的过程中是需要按照先后次序来完成的。从java基础&#xff0c…

中国电信携手鼎桥创新中心开展终端优选测评工作

近期&#xff0c;中国电信物联网开放实验室与鼎桥创新中心本着“优势互补、合作共赢、共同发展”的原则&#xff0c;携手联合开展了中国电信CTWing物联网市场终端优选测试&#xff0c;完成了多款行业智能终端的测评&#xff0c;为物联网市场上架产品的质量保驾护航&#xff0c;…

Node.js--》Express和路由模块的讲解使用

目录 Express Express的安装与使用 托管静态资源 nodemon Express路由 模块化路由 Express Express是基于Node.js平台&#xff0c;快速、开放、极简的Web开发框架。Express的作用和Node.js内置的http模块类似&#xff0c;是专门用来创建Web服务器的。Express的本质&…

cubeIDE开发, stm32独立看门狗IWDT的CubeMX配置及HAL库底层实现分析

一、STM32 的IWDT简介 2.1 看门狗原理 看门狗本质上就是一种计数器&#xff0c;和我们现实生活中一炷香现象、沙漏现象等是同理的&#xff0c;计数器一般有两种做法&#xff0c;一种是递增&#xff0c;超过固定阀值报警&#xff1b;一种是递减&#xff0c;通常值降到0时报警。后…

数据结构训练营5

开启蓝桥杯备战计划&#xff0c;每日练习算法一题&#xff01;&#xff01;坚持下去&#xff0c;想必下一年的蓝桥杯将会有你&#xff01;&#xff01;笔者是在力扣上面进行的刷题&#xff01;&#xff01;由于是第一次刷题&#xff01;找到的题目也不咋样&#xff01;所以&…

Databend in 2022

上一次写总结还是在回顾 Datafuse Labs 成立一周年&#xff0c;转眼来到 22 年末&#xff0c;Databend 也快要开始第三个年头的征程了。 今天就让我们一起回顾一下 Databend 在 2022 年的成果。 开源 Databend 是一款强大的云数仓&#xff0c;专为弹性和高效设计&#xff0c…

Spring Authorization Server 1.0 提供 Oauth 2.1 和 OpenID Connect 1.0 实现

1. 概述 在引入Java 社区两年半之后&#xff0c;VMWare发布了Spring Authorization Server 1.0。Spring 授权服务器项目构建在Spring Security之上&#xff0c;支持创建OpenID Connect 1.0身份提供者和OAuth 2.1授权服务器。该项目取代了不再维护的 Spring Security OAuth项目…

RHCEansible虚拟机初始化配置,ansible配置和安装

1.保证三台主机能互相通信&#xff0c;需要设置同一种网络模式&#xff08;nat&#xff09; 2.配置静态ip地址&#xff08;命令行&#xff0c;图形界面都可以&#xff09; server---192.168.171.100 node1---192.168.171.222 node2---192.168.171.10 3.更改主机名 永久更…

C++代码编程学习(1):简易通讯录的创建

2022年圣诞节到来啦&#xff0c;很高兴这次我们又能一起度过~ CSDN诚邀各位技术er分享关于圣诞节的各种技术创意&#xff0c;展现你与众不同的精彩&#xff01;参与本次投稿即可获得【话题达人】勋章【圣诞快乐】定制勋章&#xff08;1年1次&#xff0c;错过要等下一年喔&#…

消费市场的“跨年”:2023,数字新消费将引领市场何去何从?

配图来自Canva可画 2022&#xff0c;变局如同一个过滤器&#xff0c;每一个身处其中的消费者和消费品牌都在经受考验。我国人口红利在消退&#xff0c;竞争在加剧&#xff0c;需求在变化。光大证券研究所的数据显示&#xff0c;2022年来我国居民收入增速小幅改善&#xff0c;但…

Spring之Bean创建过程

1. 前言 Spring提供了xml、注解、JavaConfig多种方式来配置bean&#xff0c;不论何种方式&#xff0c;Spring最终都会将bean封装成BeanDefinition对象&#xff0c;Spring创建bean的依据也是通过BeanDefinition来完成的。 当我们调用getBean()方法获取bean实例时&#xff0c;不…

jmeter使用教程之登录接口(工作日记)

首先我们打开jmeter 快捷按钮&#xff1a;winr 会弹出快捷运行弹框&#xff0c;我们输入cmd 后点击回车 会弹出一个控制窗口&#xff0c;我们输入jmeter&#xff0c;然后回车 首次进入jmeter&#xff0c;页面显示空白页且默认英文 我们可以切换语言 【Options - Choose Lan…

AI前沿 | 利用训练好的模型库进行一键抠图(实例演示)

来源&#xff1a;投稿 作者&#xff1a;Struggling cyanobacteria 编辑&#xff1a;学姐 深度学习平台飞浆paddle的环境搭建 ① 效率更高的 gpu 版本的安装 通过 python -m pip install paddlepaddle-gpu -i https://mirror.baidu.com/pypi/simple来进行安装。 paddle.utils…

Dockerfile了解

目录 1.自定义centos&#xff0c;具备vim及ifconfig作用 2.自定义tomcat8 1.自定义centos&#xff0c;具备vim及ifconfig作用 cd /javaxl/software mkdir mydocker vi mycentosDockerfile 向 mycentosDockerfile 编辑内容 FROM centos:7 MAINTAINER zwc "zwcqq.com"…