算法--排序集合

#复杂度
排序
排序算法 平均时间复杂度 最差时间复杂度 空间复杂度 数据对象稳定性
冒泡排序 O(n2) O(n2) O(1) 稳定
选择排序 O(n2) O(n2) O(1) 数组不稳定、链表稳定
插入排序 O(n2) O(n2) O(1) 稳定
快速排序 O(nlog2n) O(n2) O(log2n) 不稳定
堆排序 O(n
log2n) O(nlog2n) O(1) 不稳定
归并排序 O(n
log2n) O(nlog2n) O(n) 稳定
希尔排序 O(n
log2n) O(n2) O(1) 不稳定
计数排序 O(n+m) O(n+m) O(n+m) 稳定
桶排序 O(n) O(n) O(m) 稳定

//冒泡排序
作用: 最常用的排序算法,对数组内元素进行排序
1.比较相邻的元素。如果第一个比第二个大,就交换他们两个。
2.对每一对相邻元素做同样的工作,执行完毕后,找到第一个最大值。
3.重复以上的步骤,每次比较次数-1,直到不需要比较
解析:2,4,8,0,5,7,1,3,9
每轮对比次数 8: 2 4 0 5 7 1 3 8 9 排序轮数 0 找到第一个最大值 之后每一次都找到一个最大值 一共进行8次
7: 2 0 4 5 1 3 7 8 1
6: 0 2 4 1 3 5 7 2
5: 0 2 1 3 4 5 3
4: 0 1 2 3 4 4
3: 0 1 2 3 5
2: 0 1 2 6
1: 0 1 7

排序总轮数(8) = 元素个数 - 1
每轮对比次数 = 元素个数 – 轮数 - 1
没想出来,官方给出:

#include
using namespace std;

int main() {
int arr[9] = { 2,4,8,0,5,7,1,3,9 };

cout << "排序前:" << endl;

for (int i = 0;i < 9;i++)
{
	cout << arr[i] << " ";
}
cout << endl;
//开始冒泡排序
//总共排序轮数为 元素个数 - 1
for (int i = 0;i < 9 - 1;i++)
{
	//内层循环对比  次数 =  元素个数 - 当前轮数 - 1
	for (int j = 0;j < 9 - i - 1;j++)
	{
		//交换 两两相邻一直交换,每轮确定一个最右侧的最大值
		if (arr[j] > arr[j + 1])
		{
			int temp = arr[j];
			arr[j] = arr[j + 1]; 
			arr[j + 1] = temp;
		}
	}
	注意:这里不写cout << arr[i],因为还没有轮换完成
}
cout << "排序后:" << endl;
for (int i = 0;i < 9;i++)
{
	cout << arr[i] << " ";
}
	system("pause");

return 0;
}

//选择排序
从数组中选择最小元素,将它与数组的第一个元素交换位置。再从数组剩下的元素中选择出最小的元素,将它与数组的第二个元素交换位置。不断进行这样的操作,直到将整个数组排序。
选择排序需要 ~N2/2 次比较和 ~N 次交换,它的运行时间与输入无关,这个特点使得它对一个已经排序的数组也需要这么多的比较和交换操作。
void SelectSort(int arr[], int length)
{
int min = 0;//第0轮 将min置为第一个数
for (int i = 0;i < length - 1;i++)//总共比较length-1轮,每轮都假设第i轮的第i
个数为最小
{
min = i;
for (int j = i + 1;j < length;j++)//从第二个数开始到最后一个 如果其值比arr[min]小 就交换下标
{
if (arr[j] < arr[min])
{
min = j;
}
}

	if (min != i)//判断如果arr[min]不为当前的arr[i] 则交换
	{
		Swap(&arr[i], &arr[min]);
	}
}

}
综上,选择排序比冒泡排序减少了交换次数,它是确定了最小值的下标才进行交换的,而不是两两相邻一直交换,不断找出最右侧的最大值。
//插入排序
每次都将当前元素插入到左侧已经排序的数组中,使得插入之后左侧数组依然有序。
对于数组 {3, 5, 2, 4, 1},它具有以下逆序:(3, 2), (3, 1), (5, 2), (5, 4), (5, 1), (2, 1), (4, 1),插入排序每次只能交换相邻元素,令逆序数量减少 1,因此插入排序需要交换的次数为逆序数量。
插入排序的时间复杂度取决于数组的初始顺序,如果数组已经部分有序了,那么逆序较少,需要的交换次数也就较少,时间复杂度较低。
平均情况下插入排序需要 ~N 2 /4 比较以及 ~N 2 /4 次交换;
最坏的情况下需要 ~N 2 /2 比较以及 ~N 2 /2 次交换,最坏的情况是数组是倒序的;
最好的情况下需要 N-1 次比较和 0 次交换,最好的情况就是数组已经有序了。
用容器的更好办法:
void InsertSort(vector& v)
{
int len = v.size();
for (int i = 1; i < len; ++i) {
int temp = v[i];
for (int j = i - 1; j >= 0; --j)
{
if (v[j] > temp)
{
v[j + 1] = v[j];
v[j] = temp;
}
else
break;
}
}
}

黑马方法:
int main()
{
int arr[9]={ 2,4,8,0,5,7,1,3,9 };

cout << "排序前:" << endl;

for (int i = 0;i < 9;i++)
{
	cout << arr[i] << " ";
}
cout << endl; 

//插入排序
int j = 0;
for (int i = 1;i < 9;i++)//从第二个元素开始 与第一个比较
{
	int temp = arr[i];//缓存当前节点
	for (j = i - 1;j >= 0 && temp < arr[j];j--)//j是i前一个数 比较两者大小 如果arr[j]比较大 则让arr[j]的数向后移动
	{
		arr[j + 1] = arr[j];
	}

	arr[j + 1] = temp;//把当前结点插入到合适位置
}

cout << "排序后:" << endl;

for (int i = 0;i < 9;i++)
{
	cout << arr[i] << " ";
}
cout << endl;

system("pause");
return 0;

}
//希尔排序
对于大规模的数组,插入排序很慢,因为它只能交换相邻的元素,每次只能将逆序数量减少 1。希尔排序的出现就是
为了解决插入排序的这种局限性,它通过交换不相邻的元素,每次可以将逆序数量减少大于 1。
希尔排序使用插入排序对间隔 h 的序列进行排序。通过不断减小 h,最后令 h=1,就可以使得整个数组是有序的。

那么怎么确定分组呢?这个增量确定为increasement=inc/3+1
//希尔排序
int increasement = 9;
int i, j, k;
do
{
//确定分组的增量
increasement = increasement / 3 + 1;
for (i = 0;i < increasement;i++)
{
for (j = i + increasement;j < 9;j += increasement)
{
if (arr[j] < arr[j - increasement]) {
int temp = arr[j];
for (k = j - increasement;k >= 0 && temp < arr[k];k -= increasement)
{
arr[k + increasement] = arr[k];
}
arr[k + increasement] = temp;
}
}
}

} while (increasement > 1);

//快速排序

  1. 基本算法
    归并排序将数组分为两个子数组分别排序,并将有序的子数组归并使得整个数组排序;
    快速排序通过一个切分元素将数组分为两个子数组,左子数组小于等于切分元素,右子数组大于等于切分元素,将这两个子数组排序也就将整个数组排序了。

#define _CRT_SECURE_NO_WARNINGS
#include
using namespace std;
#include

//快速排序 从小到大
void QuickSort(vector& v, int start, int end) {
if (start >= end) // 结束标志
return;
int i = start; // 低位下标
int j = end; // 高位下标
int key = v[i]; // 设第一个为基准

while (i < j)
{
	// 将比第一个小的移到前面
	while (i < j && v[j] >= key)
		j--;
	if (i < j)
		v[i++] = v[j];//i++;可以分开写


	// 将比第一个大的移到后面
	while (i < j && v[i] <= key)
		i++;
	if (i < j)
		v[j--] = v[i];
}
// 基准置位
v[i] = key;//给v[j]也行 此时i=j
// 前半递归
QuickSort(v, start, i - 1);
// 后半递归
QuickSort(v, i + 1, end);

}

void printVector(vector& v)
{
for (vector::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}

int main()
{
//vector v1;
//int len = v1.size();
//v1.push_back(9);
//v1.push_back(4);
//v1.push_back(6);
//v1.push_back(1);
//v1.push_back(3);
//v1.push_back(8);
//v1.push_back(2);
//v1.push_back(5);
//这样插入数据两次结果都一样未排序 存疑

int arr[9] = { 4,2,8,0,5,7,1,3,9 };

vector<int> v1(arr, arr + sizeof(arr) / sizeof(arr[0]));
int len = v1.size();
cout << "排序前:" << endl;
for (vector<int>::iterator it = v1.begin(); it != v1.end(); it++) {
	cout << *it << " ";
}
cout << endl;

QuickSort(v1, 0, len - 1);//注意不能传入8 因为有递归 所以不能输入确切的值

cout << "排序后:" << endl;

for (vector<int>::iterator it = v1.begin(); it != v1.end(); it++) {
	cout << *it << " ";
}
cout << endl;

system("pause");
return 0;

}
//归并排序
归并方法将数组中两个已经排序的部分归并成一个。

模板简洁高b格写法
template
void merge_sort_recursive(T arr[], T reg[], int start, int end) {
if (start >= end)
return;
int len = end - start, mid = (len >> 1) + start;
int start1 = start, end1 = mid;
int start2 = mid + 1, end2 = end;
merge_sort_recursive(arr, reg, start1, end1);
merge_sort_recursive(arr, reg, start2, end2);
int k = start;
while (start1 <= end1 && start2 <= end2)
reg[k++] = arr[start1] < arr[start2] ? arr[start1++] : arr[start2++];
while (start1 <= end1)
reg[k++] = arr[start1++];
while (start2 <= end2)
reg[k++] = arr[start2++];
for (k = start; k <= end; k++)
arr[k] = reg[k];
}
//整數或浮點數皆可使用,若要使用物件(class)時必須設定"小於"(<)的運算子功能
template
void merge_sort(T arr[], const int len) {
T *reg = new T[len];
merge_sort_recursive(arr, reg, 0, len - 1);
delete[] reg;
}

#include
using namespace std;
#include<time.h>
#include<sys/timeb.h>

#define MAX 10

int* CreateArray()
{
srand((unsigned int)time(NULL));
int* arr = (int*)malloc(sizeof(int)*MAX);
for (int i = 0;i < MAX;i++)
{
arr[i] = rand() % MAX;
}
return arr;
}

//合并算法
void Merge(int arr[], int start, int end,int mid,int* temp)
{
int i_start = start;
int i_end = mid;
int j_start = mid + 1;
int j_end = end;

//表示辅助空间有多少个元素
int length = 0;

//合并两个有序序列
while (i_start <= i_end && j_start <= j_end)
{
	if (arr[i_start] < arr[j_start])
	{
		temp[length] = arr[i_start];
		length++;
		i_start++;
	}
	else {
		temp[length] = arr[j_start];
		j_start++;
		length++;
	}
}

//i这个序列
while (i_start <= i_end)
{
	temp[length] = arr[i_start];
	i_start++;
	length++;
}

//j序列
while (j_start <= j_end)
{
	temp[length] = arr[j_start];
	length++;
	j_start++;
}

//辅助空间数据覆盖原空间
for (int i = 0;i < length;i++)
{
	arr[start + i] = temp[i];

}

}

//归并排序
void MergeSort(int arr[],int start,int end,int* temp)
{
//判断何时结束
if (start >= end) {
return;
}

int mid = (start + end) / 2;
//递归分组
//左边
MergeSort(arr, start, mid, temp);
//右边
MergeSort(arr, mid + 1, end, temp);

//合并
Merge(arr, start, end, mid, temp);

}

int main()
{
int* myArr = CreateArray();
//辅助空间
int* temp = (int*)malloc(sizeof(int)*MAX);

cout << "排序前:" << endl;

for (int i = 0;i < MAX;i++)
{
	cout << myArr[i] << " ";
}
cout << endl;

MergeSort(myArr, 0, MAX - 1,temp);

cout << "排序后:" << endl;

for (int i = 0;i < MAX;i++)
{
	cout << myArr[i] << " ";
}
cout << endl;

//释放空间
free(temp);
free(myArr);

system("pause");
return 0;

}
//堆排序


  1. 堆中某个节点的值总是大于等于其子节点的值,并且堆是一颗完全二叉树。
    堆可以用数组来表示,这是因为堆是完全二叉树,而完全二叉树很容易就存储在数组中。位置 k 的节点的父节点位置为 k/2,而它的两个子节点的位置分别为 2k 和 2k+1。这里不使用数组索引为 0 的位置,是为了更清晰地描述节点的位置关系。

大顶堆:让元素大的数放在最上面 再与最后一个结点交换 最终实现从小到大排序
#include
using namespace std;

void PrintArray(int arr[], int len) {
for (int i = 0;i < len;i++)
{
cout << arr[i] << " ";
}
cout << endl;
}

void MySwap(int arr[], int a, int b)
{
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}

//myArr 待调整的数组
//index 待调整的结点的下标
//len 数组的长度

void HeapAdjust(int arr[], int index, int len)
{
//先保存当前结点的下标
int max = index;
//保存左右孩子的数组下标
int lchild = index * 2 + 1;
int rchild = index * 2 + 2;
if (lchild < len && arr[lchild] > arr[max]) {//判断小于len是防止它没有子结点
max = lchild;
}
if (rchild < len && arr[rchild] > arr[max]) {
max = rchild;
}
if (max != index)
{
//交换
MySwap(arr, max, index);//递归是为了排序其他的结点 使他们满足大的数在上面

	HeapAdjust(arr,max,len);//比如max为1时 执行上述交换 max变为结点3 这里用递归 继续排序左下角三个数
}

}

//堆排序
void HeapSort(int myArr[], int len) {
//初始化堆
for (int i = len / 2 - 1;i >= 0;i–)//因为从0开始算 4这个数位于第三个
{
HeapAdjust(myArr, i, len);//先找到第一个最大数9排在最上面
}

//交换堆顶元素和最后一个元素
for (int i = len - 1;i > =0;i--)
{
	MySwap(myArr, 0, i);//9 和 0交换之后 arr[0]这个位置不符合堆排序概念了
	HeapAdjust(myArr, 0, i);所以从0到len-1这个位置之间再进行堆排序调整
}

}

int main()
{
int myArr[] = { 4,2,8,0,5,7,1,3,9 };
int len = sizeof(myArr) / sizeof(int);
PrintArray(myArr, len);
//堆排序
HeapSort(myArr, len);

PrintArray(myArr, len);

system("pause");
return 0;

}
当max为结点3

变为

当max为结点2时不用动,当max为结点1时
变为

但是2 3 0还需要排序 这时就需要递归了

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值