排序算法总结

下面是九大排序算法的总体分类:
接下来,对每个排序算法进行分析:

一、直接插入排序(straight insertion sort):
1.算法思想:将数据插入到已经排好的序列中。有序序列初始只有一个元素,将下一个的元素插入,有序队列增长, 直到最后整个要排序的队列都有序。
2.时间复杂度:
最好时间复杂度:O(N) :原有数据已经有序
最坏时间复杂度O(N^2):原有数据完全或接近逆序
3.应用场景:适用于元素已经基本有序,不适用于元素逆序或接近逆序的情况。
4.代码实现:
代码包含两个for循环, 第一个标记当前要插入的元素位置, 第二个为单趟插入,将要插入的元素插入到有序序列中。
//插入排序
void InsertSort(int* arr, size_t size)
{
	assert(arr);
	for(size_t j = 1; j < size; ++j)//要插入数据的位置
	{
		int insert = arr[j]; //要插入的数据
		int i = j-1;
		for( ; i >= 0 ;--i) //单趟插入
		{
			if(arr[i] > insert)
				arr[i+1] = arr[i];
			else
				break;
		}
		arr[i+1] = insert;
	}
}

二、希尔排序(Shell Sort),又名缩小增量排序:
当要排序的数据列完全或接近逆序时,用插入排序时间复杂度接近O(N^2),为了优化这种情况,减少插入时的数据移动,提出了希尔排序。
1.算法思想:将数据分组,对每组的数据进行插入排序,然后缩小分组的大小再进行插入排序,直到分组大小为1,则要排列的数据有序。(预排序+插入排序)
2.时间复杂度:
3.应用场景:适用于元素接近逆序, 不适用于元素基本有序的情况。
4.代码实现:
gap为每组的大小,当gap=1时,相当于插入排序,gap的最小值为1.
//希尔排序
void ShellSort(int* arr, size_t size)
{
	assert(arr);
	int gap = size;
	while(gap != 1)
	{
		gap = gap/3 + 1;
		for(size_t j = gap; j < size; ++j)
		{
			int insert = arr[j];
			int i = j-gap;
			for( ; i>=0; i-=gap)
			{
				if(arr[i] > insert)
					arr[i+gap] = arr[i];
				else
					break;
			}
			arr[i+gap] = insert;
		}
	}
}

三、选择排序:
1.算法思想:
2.时间复杂度:
3.适用场景
4.代码实现:
void SelectSort(int* arr, size_t size)
{
	assert(arr);
	int left = 0;
	int right = size-1;
	while(left < right)
	{
		size_t max = left; //记录最大值的下标
		size_t min = left;
		for(int i = left; i <= right; ++i)
		{
			if(arr[i] > arr[max])
				max = i;
			if(arr[i] < arr[min])
				min = i;
		}
		swap(arr[left], arr[min]);
		//注意:防止上面的交换使max值不正确。
		//当max的值为左时,交换会让max值改变,这里要调整。
		if(left == max) 
			max = min;
		swap(arr[right], arr[max]);
		left++;
		right--;
	}
}

四、堆排序:
1.算法思想:
2.时间复杂度:
3.适用场景
4.代码实现:
void AdjustDown(int* arr, size_t size, size_t parent)
{
	size_t child = parent*2+1;
	while(child < size)
	{
		if(child+1<size && arr[child+1] > arr[child])
			++child;
		if(arr[parent] < arr[child])
			swap(arr[parent], arr[child]);
		else
			break;
		parent = child;
		child = parent*2+1;
	}
}

//堆排序
void HeapSort(int* arr, size_t size)
{
	assert(arr);
	//建堆
	int parent = (size-1-1)/2;
	while(parent >= 0)
	{
		AdjustDown(arr, size, parent);
		parent--;
	}
	//排序
	int index = size-1;//记录交换的位置
	while(index>0)
	{
		swap(arr[0], arr[index]);
		AdjustDown(arr, index, 0);
		--index;
	}

五、冒泡排序:
1.算法思想:
2.时间复杂度:
3.适用场景
4.代码实现:
void BubbleSort(int* arr, size_t size)
{
	assert(arr);
	for(size_t i = 0; i<size-1; ++i)
	{
		for(size_t j = 1; j < size-i; ++j)
		{
			if(arr[j-1] > arr[j])
			{
				swap(arr[j-1],arr[j]);
			}
		}
	}
}

六、快速排序:
1.算法思想:
2.时间复杂度:
3.适用场景
4.代码实现:
//快速排序
//优化一:三数取中法
size_t GetMid(int* arr, size_t left, size_t right)
{
	size_t mid = left+((right-left)>>2);
	if(arr[left] < arr[mid])
	{
		if(arr[mid] < arr[right])
			return mid;
		else if(arr[left] < arr[right])
			return right;
		else
			return left;
	}
	else  //left大
	{
		if(arr[left] < arr[right])
			return left;
		else if(arr[right] < arr[mid])
			return mid;
		else
			return right;
	}
}
//法1:左右指针
size_t PartSort1(int* arr, size_t left, size_t right)
{
	size_t key = right;
	size_t mid = GetMid(arr, left, right);
	if(key != mid)
		swap(arr[key], arr[right]);
	while(left < right)
	{
		//left找大
		while(left < right && arr[left] <= arr[key])
			++left;
		while(left < right && arr[right] >= arr[key])
			--right;
		swap(arr[left], arr[right]);
	}
	if(right != key)
		swap(arr[left], arr[key]);
	return left;
}

//法2:挖坑法
size_t PartSort2(int* arr, size_t left, size_t right)
{
	int key = arr[right];
	while(left < right)
	{
		while(left < right && arr[left] <= key)
			++left;
		arr[right] = arr[left];

		while(left < right && arr[right] >= key)
			--right;
		arr[left] = arr[right];
	}
	//right == left
	arr[right] = key;
	return right;
}

//法3:前后指针法
size_t PartSort3(int* arr, int left, int right)
{
	int cur = left-1;
	int prev = left-1;
	int key = right;
	while(cur < right)
	{
		while(cur < right && arr[++cur] >= arr[key]);//cur找小
		++prev;
		swap(arr[prev], arr[cur]);
	}
	return prev;
}

void QuickSort(int* arr, size_t left, size_t right)
{
	assert(arr);
	if(right-left > 0)
	{
		//优化二:区间数量较少时,用插入排序替代递归
		if(right-left > 5)  
		{
			int div = PartSort3(arr, left, right);
			//[left, div-1] [div+1, right]
			if(div != left) //注意:当div为边界时,边界的一边不需要继续排序
				QuickSort(arr, left, div-1);
			if(div != right)
				QuickSort(arr, div+1, right);
		}
		else
		{
			InsertSort(&arr[left], right-left+1);
		}
	}
}

七、归并排序:
1.算法思想:
2.时间复杂度:
3.适用场景
4.代码实现:
//[left, right]
void MSort(int* arr,int* tmp, int left, int right)
{
	if(right - left > 0)
	{
		int mid = left+(right-left)/2;
		MSort(arr, tmp, left, mid);
		MSort(arr, tmp, mid+1, right);
		
		//合并
		int begin1 = left, end1 = mid;
		int begin2 = mid+1, end2 = right;
		int i = left;
		while(begin1<=end1 && begin2<= end2)
		{
			if(arr[begin1] < arr[begin2])
			{
				tmp[i++] = arr[begin1];
				++begin1;
			}
			else
			{
				tmp[i++] = arr[begin2];
				++begin2;
			}
		}
		while(begin1 <= end1)
		{
			tmp[i++] = arr[begin1];
			++begin1;
		}
		while(begin2 <= end2)
		{
			tmp[i++] = arr[begin2];
			++begin2;
		}
		for(int j = left; j <= right; ++j)
		{
			arr[j] = tmp[j];
		}
	}

}
void MergeSort(int* arr, size_t size)
{
	assert(arr);
	int* tmp = new int[size];
	MSort(arr, tmp, 0, size-1);
	delete[] tmp;
}

八、计数排序:
1.算法思想:
2.时间复杂度:
3.适用场景
4.代码实现:
//1.计数排序:利用哈希的直接定址法,适用于数据比较集中的排序
void CountSort(int* arr, size_t size)
{
	assert(arr);
	int max = arr[0];
	int min = arr[0];
	for(int i = 1 ; i < size; ++i)
	{
		if(arr[i] > max)
			max = arr[i];
		if(arr[i] < min)
			min = arr[i];
	}
	int range = max - min + 1;
	int* tmp = new int[range];//存储每个数据出现的次数
	memset(tmp, 0, sizeof(tmp[0])*range);
	for(int i = 0; i < size; ++i)
	{
		tmp[arr[i] - min]++;
	}

	int j = 0;
	for(int i = 0; i < range; ++i)
	{
		while(tmp[i]--)
		{
			arr[j++] = min + i;
		}
	}
	delete[] tmp;
}

九、基数排序:
1.算法思想:
2.时间复杂度:
3.适用场景
4.代码实现:
//2.基数排序
// 最高位优先 -- MSD
// 最低位优先 -- LSD
//
void MSDSort(int* arr, size_t size)
{
	assert(arr);
	int max = arr[0];
	for(int i = 1; i < size; ++i)
		if(arr[i] > max)
			max = arr[i];
	
	//统计最大位数
	int digit = 1; //最大位数
	int div = 10;
	while((max / div) > 0)
	{
		++digit;
		div *= 10;
	}
	
	//从低位到高位排序
	int* beginIndex = new int[10];//记录每个桶的开始下标
	int* tmp = new int[size];
	
	div = 1;
	while(digit--)
	{
		//统计每个桶的开始下标
		memset(beginIndex, 0, sizeof(beginIndex[0])*10);
		
		for(int i = 0; i < size; ++i)//统计每个桶的数据个数
		{
			size_t index = (arr[i]/div) % 10;
			beginIndex[index]++;
		}
		int index = 0;
		for(int i = 0; i < 10; ++i)//计算每个桶的开始下标
		{
			int count = beginIndex[i];
			beginIndex[i]  = index;
			index += count;
		}

		//将单次排序数据写入临时数组
		for(int i = 0; i < size; ++i)
		{
			size_t index = (arr[i]/div) % 10;
			tmp[beginIndex[index]++] = arr[i];
		}
	
		div *= 10;
		//将tmp的数据拷贝到arr中
		for(int i =0 ;i < size; ++i)
		{
			arr[i] = tmp[i];
		}
	}

	delete[] beginIndex;
	delete[] tmp;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值