【数据结构】排序

1.直接插入排序

依次将待排序的数插入到前面的有序子序列中

void InsertSort(int* arr, int n)
{
	for (int i = 1; i < n; i++)
	{	
		//一趟排序
		int cur = i;//待排序数的下标
		while (cur >= 0 && arr[cur] < arr[cur-1])
		{
			Swap(&arr[cur], &arr[cur-1]);
			cur--;
		}
	}
}

2.冒泡排序

依次比较相邻的两个数,将比较小的数放在前面,比较大的数放在后面(升序),直至全部排序完成。

void BubbleSort(int* arr, int n)
{
	int exchange = 0;//记录单趟排序中是否发生交换,如果没有说明数组已有序,无需交换
	for (int j = 1; j < n; j++)//n个数,需要n-1趟排序
	{
		//单趟排序
		for (int i = 0; i < n - j; i++)
		{
			if (arr[i] > arr[i + 1])
			{
				Swap(&arr[i], &arr[i + 1]);
				exchange = 1;
			}
			if (exchange == 0)
			{
				break;
			}
		}
	}
}

3.希尔排序

对于直接插入排序来说,如果待排序数组是基本有序的,那每次插入排序所需要的比较次数就会变少,因此希尔排序是在插入排序前进行预排序,使待排序数组基本有序,提升排序的效率。

预排序是将待排序数组,根据gap(每次gap的调整一般为gap = gap/3+1)的值分成若干个小数组,对小数组进行直接插入排序

希尔排序的实现有两种

第一种:

void ShellSort(int* arr, int n)
{
	int gap = n;
	int group = 0;
	while (gap > 1)//gap大于1就进行预排序
	{
		group = 0;
		gap = gap / 3 + 1;
		while (group < gap)
		{
			int cur = 0;
			while (cur < n)
			{
				for (int i = cur + gap; i > 0 && i < n; i -= gap)
				{
					if (arr[i] < arr[i - gap])
					{
						Swap(&arr[i], &arr[i - gap]);
					}
				}
				cur += gap;
			}
			group++;
		}
		
	}

}

 第二种:

void ShellSort(int* arr, int n)
{
	int gap = n;
	int group = 0;
	int cur = 0;
	while (gap > 1)//gap大于1就进行预排序
	{
		group = cur = 0;
		gap = gap / 3 + 1;
		while (group < n)
		{
			cur = group;
			cur = cur+gap;
			if (cur <n)
			{
				if (arr[cur] < arr[cur - gap])
				{
					Swap(&arr[cur], &arr[cur - gap]);
				}
			}
			else
			{
				break;
			}

			group++;
		}

	}
}

4.选择排序

遍历数组,每次选出最大(最小)的数放到最后(前)

为了提高效率,每次遍历的时候可以同时选择最大和最小的数

void SelectSort(int* arr, int size)
{
	int max = arr[0];
	int min = max;
	int maxIndex = 0;
	int minIndex = 0;
	int begin = 0;
	int end = size - 1;
	while (begin < end)
	{
		max = arr[begin];
		maxIndex = begin;
		min = arr[end];
		minIndex = end;
		//单趟排序
		for (int i = begin; i <= end; i++)
		{
			if (arr[i] < min)
			{
				min = arr[i];
				minIndex = i;
			}
			if (arr[i] > max)
			{
				max = arr[i];
				maxIndex = i;
			}
		}
		Swap(&arr[begin], &arr[minIndex]);
		if (maxIndex == begin)//交换最小值的时候,把max覆盖掉了
		{
			Swap(&arr[end], &arr[minIndex]);
		}
		else
		{
			Swap(&arr[end], &arr[maxIndex]);
		}
		begin++;
		end--;
	}

}

5.堆排序

利用大(小)根堆,依次选出最大(小)的数

void AdjustUp(int* p, int child)
{
	assert(p);

	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (p[parent] > p[child])
		{
			Swap(&p[parent], &p[child]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}
void AdjustDown(int* p, int size, int parent)
{
	assert(p);

	int child = parent * 2 + 1;
	while (child < size)
	{
		if (child + 1 < size && p[child + 1] < p[child])//右孩子小于左孩子
		{
			child++;
		}
		if (p[child] < p[parent])
		{
			Swap(&p[child], &p[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}
void HeapSort(int* p, int size)
{
	assert(p);
	向上调整建堆
	//for (int i = 1; i < size; i++)
	//{
	//	AdjustUp(p, i);
	//}
	//向下调整建堆
	for (int i = (size - 1 - 1) / 2; i >= 0; i--)//从最后一个父节点开始调
	{
		AdjustDown(p, size, i);
	}
	//降序
	while (size > 0)
	{
		Swap(&p[0], &p[size - 1]);
		size--;
		AdjustDown(p, size, 0);
	}
}

6.快速排序

每趟排序,选择一个key,将小于key的放右边,大于key的放左边,然后再对key的左右两边进行相同的操作,直到完成排序。

hoare:

hoare法需要注意的是,当key取最左边的数时,需要R先走,使每次停下来的位置都小于key。而key取最右边时,需要L先走,时每次停下来的位置都大于key

//hoare
int PartSort1(int* arr, int left, int right)
{
	int keyi = left;
	while (left < right)
	{
		while (arr[right] > arr[keyi])//让right停在小于key的位置上
		{
			right--;
		}
		while (arr[left] < arr[keyi])//让left停在大于key的位置上
		{
			left++;
		}
		Swap(&arr[left], &arr[right]);
	}
	Swap(&arr[keyi], &arr[right]);
	return right;
}
void QuickSort1(int* arr, int begin,int end)
{
	if (begin >= end)
	{
		return;
	}
	int keyi = PartSort1(arr, begin, end);
	QuickSort1(arr, begin, keyi-1);
	QuickSort1(arr, keyi+1, end);
}

挖坑法:

相比hoare法,挖坑法就不用规定谁先走谁后走

int PartSort2(int* arr, int left, int right)
{
	int keyi = left;
	int temp = arr[keyi];
	int pit = left;

	while (left < right)
	{
		while (left < right && arr[right] >= temp)
		{
			right--;
		}
		arr[pit] = arr[right];
		pit = right;
		while (left < right && arr[left] <= temp)
		{
			left++;
		}
		arr[pit] = arr[left];
		pit = left;
	}
	arr[pit] = temp;
	return pit;
}

int PartSort3(int* arr, int left, int right)
{
	int keyi = left;
	int prev = keyi;
	int cur = keyi + 1;
	while (cur <= right)
	{
		if (arr[cur] < arr[keyi] && arr[prev++] != arr[cur])
		{
			Swap(&arr[cur], &arr[prev]);
		}
		cur++;
	}
	Swap(&arr[prev], &arr[keyi]);
	return prev;
}
void QuickSort3(int* arr, int begin, int end)
{
	if (begin >= end)
	{
		return;
	}
	int keyi = PartSort3(arr, begin, end);
	QuickSort1(arr, begin, keyi - 1);
	QuickSort1(arr, keyi + 1, end);
}

非递归:

快排的非递归实现需要借助栈,将区间压入栈中来模拟递归

void QuickSort4(int* arr, int begin, int end)
{
	int left = 0;
	int right = 0;
	Stack st;
	StackInit(&st);
	StackPush(&st, begin);
	StackPush(&st, end);
	while (!StackEmpty(&st))
	{
		right = StackTop(&st);
		StackPop(&st);
		left = StackTop(&st);
		StackPop(&st);

		int keyi = PartSort2(arr, left, right);
		if (keyi-1 > left)
		{
			StackPush(&st, left);
			StackPush(&st, keyi - 1);
		}
		if (right > keyi + 1)
		{
			StackPush(&st, keyi + 1);
			StackPush(&st, right);
		}

	}
	StackDestroy(&st);
}

快排的效率取决于key的取值,如果key恰好能把待排序数组二分,则递归的层数最少,所以为了使快排的效率高,可以对key的选取进行优化,在选取key的时候可以选随机值,或者使用三数取中法(选取头尾中三个数中,值的大小在中间的那个数为key)。也可以选择在小区间是使用直接插入排序,减少递归层数。

7.归并排序

将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。

 

void _MergeSort(int* arr,int left,int right,int* tempArray)
{
	if (left >= right)
	{
		return;
	}
	int mid = (left + right) / 2;
	_MergeSort(arr, left, mid,tempArray);
	_MergeSort(arr, mid + 1, right,tempArray);

	int begin1 = left;
	int end1 = mid;
	int begin2 = mid + 1;
	int end2 = right;
	int k = left;
	while (begin1 <= end1 && begin2 <= end2 )
	{
		if (arr[begin1] < arr[begin2])
		{
			tempArray[k++] = arr[begin1++];
		}
		else
		{
			tempArray[k++] = arr[begin2++];
		}
	}
	while (begin1 <= end1)
	{
		tempArray[k++] = arr[begin1++];
	}
	while (begin2 <= end2)
	{
		tempArray[k++] = arr[begin2++];
	}

	memmove(arr, tempArray, sizeof(int)*(right - left + 1));
}
void MergeSort(int* arr, int n)
{
	int* tempArray = (int*)malloc(sizeof(int) * n);
	assert(tempArray);
	_MergeSort(arr, 0, n - 1, tempArray);

	free(tempArray);
	tempArray = NULL;
}

 非递归:

 

void MergeSortNotR(int* arr, int n)
{
	int* tempArray = (int*)malloc(sizeof(int) * n);
	int gap = 1;
	int begin1 = 0;
	int begin2 = 0;
	int end1 = 0;
	int end2 = 0;
	while (gap < n)
	{
		for(int i = 0;i < n;i+=2*gap)
		{
			begin1 = i;
			end1 = begin1 + gap - 1;
			begin2 = begin1 + gap;
			end2 = begin2 + gap - 1;

			if (end1 > n -1)
			{
				end1 = n - 1;
			}
			if (begin2 > n-1)
			{
				begin2 = end2 + 1;
			}
			else if (end2 > n - 1)
			{
				end2 = n - 1;
			}

			int k = begin1;

			while (begin1 <= end1 && begin2 <= end2)
			{
				if (arr[begin1] < arr[begin2])
				{
					tempArray[k++] = arr[begin1++];
				}
				else
				{
					tempArray[k++] = arr[begin2++];
				}
			}
			while (begin1 <= end1)
			{
				tempArray[k++] = arr[begin1++];
			}
			while (begin2 <= end2)
			{
				tempArray[k++] = arr[begin2++];
			}
			
		}
		gap *= 2;
		memmove(arr, tempArray, sizeof(int) * n);
	}
	free(tempArray);
}

8.计数排序

计数是一个非基于比较的排序算法,它的思想是在给定的一组序列中,先找出该序列中的最大值和最小值,从而确定需要开辟多大的辅助空间,每一个数在对应的辅助空间中都有唯一的下标。

void CountSort(int* arr, int n)
{
	//找到数组中的最大最小值
	int max = arr[0];
	int min = arr[0];
	for (int i = 0; i < n; i++)
	{
		if (arr[i] > max)
		{
			max = arr[i];
		}
		if (arr[i] < min)
		{
			min = arr[i];
		}
	}
	//给定区间
	int range = max - min + 1;
	int* tempArray = (int*)calloc(range,sizeof(int));//分配空间,并初始化为0
	//将原数组的值映射到临时数组
	for (int i = 0; i < n; i++)
	{
		tempArray[arr[i] - min ]++;
	}

	int index = 0;
	for (int i = 0; i < range; i++)
	{
		while (tempArray[i] != 0)
		{
			arr[index++] = i + min;
			tempArray[i]--;
		}
	}

}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值