C语言排序详解

排序

插入排序

把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为 止,得到一个新的有序序列 。

插入排序对于接近有序的数据进行排序时,效率很高。

img

void InsertSort(int* number, int size)
{
	for (int i = 0; i < size - 1; i++)
	{
		int tmp = number[i + 1];
		int end = i;
		while (end >= 0)
		{
			if (number[end] > tmp)
			{
				number[end + 1] = number[end];
				end--;
			}
			else
				break;
		}
		number[end + 1] = tmp;
	}
}

希尔排序

希尔排序法又称缩小增量法。希尔排序法的基本思想是:先选定一个整数,把待排序文件中所有记录分成个 组,所有距离为的记录分在同一组内,并对每一组内的记录进行排序。然后,取,重复上述分组和排序的工 作。当到达=1时,所有记录在统一组内排好序。

根据插入排序对于接近有序的数据进行排序效率高。

当gap大于1时都为预排序阶段。gap值越大,大的值可以越快的到达后面。

gap越小,数据越接近有序

void ShellSort(int * nums, int size)
{
	int gap = size;
	//当gap为1时即为插入排序
	while (gap > 1)
	{
		gap /= 2;
		for (int i = 0; i < size - gap; i++)
		{
			int end = i;
			int tmp = nums[end + gap];
			while (end >= 0)
			{
				if (nums[end] > tmp)
				{
					nums[end + gap] = nums[end];
				}
				else
					break;
				end -= gap;
			}
			nums[end + gap] = tmp;
		}
	}
}

选择排序

在这里插入图片描述

选择排序每次找出区间内最小的元素放到最左边,然后缩小区间,继续找次小的元素,当区间为0时候结束排序

void SelectSort(int * number, int size)
{
	for (int i = 0; i < size - 1; i++)
	{
		int maxi = 0;
		for (int j = 0; j < size - i; j++)
		{
			if (number[j] > number[maxi])
			{
				maxi = j;
			}
		}
		swap(&number[size - i - 1], &number[maxi]);
	}
}

堆排序

堆分为大堆和小锥

大堆:父节点大于左孩子和孩子

小堆:父节点小于左孩子和右孩子。

当需要排升序时,建大根堆,排降序时建小根堆。

算法描述:

  • 将初始待排序关键字序列(R1,R2….Rn)构建成大顶堆,此堆为初始的无序区;

  • 将堆顶元素R[1]与最后一个元素R[n]交换,此时得到新的无序区(R1,R2,……Rn-1)和新的有序区(Rn),且满足R[1,2…n-1]<=R[n];

  • 由于交换后可能会不满足堆条件,此时对堆顶进行向下调整(区间为R1,R2…Rn-1)为一个新堆,此时再将堆顶元素和无序区最后一个元素交换,得到新的无序区为(R1,R2…Rn-2),新的有序区为(Rn-1,Rn);重复以上步骤,直到无序区只剩一个数据,排序结束。
    在这里插入图片描述

void HeapSort(int* nums, int size)
{
	//大堆 -->升序
	//建堆
	for (int i = (size - 2) / 2; i >= 0; i--)
	{
		AdjustDown(nums, i, size-1);
	}

	int end = size - 1;
	while (end > 0)
	{
		swap(&nums[end], &nums[0]);
		end--;
		AdjustDown(nums, 0, end);
	}
}
void AdjustDown(int* nums, int pos, int size)
{
	int parent = pos;
	int child = pos * 2 + 1;//默认为左子树
	while (child <= size)
	{
		//判断右子树是否大于左子树
		if (child + 1 <= size&&nums[child] < nums[child + 1])
			child++;
		//和parent作比较
		if (nums[child] > nums[parent])
		{
			swap(&nums[child], &nums[parent]);
		}
		else
			break;
		parent = child;
		child = parent * 2 + 1;
	}
}

冒泡排序

冒泡排序的思想:比较相邻的元素,当前一个元素大于后一个元素(或后一个大于前一个)时,进行位置调换,每一对相邻的元素进行相同的操作,从开始比到结尾,此时最后一个即为最大(或者最小的值)。

img

void BubbleSort(int* nums, int size)
{
	for (int i = 0; i < size - 1; i++)
	{
		int flag = 0;
		for (int j = 0; j < size - 1 - i; j++)
		{
			if (nums[j] > nums[j + 1])
			{
				flag = 1;
				swap(&nums[j], &nums[j + 1]);
			}
		}
		if (flag == 0)
			break;
	}
}

快速排序

快速排序分为三种排序方式

hoare版本(左右指针法)

选取一个值作为key(可以是最左面或者是最右面),这里选最左边的值。

  1. 定义两个指针begin和end分别指向最左边和最右面。
  2. 先让end向做移动找比key小的值。
  3. 再让begin向右移动找比key大的值。
  4. 当找到后交换两个值。
  5. 当left>=right时结束循环,将key的值和left处的值进行交换,此时key就找到了应该属于他的位置。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FXIdDXq2-1637395576258)(C:\Users\wang\AppData\Roaming\Typora\typora-user-images\image-20211120142145806.png)]

此时就分为了两个区间为【left,begin-1】begin【begin+1,right】,在对左右区间分别进行递归调用完成排序。

//左右指针排序一趟的过程
int PartSort1(int * nums, int left, int right)
{
	int keyi = left;
	int end = right;
	int key = nums[left];
	while (left < right)
	{
		//右面先走,在右面找小于key的值
		while (left < right)
		{
			if (nums[right] < key)
				break;
			right--;
		}

		//左面找大于key的值
		while (left < right)
		{
			if (nums[left] > key)
				break;
			left++;
		}

		//交换left和right
		if (left < right)
		{
			swap(&nums[left], &nums[right]);
		}
	}
	swap(&nums[keyi], &nums[left]);
	return left;
}

前后指针法

选取左边的值作为key,让cur指向第二个位置,prev指向第一个位置,让cur往前走找小于key的值,找到后和++prev进行交换,当cur>right时,循环结束。

将key的值交换到prev上。

int PartSort3(int* nums, int left, int right)
{
	int key = nums[left];
	int prev = left, cur = left + 1;
	while (cur <= right)
	{
		if (nums[cur] < key)
		{
			prev++;
			swap(&nums[cur], &nums[prev]);
		}
		cur++;
	}
	swap(&nums[prev], &nums[left]);
	return prev;
}

在这里插入图片描述

挖坑法

挖坑法是选出一个值做key,并将key的位置设置为坑,然后从右边向左面找小于key的值,找到后扔进坑里,当前位置就变成了新的坑,再从左边找大于key的值,将找到的值放进坑中,当前位置有又变成了新的坑,知道left==right;当left和right相等时,将key的值扔进坑里面,此时key便找到了属于自己的位置

在这里插入图片描述


//挖坑法
int PartSort2(int * nums, int left, int right)
{
	int hole = left;
	int key = nums[left];
	while (left < right)
	{
		//从右面找比key小的值
		while (left < right&&nums[right] >= key)
		{
			right--;
		}
		//将找到的值放到坑里面
		nums[hole] = nums[right];
		//将left的位置变成新的坑
		hole = right;

		//在左边找大于key 的值,放进坑中
		while (left < right && nums[left] <= key)
		{
			left++;
		}
		nums[hole] = nums[left];
		hole = left;
	}
	//将key放进坑中
	nums[hole] = key;
	return hole;
}
void QuickSort(int * nums, int left, int right)
{

	if (left >= right)
		return;
	//小区间优化
	if (right - left + 1 < 10)
	{
		InsertSort(nums + left, right - left + 1);
	}
	else
	{
		int pos = PartSort2(nums, left, right);
		QuickSort(nums, left, pos - 1);
		QuickSort(nums, pos + 1, right);
	}
}

快速排序的非递归

因为非递归需要用到栈,所以此处用的c++进行实现。

在这里插入图片描述

#include<iostream>
#include<stack>
#include<vector>
#include<ctime>
using namespace std;
void QuickSort(vector<int>& nums)
{
	stack<pair<int,int>> s;
	s.push(make_pair(0, nums.size() - 1));
	int left, right;
	while (!s.empty())
	{
		left = s.top().first;
		right = s.top().second;
		s.pop();

		int prev = left - 1;
		int cur = left;
		int key = nums[left];
		while (cur <= right)
		{
			if (key > nums[cur])
			{
				prev++;
				swap(nums[cur], nums[prev]);
			}
			cur++;
		}
		nums[++prev] = key;
		if (left < prev - 1)
		{
			s.push(make_pair(left, prev - 1));
		}

		if (prev + 1 < right)
		{
			s.push(make_pair(prev + 1, right));
		}
	}
}

int main()
{
	srand((unsigned)time(NULL));
	vector<int> nums{ 10,6,3,9,1,3,7,2 };
	int n = 100000;
	for (int i = 0; i < n; i++)
	{
		nums.push_back(rand());
	}
	QuickSort(nums);
	for (auto e : nums)
		cout << e << " ";
	return 0;
}

归并排序

归并排序(Merge Sort)是建立在归并操作上的一种有效,稳定的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并

在这里插入图片描述
在这里插入图片描述

void _mergeSort(int*nums, int* tmp, int left, int right)
{
	if (left >= right)
		return;
	int mid = left + (right - left) / 2;
	_mergeSort(nums, tmp, left, mid);
	_mergeSort(nums, tmp, mid + 1, right);

	//左右已经有序进行归并
	int i = left;
	int begin1 = left, begin2 = mid + 1;
	while (begin1 <= mid && begin2 <= right)
	{
		if (nums[begin1] > nums[begin2])
			tmp[i++] = nums[begin2++];
		else
			tmp[i++] = nums[begin1++];
	}

	while (begin1 <= mid)
	{
		tmp[i++] = nums[begin1++];
	}

	while (begin2 <= right)
		tmp[i++] = nums[begin2++];

	//将归并好的数据拷贝会原来数组
	for (i = left; i <= right; i++)
	{
		nums[i] = tmp[i];
	}
}

void mergeSort(int* nums, int size)
{
	int* tmp = (int*)malloc(sizeof(int)*size);
	_mergeSort(nums, tmp, 0, size - 1);
}

1] > nums[begin2])
tmp[i++] = nums[begin2++];
else
tmp[i++] = nums[begin1++];
}

while (begin1 <= mid)
{
	tmp[i++] = nums[begin1++];
}

while (begin2 <= right)
	tmp[i++] = nums[begin2++];

//将归并好的数据拷贝会原来数组
for (i = left; i <= right; i++)
{
	nums[i] = tmp[i];
}

}

void mergeSort(int* nums, int size)
{
int* tmp = (int*)malloc(sizeof(int)*size);
_mergeSort(nums, tmp, 0, size - 1);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值