排序合集

一、排序的基本概念

排序:简而言之,就是按照一定顺序实现数据集合内元素的有序。
算法的稳定性:如果待排序表中有两个元素Ri和Rj,对应关键字keyi=keyj,且排序前,Ri在Rj前面;排序后,Ri仍在Rj前面,则称这个算法使稳定的。

二、插入排序

插入排序的基本思想:将一个待排序的元素按关键字大小插入到一个已经排序好的子序列,直到全部元素插入完成。

(1)直接插入排序

有序序列L[0…i-1]L[i]无序序列L[i+1…n-1]

将元素L[i]插入到有序序列L[0…i-1]当中,需要执行以下操作:
1)查找在L[0…i-1]中要插入的位置K
2)将L[k…i-1]元素后移一个位置
3)将L[i]插入到L[k]上

template<class T>
void InsertSort(T *arry, int n)
{
	int i, j;
	for (i = 1; i < n; i++)  //将arry[1...n-1]元素依次插入到有序序列当中
	{
		int temp = arry[i];  //记录当前插入元素
		for (j = i - 1; temp < arry[j]; j--) //当前要插入元素和已经排序好的子序列元素进行比较,从后向前
		{
			arry[j+1] = arry[j]; //如果插入元素小于子序列最后一个元素,子序列元素后移
		}
		arry[j + 1] = temp;//将插入元素放入要插入的位置

	}
}
int arry[8] = { 49,38,65,97,76,13,27,49 };

输出:
第1遍排序:38 49 65 97 76 13 27 492遍排序:38 49 65 97 76 13 27 493遍排序:38 49 65 97 76 13 27 494遍排序:38 49 65 76 97 13 27 495遍排序:13 38 49 65 76 97 27 496遍排序:13 27 38 49 65 76 97 497遍排序:13 27 38 49 49 65 76 97

直接插入排序算法性能分析:
空间效率:使用了常数个辅助单元,空间复杂度O(1)
时间效率:向子序列插入元素n-1躺,每趟操作分为移动元素和比较关键字。
最好的情况下,元素已经有序,只需要比较,不需要移动,时间复杂度O(n)。
最坏情况下:元素逆序。
平均时间复杂度:O(n2)
稳定性:从后先前比较,不会出现元素相对位置出现变化,所以是稳定的。

(2)折半插入排序

折半插入排序基本思想:在直接插入排序上,把比较和移动分离,先找到要插入的位置,再进行元素位置的移动。

int Fold(int* arry, int n,int key)
{
	int low = 0;
	int high = n - 1;
	
	while (low<=high)
	{
		int mid = (low + high) / 2;  //取中间点
		if (arry[mid] > key)         //中间点的值大于插入值
		{
			high = mid-1;              //向左半子表继续查找
		}
		else
		{
			low = mid+1;               //向右半子表继续查找
		}
	}
	return low;   //返回要插入的位置
}

void InsertSort(int* arry, int n)
{
	int i, j;
	for (i = 1; i < n; i++)  //将arry[1...n-1]元素依次插入到有序序列当中
	{
		int temp = arry[i];  //记录当前插入元素
		int k = Fold(arry, i, temp);  //找到要插入的位置
		for (j = i - 1; j >= k; j--)  //子序列元素后移
		{
			arry[j + 1] = arry[j];
		}
		arry[k] = temp;//将插入元素放入要插入的位置
	}
}

(3)希尔排序

增量排序,划分小的子序列,然后对子序列进行直接插入排序。

template<class T>
void ShellSort(T * arry,int n)
{
	for (int size = n/2; size >= 1; size=size/2)  //步长
	{
		for (int i = size ; i < n; i++)      //步长前面的元素相当于子序列
		{
			if (arry[i] < arry[i - size])   //如果当前元素小于步长前面的子序列元素,进行直接插入排序
			{
				T temp = arry[i];
				int j = 0;
				for ( j = i - size; j >= 0 && temp < arry[j]; j = j - size)//寻找插入点
				{
					arry[j + size] = arry[j];
					
				}
				arry[j + size] = temp; //插入元素
			}
		}

	}
}
输出:
int arry[8] = { 49,38,65,97,76,13,27,49 };1遍排序:49 13 27 49 76 38 65 971遍排序:27 13 49 38 65 49 76 971遍排序:13 27 38 49 49 65 76 97

空间效率:使用了常数个辅组单元,空间复杂度O(1)
时间效率:n在某个特定范围,时间复杂度O(n1.3),最坏情况下:O(n2)
稳定性:不稳定

三、交换排序

(1)冒泡排序

基本思想:从前往后(从后往前)两两比较相邻元素的值,如果A[i-1]>A[i],交换位置,直到序列排完。最多n-1趟冒泡就可以排序好数组。

template<class T>
void BubbleSort(T* arry, int n)
{
	for (int i = 0; i < n-1; i++)  //最多n-1趟冒泡排序
	{
		bool flag = false;  //本趟冒泡排序是否发生交换的标志
		for (int j = n - 1; j > i; j--)  //从后向前冒泡
		{
			if (arry[j - 1] > arry[j])
			{
				swap(arry[j - 1], arry[j]);   //交换
				flag = true;
			}
		}
		if (flag == false)  //没有交换,就表示元素已经有序
			return;
	}
}
输出:
int arry[8] = { 49,38,65,97,76,13,27,49 };
没有加flag
第1遍排序:13 49 38 65 97 76 27 492遍排序:13 27 49 38 65 97 76 493遍排序:13 27 38 49 49 65 97 764遍排序:13 27 38 49 49 65 76 975遍排序:13 27 38 49 49 65 76 976遍排序:13 27 38 49 49 65 76 977遍排序:13 27 38 49 49 65 76 97
加flag
第1遍排序:13 49 38 65 97 76 27 492遍排序:13 27 49 38 65 97 76 493遍排序:13 27 38 49 49 65 97 764遍排序:13 27 38 49 49 65 76 97

空间效率:常数个辅组单元,空间复杂度O(1)
时间效率:最坏情况下为O(n2),最好为O(n)。平均为O(n2)
稳定性:稳定

(2)快速排序

快速排序的基本思想是基于分治法的:在待排序的表L[1…n]中人去一个元素pivot作为枢纽(通常取首元素),通过一趟快速排序将表划分成两个部分L[1…k-1]和L[k+1…n]。L[1…k-1]中元素都小于pivot和L[k+1…n]中元素都大于pivot,L[k]放pivot。

template<class T>
int Partition(T* arry, int low, int high)
{
	T pivot = arry[low];  //当前元素第一个当作pivot
	while (low < high)
	{
		while (low < high && arry[high] >= pivot)
			high--;
		arry[low] = arry[high];   //将比枢轴元素小的元素放到左端
		while (low < high && arry[low] <= pivot)
			low++;
		arry[high] = arry[low];   //将比枢轴元素大的元素放到右端
	}
	arry[low] = pivot;  //将枢轴放到最终位置

	return low;  //返回枢轴最终存放的位置
}

template<class T>
void QuickSort(T* arry, int low, int high)
{
	if (low < high)  //递归跳出条件
	{
		int pivotpos = Partition(arry, low, high);  //划分
		QuickSort(arry, low, pivotpos - 1);  //对子表递归
		QuickSort(arry, pivotpos+1, high);
	}
}

输出:
int arry[8] = { 49,38,65,97,76,13,27,49 };1遍排序:27 38 13 49 76 97 65 492遍排序:13 27 38 49 76 97 65 493遍排序:13 27 38 49 49 65 76 974遍排序:13 27 38 49 49 65 76 97

空间效率:O(log2n)
时间效率:O(nlog2n)
稳定性:不稳定
快速排序是所有内部排序算法中平均性能最优的算法。

四、选择排序

选择排序的基本思想是:每一趟(如第i趟)在后面n-i+1个待排序元素中找到最小元素,作为有序序列第i个元素。

(1)简单选择排序

template<class T>
void SelectSort(T* arry, int n)
{
	for (int i = 0; i < n - 1; i++)  //一共n-1趟
	{
		int min = i;  //记录最小元素位置
		for (int j = i + 1; j < n; j++)//在位置[i+1...n-1]中找到最小元素位置
		{
			if (arry[j] < arry[min])
			{
				min = j;  
			}
		}
		swap(arry[i], arry[min]);//交换min元素到位置i,把i位置元素交换到min处
	}
}
输出:
int arry[8] = { 49,38,65,97,76,13,27,49 };1遍排序:13 38 65 97 76 49 27 492遍排序:13 27 65 97 76 49 38 493遍排序:13 27 38 97 76 49 65 494遍排序:13 27 38 49 76 97 65 495遍排序:13 27 38 49 49 97 65 766遍排序:13 27 38 49 49 65 97 767遍排序:13 27 38 49 49 65 76 97

空间效率:O(1)
时间效率:O(n2)
稳定性:不稳定

(2)堆排序

堆的定义:n个关键字序列L[1…n]称为堆
①L(i)>=L(2i)且L(i)>=L(2i+1) 或者
②L(i)<=L(2i)且L(i)<=L(2i+1)
满足①称为大顶堆;满足②称为小顶堆

建立大顶堆的算法

template <class T>
void HeadAdjust(T* arry, int k, int len)
{
	int temp = arry[k];
	for (int i = k * 2; i <= len; i = i * 2)
	{
		if (i < len && arry[i] < arry[i + 1])//比较k节点的两个子节点
		{
			i++;                             //取较大的一个子节点下标
		}
		if (temp > arry[i])    //k节点即根节点,比它的孩子节点的值都大,不用调整
			break;
		else
		{
			arry[k] = arry[i];  //调整值大的节点为k节点
			k = i;
		}
	}
	arry[k] = temp;
}

template<class T>
void BulidMaxHeap(T* arry, int len)
{
	for (int i = len/2; i >=1; i--)
	{
		HeadAdjust(arry, i, len);
	}
}

堆排序算法

template<class T>
void HeapSort(T* arry, int len)
{
	BulidMaxHeap(arry, len);
	for (int i = len; i > 1; i--)
	{
		swap(arry[i], arry[1]);
		HeadAdjust(arry, 1, i - 1);
	}
}

输入:
int arry[9] = { 0,53,17,78,9,45,65,87,32};
大顶堆的输出,从下标1开始,下标0忽略
87 45 78 32 17 65 53 9
78 45 65 32 17 9 53
65 45 53 32 17 9
53 45 9 32 17
45 32 9 17
32 17 9
17 9
9

空间效率:空间复杂度O(1)
时间效率:建堆时间O(n),堆排序时间复杂度O(nlog2n)
稳定性:不稳定

五、归并排序

(1)归并排序

归并是将两个或者两个以上的有序表合并成一个有序表。
下面描述2路归并算法:

template<class T>
void Merge(T* arry, int low, int mid, int high)
{
	T* temp=new T[high+1];  //辅助数组
	for (int i = low; i <= high; i++)
	{
		temp[i] = arry[i]; //把arry数组元素全部复制到temp中
	}
	int i,j,k;
	for ( i = low, j=mid+1, k=i; i<=mid&&j<=high; k++)//比较左右两个半边数组元素大小,依次放入到arry数组中去
	{
		if (temp[i] < temp[j])
		{
			arry[k] = temp[i++];
		}
		else
		{
			arry[k] = temp[j++];
		}
	}
	while (i<=mid) //把左半边没有放进去的元素放进arry中
	{
		arry[k++] = temp[i++];
	}
	while (j<=high)//把右半边没有放进去的元素放进arry中
	{
		arry[k++] = temp[j++];
	}
}


template<class T>
void MergeSort(T* arry, int low, int high)
{
	if (low < high)
	{
		int mid = (low + high) / 2;  //划分
		MergeSort(arry, low, mid);   //对左侧递归
		MergeSort(arry, mid + 1, high); //对右侧递归
		Merge(arry, low, mid, high);  //合并
	}
}

空间效率:需要n个辅组单元,O(n)
时间效率:O(nlog2n)
稳定性:稳定

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值