常见的几种排序

void Swap(int* p, int* q)
{
    
    
	int tmp = *q;
	*q = *p;
	*p = tmp;
}
//三数取中
int GetMidIndex(int* a, int begin, int end)
{
    
    
	int mid = (begin + end) / 2;
	if (a[begin] < a[mid])
	{
    
    
		if (a[mid] < a[end])
			return mid;
		else if (a[begin] > a[end])
			return begin;
		else
			return end;
	}
	else // a[begin] > a[mid]
	{
    
    
		if (a[mid] > a[end])
			return mid;
		else if (a[begin] < a[end])
			return begin;
		else
			return end;
	}
}

交换函数在多数排序中都会用到,特地写一个交换函数比较方便。

插入排序

思路:插入排序基本操作就是将一个数据插入到已经排序好的有序序列中,从而得到一个新增一个数据的序列;
在这里插入图片描述

// 插入排序
void InsertSort(int* a, int n)
{
    
    
	assert(a);
	for (int i = 0; i < n-1; i++)
	{
    
    
		int end = i;
		int tmp = a[end + 1];
		while (end >= 0)
		{
    
    
			if (tmp < a[end])
			{
    
    
				//a[end+1] = a[end];
				Swap(&a[end], &a[end + 1]);
				--end;
			}
			else
			{
    
    
				break;
			}
		}
		//a[end + 1] = tmp;
	}
	return a;
}

希尔排序

在希尔排序里我们首先会设置一个增量值 ,然后把数据按照增量分为几组(分组只是逻辑上的分组并非真的分组) 然后对分组内的数据进行排序。排序完成后 增量变小,然后再根据增量进行排序。依次类推 当增量为1时,再进行排序的时候,此时的数据经过前几次的处理,变得相对很规律。
在这里插入图片描述

// 希尔排序
void ShellSort(int* a, int n)
{
    
    
	assert(a);
	int gap = n;
	while (gap > 1)
	{
    
    
	    //确保最后一次时gap为1;
	    //gap逐渐缩小;
		gap = gap / 3 + 1;
		for (int i = 0; i < n - gap; i++)
		{
    
    
			int end = i;
			int tmp = a[end + gap];
			while (end >= 0)
			{
    
    
				if (tmp < a[end])
				{
    
    
					//a[end+1] = a[end];
					Swap(&a[end], &a[end + gap]);
					end -= gap;
				}
				else
				{
    
    
					break;
				}
			}
			//a[end + 1] = tmp;
		}
	}
	return a;
}

选择排序

选择排序是在一组数据中找到最小的数之后,将其与第一个数交换,第一个数就变的有序,然后再从第二个数开始遍历,找到最小的数,与第二个数交换,以此类推;
在这里对其进行优化:同时找到最大的数和最小的数,放到最后一位和第一位,以此类推;
在这里插入图片描述

//选择排序
void SelectSort(int* a, int n)
{
    
    
	assert(a);
	int end = n - 1, begin = 0;
	while (begin < end)
	{
    
    
		int min,max;//最大数和最小数的下标
		min = max = begin;
		//循环找到最大的数和最小的数
		for (int i = begin+1; i <= end; i++)
		{
    
    
			if (a[i] > a[max])
			{
    
    
				max = i;
			}
			if (a[i] < a[min])
			{
    
    
				min = i;
			}
		}
        
		Swap(&a[begin], &a[min]);
		//防止第一个元素为max,若第一个元素最大,此时min下标为第一个,先让max=min;
		if (begin == max)
		{
    
    
			max = min;
		}
		Swap(&a[end], &a[max]);
        //缩小范围
		begin++; end--;
	}
}

冒泡排序

将相邻元素两两比较,反序则交换,每一趟都将最大的数交换到最后面;
在这里插入图片描述

//冒泡排序
void BubbleSort(int* a, int n)
{
    
    
	assert(a);
	for (int i = 0; i < n-1; i++)
	{
    
    
		for (int j = 0; j < n-i-1; j++)
		{
    
    
			if (a[j] > a[j + 1])
			{
    
    
				Swap(&a[j], &a[j + 1]);
			}
		}
	}
	return a;
}

zhi## 快速排序(三种方法)
一共三种方法,都是设定一个基准数,把比基准数大的数放到后面,把比基准数小的数放到前面,再进行递归,使其有序
一、左右指针法
一前一后两个指针,right从最后面出发,left从最前面出发,left找到比key(key即为基准值)大的之后,right再往前走找到比key小的,两数交换,以此类推,最后key恰好落到最终排序后的位置上,然后在key前后两个区间递归;
在这里插入图片描述

// 快速排序左右指针法
int PartSort1(int* a, int left, int right)
{
    
    
	//int key = a[right];
	//三数取中,防止逆序时为时间复杂度最大;
	int midIndex = GetMidIndex(a, left, right);
	Swap(&a[midIndex], &a[right]);
	int keyi = right;
	while (right > left)
	{
    
    
		while (right > left && a[left] <= a[keyi])  left++;
		while (right > left && a[right] >= a[keyi])  right--;
		Swap(&a[right], &a[left]);
	}

	Swap(&a[keyi], &a[right]);
	return left;
}
void QuickSort1(int* a, int left, int right)
{
    
    
	assert(a);
	if (left >= right) return;
	
	int meet = PartSort1(a, left, right);
	QuickSort1(a, left, meet - 1);
	QuickSort1(a, meet + 1, right);
	return a;
}

二、挖坑法
先将key保存,将key的位置作为坑的位置,left先找到一个比key大的数,将其填入key的位置,left的位置变成坑,right再找到一个比可以key小的数,然后填入坑,以此类推,最后key恰好落到最终排序后的位置上,然后在key前后两个区间递归;
在这里插入图片描述

// 快速排序挖坑法
int PartSort2(int* a, int left, int right)
{
    
    
	int midIndex = GetMidIndex(a, left, right);
	Swap(&a[midIndex], &a[right]);

	int key = a[right];
	
	while (right > left)
	{
    
    
		while (right > left && a[left] <= key)  left++;
		a[right] = a[left];
		while (right > left && a[right] >= key)  right--;
		a[left] = a[right];
	}
	a[left] = key;
	return left;
}
void QuickSort2(int* a, int left, int right)
{
    
    
	assert(a);
	if (left >= right) return;

	int meet = PartSort2(a, left, right);
	QuickSort2(a, left, meet - 1);
	QuickSort2(a, meet + 1, right);
	return a;
}

三、前后指针法
一前一后两个指针,初始cur=0,prev=cur-1,cur像是火车头,拉着比key值大的数往最后面走;
在这里插入图片描述

// 快速排序前后指针法
int PartSort3(int* a, int left, int right)
{
    
    
	int midIndex = GetMidIndex(a, left, right);
	Swap(&a[midIndex], &a[right]);

	int prev = left - 1;
	int cur = left;
	int keyindex = right;

	while (cur < right)
	{
    
    
		if (a[cur] < a[keyindex] && ++prev != cur)
			Swap(&a[prev], &a[cur]);

		++cur;
	}

	Swap(&a[++prev], &a[keyindex]);

	return prev;
}
void QuickSort3(int* a, int left, int right)
{
    
    
	assert(a);
	if (left >= right) return;

	int meet = PartSort1(a, left, right);
	QuickSort3(a, left, meet - 1);
	QuickSort3(a, meet + 1, right);
	return a;
}

归并排序

递归实现:
归并排序是利用归并的思想实现的排序算法,该算法采用经典的分治策略(分治法将问题分成一些小的问题然后递归求解,而治的阶段则将分的阶段得到的各答案‘修补’在一起,即分而治之)。
在这里插入图片描述

// 归并排序递归实现
void _MergeSort(int* a, int L,int R,int* tmp)
{
    
    
	if (L >= R)
		return;

	int M = (L + R) / 2;
	_MergeSort(a, L, M, tmp);
	_MergeSort(a, M + 1, R, tmp);

	int begin1 = L, end1 = M;
	int begin2 = M + 1, end2 = R;
	int i = L;
	while (begin1 <= end1 && begin2 <= end2)
	{
    
    
		if (a[begin1] > a[begin2])
			tmp[i++] = a[begin2++];
		else
			tmp[i++] = a[begin1++];
	}

	while (begin1 <= end1)
		tmp[i++] = a[begin1++];
	
	while (begin2 <= end2)
		tmp[i++] = a[begin2++];
	
	for (int i = L; i <= R; i++)
	{
    
    
		a[i] = tmp[i];
	}
}
void MergeSort(int* a, int n)
{
    
    
	assert(a);
	int* tmp = malloc(sizeof(int) * n);
	_MergeSort(a, 0, n - 1, tmp);
	return a;
	free(tmp);
}

// 归并排序非递归实现
void MergeArr(int* a, int begin1, int end1, int begin2, int end2, int* tmp)
{
    
    
	int left = begin1, right = end2;
	int index = begin1;
	while (begin1 <= end1 && begin2 <= end2)
	{
    
    
		if (a[begin1] < a[begin2])
			tmp[index++] = a[begin1++];
		else
			tmp[index++] = a[begin2++];
	}

	while (begin1 <= end1)
		tmp[index++] = a[begin1++];

	while (begin2 <= end2)
		tmp[index++] = a[begin2++];

	// 把归并好的再tmp的数据在拷贝回到原数组
	for (int i = left; i <= right; ++i)
		a[i] = tmp[i];
}
void MergeSortNonR(int* a, int n)
{
    
    
	assert(a);
	int* tmp = malloc(sizeof(int) * n);
	int gap = 1;
	while (gap < n)
	{
    
    
		for (int i = 0; i < n; i += 2 * gap)
		{
    
    
			// [i,i+gap-1] [i+gap, i+2*gap-1]
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + 2 * gap - 1;
			// 1、合并时只有第一组,第二组不存在,就不需要合并
			if (begin2 >= n)
				break;

			// 2、合并时第二组只有部分数据,需要修正end2边界
			if (end2 >= n)
				end2 = n - 1;

			MergeArr(a, begin1, end1, begin2, end2, tmp);
		}
		//PrintArray(a, n);
		gap *= 2;
	}
	free(tmp);
}

猜你喜欢

转载自blog.csdn.net/qq_43745617/article/details/113251312