八大基础排序

这篇博客详细介绍了八种基础排序算法:简单插入排序、希尔排序、冒泡排序、快速排序、选择排序、堆排序、归并排序和基数排序。每种排序算法的特点、时间复杂度和空间复杂度都进行了说明,对于理解排序算法的原理和应用非常有帮助。
摘要由CSDN通过智能技术生成

一、简单插入排序
有一个已经有序的数据序列,要求在这个已经排好的数据序列插入一个数,但要求插入此数后,数据序列仍然有序,此时就需要用插入排序。插入排序的基本操作就是将一个数据插入到已经排好序的有序数据中,从而得到一个新的,个数+1的有序数列,算法适用于少量数据的排序。
具有稳定性
时间复杂度O(n^2),空间复杂度O(1)
(简单)直接插入排序特点:数据越有序越快,完全有序则为O(n)

#include<stdio.h>
void InsertSort(int arr[],int len)
{
 int i=1;
 int j=i-1;
 int tmp;
 for(i;i<len;i++)
 {
  tmp=arr[i];
  for(j=i-1;j>=0&&arr[j]>tmp;j--)//以排序部分还没有遍历完成
  {
   arr[j+1]=arr[j];                  
  }
  arr[j+1]=tmp;
 }
}

二、希尔插入排序
希尔排序:间隔式的分组,利用插入排序让组内有序,然后缩小分组直到组数为1则完全有序
一趟shell过程,gap为组数(间隔)
时间复杂度O(n1.3~n1.5),空间复杂度O(1),不稳定

#include<stdio.h>
void Shell(int arr[],int len,int dk)
{
 int i;
 int j;
 int tmp;
 for(i=dk;i<len;i++)
 {
  tmp=arr[i];
  for(j=i-dk;j>=0&&arr[j]>tmp;j-=dk)//以排序部分还没有遍历完成
  {
   arr[j+dk]=arr[j];                  
  }
  arr[j+dk]=tmp;
 }
}
void ShellSort(int arr[],int len)
{
 int dk[]={5,3,1};
 int dk_len=sizeof(dk)/sizeof(dk[0]);
 int i=0;
 for(i;i<dk_len;i++)
 {
  Shell(arr,len,dk[i]);
 }
}

三、冒泡排序:两两比较,大的往后
时间复杂度O(n²),空间复杂度O(1),具有稳定性

#include<stdio.h>
void BubbleSort(int* arr,int len)=
{
	int tmp;
	for(int i = 0;i<len-1;i++)//趟数
	{
		for(int j = 0;j+1<len-i;j++)//注意j+1越界
		{
			if(arr[j] > arr[j+1])//交换
			{
				tmp = arr[j];
				arr[j] = arr[j+1];
				arr[j+1] = tmp;
			}
		}
	}
}

四、快速排序:
1.找到一个基准(第一给数据)
2.从后往前找比基准值小的数字,找到往前移
3.从前往后找比基准值大的数字,找到往后移
4.重复2,3直到找到基准位置
时间复杂度O(nlogn);空间复杂度O(logn)
快速排序的缺点:1.越有序越慢,完全有序则为O(n²);2.空间复杂度为O(logn),不是O(1);3.不稳定

//快速排序的一次划分
int Partition(int *arr,int low,int high)
{
	int tmp = arr[low];//基准值
	while(low<high)
	{
		//从后往前找比基准值小的数据,往前移
		while(low<high&&arr[high]>tmp)
			high--;
		if(low < high)
			arr[low] = arr[high];
		//从前往后找比基准大的数据,往后移
		while(low<high&&arr[low]<=tmp)
			low++;
		if(low < high)
			arr[high] = arr[low];
	}
	arr[low] = tmp;
	return low;
}
void Quick(int* arr,int low,int high)
{
	int par = Partition (arr,low,high);
	if(low < par-1)//左边数据超过一个
	{
		Quick(arr,low,par-1);
	}
	if(par+1<high)//右边数据超过一个
	{
		Quick(arr,par+1,high);
	}
}
void QuickSort(int* arr,int len)
{
	Quick (arr,0,len-1);
}

五、选择排序:每次从待排序中找到最小值和待排序的第一个交换
时间复杂度:时间复杂度:O(n²);空间复杂度:O(1) ;不稳定

#include<stdio.h>
void SelectSort(int* arr,int len)
{
	int minIndex;//最小值下标
	int tmp;
	for(int i = 0;i<len - 1;i++)
	{
		minIndex = i;
		for(int j = i+1;j<len;j++)//找最小值
		{
			if(arr[minIndex]>arr[j])
			{
				minIndex = j;
			}
		}
		tmp = arr[minIndex];
		arr[minIndex] = arr[i];
		arr[i] = tmp;
	}
}

六、堆排序
时间复杂度:时间复杂度:O(nlogn);空间复杂度:O(1);不稳定

#include<stdio.h>
//堆调整,从上往下调整,start为起始下标,end为结束下标
void HeapAdjust(int* arr,int start,int end)
{
	int tmp = arr[start];
	for(int i = 2*start+1;i<=end;i=2*i+1)
	{
		if(i<end&&arr[i]<arr[i+1])//有右孩子,且左孩子的值小于右孩子
			i++;//i一定是左右孩子较大值的下标
		if(arr[i]>tmp)
		{
			arr[start] = arr[i];
			start = i;
		}
		else//找到位置了
		{
			break;
		}
	}
	arr[start] = tmp;
}
void HeapSort(int* arr,int len)
{
	//第一次建大根堆,从后往前,多次调整
	int i;
	for(i = (len-1-1)/2;i>=0;i--)//从最后一颗子树开始
	{
		HeapAdjust (arr,i,len-1);
	}
	//每次将根和待排序的最后一个交换,然后在调整
	int tmp;
	for(i = 0;i<len-1;i++)
	{
		//交换
		tmp = arr[0];
		arr[0] = arr[len-1-i];
		arr[len-1-i] = tmp;

		//再调整
		HeapAdjust(arr,0,len-1-i-1);
	}
}

七、归并排序:将两段有序的数据合并成一段有序的数据,直到所有的数据有序
时间复杂度:O(nlogn);空间复杂度O(n);稳定

#include<stdio.h>
//一次归并
//gap归并段长度
static void Merge(int* arr,int len,int gap)
{
	int low1 = 0;//第一个归并段的起始下标
	int high1 = low1+gap-1;//第一个归并段的结束下标
	int low2 = high1+1;//第二个归并段的起始下标
	int high2 = low2+gap<len?low2+gap-1:len-1;//第二个归并段结束下标
	int* brr = (int*)malloc(sizeof(int)*len);//存放归并好的数据
	int i = 0;//brr下标
	assert(brr!=NULL);
	//有两个归并段
	while(low2 < len)
	{
		//两个归并段都还有数据,需要比
		while(low1<=high1&&low2<=high2)
		{
			if(arr[low1] <= arr[low2])
			{
				brr[i++] = arr[low1++];
			}
			else
			{
				brr[i++] = arr[low2++];
			}
		}
		//一个归并段的数据已经完成,另一个还有数据
		while(low1<=high1)
			brr[i++] = arr[low1++];
		while(low2<=high2)
			brr[i++] = arr[low2++];
		//下两个归并段
		low1 = high2+1;
		high1 = low1+gap-1;
		low2 = high1+1;
		high2 = low2+gap<len?low2+gap-1:len-1;
	}
	//只有一个归并段
	while(low1<len)
	{
		brr[i++] = arr[low1++];
	}

	//将归并好的数据拷贝到arr中
	for(i = 0;i<len;i++)
	{
		arr[i] = brr[i];
	}
	free(brr);
}
void MergeSort(int* arr,int len)
{
	for(int i = 1;i<len;i*=2)
	{
		Merge(arr,len,i);
	}
}

法二:不用拷贝

#include<iostream>
#include<vector>
#include<assert.h>
#include<stdlib.h>
#include<deque>
#include<limits.h>
using namespace std;
//一次归并
void Merge(int* dist, int* src, int left, int m, int right)
{
	int i = left, j = m + 1;
	int k = left;
	while (i <= m && j <= right)
	{
		dist[k++] = src[i] < src[j] ? src[i++] : src[j++];
	}
	while (i <= m)
	{
		dist[k++] = src[i++];
	}
	while (j <= right)
	{
		dist[k++] = src[j++];
	}
}
void MergePass(int* dist, int* src, int n, int s)
{
	int i = 0;
	//有两个归并段,并且数据完整
	for (i; i + 2 * s - 1 <= n - 1; i = i + 2 * s)//i + 2 * s下一个归并段起始
	{
		Merge(dist, src, i, i + s - 1, i + 2 * s - 1);
	}
	//有数据,但不完整
	if (n - 1 > i + s - 1)
	{
		Merge(dist, src, i, i + s - 1, n - 1);
	}
	//不能归并,拷贝
	else
	{
		for (int j = i; j < n; ++j)
		{
			dist[j] = src[j];
		}
	}
}
void MergeSort(int* ar, int n)
{
	if (ar == nullptr || n < 1) return;
	int* br = new int[n];
	int s = 1;
	while (s < n)
	{
		MergePass(br, ar, n, s);//1 4 16
		s += s;
		MergePass(ar, br, n, s);//2 8 32
		s += s;
	}
	delete[] br;
}
int main()
{
	int ar[] = { 56,34,100,23,78,90,12,45,67,88 };
	int n = sizeof(ar) / sizeof(int);
	MergeSort(ar, n);
	for (auto& x : ar)
	{
		cout << x << "	";
	}
	return 0;
}

归并排序(递归型)

#include<iostream>
#include<stdlib.h>
#include<assert.h>
//dist目标数组,src源数组
void Copy(int* src, int* dist, int left, int right)//将归并好的目标数组,拷贝到源数组
{
	for (int i = left; i <= right; ++i)
	{
		src[i] = dist[i];
	}
}
void Merge(int* dist, int* src, int left, int m, int right)
{
	int i = left, j = m + 1;
	int k = left;
	//两个归并段,都有数据
	while (i <= m && j <= right)
	{
		dist[k++] = src[i] < src[j] ? src[i++] : src[j++];
	}
	//左边有数据
	while (i <= m)
	{
		dist[k++] = src[i++];
	}
	//右边有数据
	while (j <= right)
	{
		dist[k++] = src[j++];
	}
}
void MergePass(int* br, int* ar, int left, int right)
{
	if (left < right)
	{
		int mid = (right - left) / 2 + left;
		MergePass(br, ar, left, mid);
		MergePass(br, ar, mid + 1, right);
		Merge(br, ar, left, mid, right);
		Copy(ar, br, left, right);
	}
}
void MergeSort(int* ar, int n)
{
	if (ar == nullptr || n < 1)
		return;
	int* br = new int[n];
	MergePass(br, ar, 0, n - 1);
	delete[] br;
}
int main()
{
	int ar[] = { 56,34,100,23,78,90,12,45,67,88 };
	int n = sizeof(ar) / sizeof(int);
	MergeSort(ar, n);
	for (auto& x : ar)
	{
		cout << x << "	";
	}
	return 0;
}

八、基数排序(桶排序)
低位优先,将所有的数字,从低位到高位,以此进相应的队列,再出,直到全部有序
时间复杂度O(d(n+rd));空间复杂度O(rd)

#include<iostream>
#include<queue>
using namespace std;
//获取最大数字的位数
static int GetFigur(int* arr, int len)
{
	int max = arr[0];
	for (int i = 0; i < len; ++i)//获取最大数字
	{
		if (max < arr[i])
		{
			max = arr[i];
		}
	}
	int count = 0;
	while (max != 0)
	{
		count++;
		max /= 10;
	}
	return count;
}
//获取十进制整数右数第figur位的数figur从0开始
static int GetNum(int n, int figur)
{
	for (int i = 0; i < figur; i++)
	{
		n /= 10;
	}
	return n % 10;
}
void RadixSort(int* arr, int len)
{
	//利用10个队列,存放进队的数字
	std::queue<int> queArr[10];
	//得到最大数字的位数,确定进队和出队的躺数
	int count = GetFigur(arr, len);
	int index;//队列的下标
	for (int i = 0; i < count; i++)//处理每个数字从右到左的第i个数
	{
		for (int j = 0; j < len; j++)//遍历数组
		{
			index = GetNum(arr[j], i);//保存arr[j]应该进入的队列下标
			queArr[index].push(arr[j]);//将数字存放到对应的队列
		}
		//依次将所有数据出队
		int j = 0;
		for (int k = 0; k < 10; k++)//10个队列
		{
			while (!queArr[k].empty())
			{
				arr[j++] = queArr[k].front();
				queArr[k].pop();
			}
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值