8大排序整理

1. 交换排序

冒泡排序

最快平均最慢空间复杂度稳定性复杂性
o(n)o(n2)o(n2)o(1)稳定简单

冒泡排序是一种用时间换空间的排序方法

最坏情况是把顺序的排列变成逆序,或者把逆序的数列变成顺序,最差时间复杂度O(N^2)只是表示其操作次数的数量级。

最好的情况是数据本来就有序,复杂度为O(n)

#include <stdlib.h>
#include <time.h>
#include <sys/timeb.h>

#include<iostream>

#define NUM 10000

using T = int;
void print(T arr[], int nNum);
void swap(T *a, T *b);
long getSystemTime();
void BubbleSort(T arr[], int nNum)
{
	T i = 0, j = 0;
	for (i = 0; i < nNum; ++i)
		for (j = 1; j < nNum - i; ++j)
			if (arr[j - 1] > arr[j])
			{
				swap(&arr[j - 1], &arr[j]);
			}
}


int main()
{
	int arr[NUM] = { 0 };
	long t_start = 0, t_end = 0;

	srand((unsigned int)time(NULL));

	for (int i = 0; i < NUM; ++i)
	{
		arr[i] = rand() % 100;
	}

	//print(arr,NUM);
	t_start = getSystemTime();
	BubbleSort(arr,NUM);
	t_end = getSystemTime();
	//print(arr, NUM);
	std::cout << "time:" << t_end - t_start << std::endl;
	return 0;
}


void swap(T *a, T *b)
{
	*a ^= *b;
	*b ^= *a;
	*a ^= *b;
}



void print(T arr[], int nNum)
{
	for (int i = 0; i < NUM; ++i)
	{
		std::cout << arr[i];
		std::cout << ",";
	}
	std::cout << "\b\n" << std::endl;
}

long getSystemTime()
{
	struct timeb tb;
	ftime(&tb);
	return tb.time * 1000 + tb.millitm;
}

快速排序

所有排序算法里命名最嚣张的一个。

最快平均最慢空间复杂度稳定性复杂性
o(N*log2N)o(N*log2N)o(n2)o(log2N)-o(N)不稳定复杂

快速排序以空间换时间,但却是效率高不稳定的排序算法。元素越多优势越明显。

划分之后一边是一个,一边是n-1个,这种极端情况的时间复杂度就是O(N^2)

最好的情况是每次都能均匀的划分序列,O(N*log2N)


void QuickSort(T arr[], int start, int end)
{
	int left = start, right = end;
	T base = arr[start];

	if (left < right)
	{
		while (left < right)
		{
			// search <base from right
			while (left < right && arr[right] >= base) --right;
			if (left < right)
			{
				arr[left] = arr[right];
				++left;
			}

			// search >base from left
			while (left < right && arr[left] < base) ++left;
			if (left < right)
			{
				arr[right] = arr[left];
				--right;
			}
		}
		arr[left] = base;

		QuickSort(arr, 0, left - 1);
		QuickSort(arr, left + 1, end);

	}

}

2. 选择排序

直接排序

最快平均最慢空间复杂度稳定性复杂性
o(n)o(n2)o(n2)o(1)不稳定简单

比冒泡快,因为交换少。

void SelectSort(T arr[], int nNum)
{
	T i = 0, j = 0;
	for (i = 0; i < nNum; ++i)
	{
		T min = i;
		for (j = 1; j < nNum - i; ++j)
		{
			if (arr[j - 1] > arr[j])
			{
				min = j;
			}

		}

		if (min != i)
		{
			swap(&arr[min], &arr[i]);
		}

	}
}

堆排序

最坏,最好,平均时间复杂度均为O(nlog2n),不稳定且复杂。

堆是一种完全二叉树:

  • 大顶堆:arr[i] >= arr[2i + 1] && arr[i] >= arr[2i + 2]
  • 小顶堆:arr[i] <= arr[2i + 1] && arr[i] <= arr[2i + 2]

步骤:

  1. 将序列构建称为大顶堆;
  2. 取出根节点,与末尾元素交换;
  3. 对交换后的n-1个序列元素进行调整,使其满足大顶堆的性质,然后交换;

总之就是建堆和交换,不断重复。

#include <windows.h>
#include <iostream>

using namespace std;

using T = int;

void print(T arr[], int nNum)
{
	HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
	COORD pos = { 0 };
	short x = 40, y = 0;
	int rowNum = 1;


	pos.X = x;
	pos.Y = y;
	SetConsoleCursorPosition(hOut, pos);
	printf("%2d",arr[0]);

	for (int i = 1; i < nNum; i += rowNum)
	{
		x -= 3;
		pos.X = x;
		pos.Y = ++y;
		SetConsoleCursorPosition(hOut, pos);

		rowNum *= 2;
		for (int j = 0; j < rowNum && i + j < nNum; ++j)
		{
			printf("%2d   ", arr[i + j]);
		}
	}
	system("pause");
	system("cls");
}

void HeapAdjust(T arr[], int index, int nLen)
{
	int max = index;
	int lchild = index * 2 + 1;
	int rchild = index * 2 + 2;

	if (lchild < nLen && arr[lchild] > arr[max])
	{
		max = lchild;
	}
	if (rchild < nLen && arr[rchild] > arr[max])
	{
		max = rchild;
	}

	if (max != index)
	{
		T tmp = arr[max];
		arr[max] = arr[index];
		arr[index] = tmp;

		HeapAdjust(arr,max,nLen);
	}
}

void HeapSort(T arr[], int nLen)
{
	// 1. Init big root heap
	for (int i = nLen / 2 - 1; i >= 0; --i)
	{
		HeapAdjust(arr, i, nLen);
	}

	// 2. swap
	for (int i = nLen - 1; i >= 0; --i)
	{
		T tmp = arr[0];
		arr[0] = arr[i];
		arr[i] = tmp;
		HeapAdjust(arr, 0, i);
	}
}

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

	system("pause");
	return 0;
}

3. 插入排序

直接插入

类似打扑克。。。

两种情况下效率高:

  • 序列基本有序
  • 元素少
最快平均最慢空间复杂度稳定性复杂性
o(n)o(n2)o(n2)o(1)稳定简单
void InsertSort(T arr[], int nNum)
{
	T i = 0, j = 0;
	for (i = 1; i < nNum; ++i)
	{
		if (arr[i] < arr[i - 1])
		{
			T min = arr[i];
			for (j = i- 1; 
				j >= 0 && min < arr[j];
				--j)
			{
				arr[j + 1] = arr[j];
			}
			arr[j + 1] = min;
		}

	}
}

希尔排序

最快平均最慢空间复杂度稳定性复杂性
o(n)o(n^(1.3))o(n2)o(1)不稳定复杂

实质上是一种分组插入方法,又称缩小增量排序,是插入排序的高效版本。

数列有n个元素,取一个小于n的整数gap,将待排序元素分成若干个组子序列,所有距离为gap的倍数的记录放在同一个组中;然后,对各组内的元素进行直接插入排序。 这一趟排序完成之后,每一个组的元素都是有序的。然后减小gap的值,并重复执行上述的分组和排序。重复这样的操作,当gap=1时,整个数列就是有序的。

步长递减:gap = gap / 3 + 1;

void ShellSort(T arr[], int nNum)
{
	T i = 0, j = 0, k = 0;
	int increasement = nNum;

	do
	{
		increasement = increasement / 3 + 1;
		for (i = 0; i < increasement; ++i)
		{
            
                /*
                    The same as InsertSort()
                */
			for (j = i + increasement; j < nNum; j += increasement)
			{
				if (arr[j] < arr[j - increasement])
				{
					T min = arr[j];
					for (k = j - increasement;
						k >= 0 && min < arr[k];
						k -= increasement)
					{
						arr[k + increasement] = arr[k];
					}

					arr[k + increasement] = min;
				}
			}


		}
	} while (increasement > 1);
}

4. 归并排序

3种时间复杂度均为o(nlog2n),空间复杂度o(n)。稳定但复杂。

数量达到一定级别后,必须利用多线程,计算后合并。

void Merge(T arr[], int start, int end, int mid)
{
	int i_start = start, i_end = mid;
	int j_start = mid + 1, j_end = end;

	int nLen = 0;

	T buf[NUM] = { 0 };

	while (i_start <= i_end && j_start <= j_end)
	{
		if (arr[i_start] < arr[j_start])
		{
			buf[nLen] = arr[i_start];
			++nLen;
			++i_start;
		}
		else
		{
			buf[nLen] = arr[j_start];
			++nLen;
			++j_start;
		}
	}
	while (i_start <= i_end)
	{
		buf[nLen] = arr[i_start];
		++nLen;
		++i_start;
	}
	while (j_start <= j_end)
	{
		buf[nLen] = arr[j_start];
		++nLen;
		++j_start;
	}
	for (int i = 0; i < nLen; ++i)
	{
		arr[start + i] = buf[i];
	}
}

// interface
void MergeSort(T arr[], int start, int end)
{
	if (start == end) return;

	int mid = (start + end) / 2;

	MergeSort(arr,start,mid);
	MergeSort(arr, mid + 1, end);

	Merge(arr,start, end, mid);
}

5. 基数排序

最坏,最好,平均时间复杂度O(d(k+n)),稳定,复杂。

空间复杂度:O(n+k)

k为常数

稳定,复杂(代码还没看懂)。

算法:按位(个位、十位、百位。。。)排序,

基数排序不是比较排序,而是通过分配和收集的过程来实现排序

初始化10个桶(固定的),桶下标为0-9
根据待排序数字的对应位的数字,把这个数字对应的item放到对应的桶中

两种排序方式:LSD和MSD,最小位优先(从右边开始)和最大位优先(从左边开始)

#include <iostream>

int maxbit(int arr[], int nLen)
{
	int max = 1; //保存最大的位数
	int p = 10;
	for (int i = 0; i < nLen; ++i)
	{
		while (arr[i] >= p)
		{
			p *= 10;
			++max;
		}
	}
	return max;
}
void RadixSort(int arr[], int nLen) //基数排序
{
	int d = maxbit(arr, nLen);
	int *tmp = new int[nLen];
	int count[10]; //计数器
	int i, j, k;
	int radix = 1;

	for (i = 0; i < d; ++i) //进行d次排序
	{
		
		memset(count, 0, 10 * sizeof(int));

		for (j = 0; j < nLen; ++j)
		{
			k = (arr[j] / radix) % 10; //统计每个桶中的记录数
			count[k]++;
		}

		for (j = 1; j < 10; ++j)
			count[j] += count[j - 1]; //将tmp中的位置依次分配给每个桶

		for (j = nLen - 1; j >= 0; --j) //将所有桶中记录依次收集到tmp中
		{
			k = (arr[j] / radix) % 10;
			tmp[ count[k] - 1 ] = arr[j];
			--count[k];
		}
		for (j = 0; j < nLen; ++j) //将临时数组的内容复制到arr中
			arr[j] = tmp[j];
		radix = radix * 10;
	}
	delete[] tmp;
}

int main()
{
	int arr[10] = {9,51,50,10,54,16,78,51,23};
	RadixSort(arr,10);
	for (int i = 0; i < 10; ++i)
	{
		printf("%d ",arr[i]);
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值