排序

直接插入排序

  • 思路
    在待排列的元素中,在前n-1项有序序列中,插入第n项元素,使n项元素有序,这样依次插入直至序列有序。
    由于不能确定有序部分,所以从第一个元素开始,视第一个元素为有序,将之后的元素依次插入。

在这里插入图片描述

  • 代码
//直接插入排序(升序)
void InsertSort(int* arr, int n)
{
	for (int i = 1; i < n; i++)
	{
		int end = i - 1;
		int tmp = arr[i];
		while (end >= 0)
		{
			if (tmp < arr[end])
			{
				arr[end + 1] = arr[end];
				--end;
			}
			else
			{
				break;
			}
		}
		arr[end + 1] = tmp;
	}
}

时间复杂度:O(N^2)
空间复杂度:O(1)
稳定性:稳定

希尔排序

希尔排序是对直接插入排序的优化,先进行预排序,将序列接近有序,再进行直接插入排序,使序列有序。

  • 思路
    选定小于n的整数gap,将待排序列分为gap组,所有间隔为gap的元素为一组,每组进行直接插入排序,然后选定小于上一次gap的整数作为gap循环操作;当gap=1时,相当于整个待排序列为一组,进行依次直接插入排序后有序。
    在这里插入图片描述
    代码
//希尔排序(升序)
void ShellSort(int* arr, int n)
{
	//设置组
	int gap = n;
	while (gap > 1)
	{
		gap = gap / 3 + 1;
		//共gap组,循环gap组  //每组循环n / gap - 1次,间隔为gap的数据为一组,每次循环插入一个数据
		for (int i = 0; i < n - gap; i++)
		{
			int end = i;
			//保存新插入的数据
			int tmp = arr[i + gap];
			//和组内数据比较
			while (end >= 0)
			{
				//如果新插入的数据小于上一个数据
				if (tmp < arr[end])
				{
					//将上一个数据向后挪动
					arr[end + gap] = arr[end];
					//再次对比上上个数据
					end -= gap;
				}
				else
					break;
			}
			//写入新插入的数据
			arr[end + gap] = tmp;

		}
	}
}

时间复杂度:O(N^1.3),特殊序列 O(N^2)
空间复杂度:O(1)
稳定性:不稳定

选择排序

  • 思路
    每一次从待排序的数据元素中选出最小和最大的一个元素,存放在序列的起始位置和结束位置,直到全部待排序的
    数据元素排完
    在这里插入图片描述

代码

//选择排序
void SelectSort(int* arr, int n)
{
	
	for (int i = 0; i < n / 2; i++)
	{
		int mini = i;
		int max = i;
		for (int j = i+1; j < n-i; j++)
		{
			if (arr[j] > arr[max])
				max = j;
			if (arr[j] < arr[mini])
				mini = j;
		}
		Swap(&arr[i], &arr[mini]);
		// 如果i和maxi重叠,交换后修正一下
		if (max == i)
			max = mini;
		Swap(&arr[n-i-1], &arr[max]);
	}
}

堆排序

  • 思路(升序)
    将待排序列向下调整建成大堆,根据大堆的特性 根节点的元素是最大的,将根节点的元素和最后一位元素交换位置,再利用向下调整将剩下元素中次大的调整到根,重复操作直至序列有序(序列从后向前依次有序)

在这里插入图片描述

// 堆排序
void AdjustDwon(int* arr, int n, int parent)
{
	int child = parent * 2 + 1;
	while (child < n)
	{
		if (child + 1 < n && arr[child + 1] > arr[child])
			child++;
		if (arr[child] > arr[parent])
		{
			Swap(&arr[child], &arr[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
			break;
	}
}


void HeapSort(int* arr, int n)
{
	//建大堆
	for (int parent = (n - 1 - 1) / 2; parent >= 0; parent--)
	{
		AdjustDwon(arr, n, parent);
	}
	//排序
	for (int end = n-1; end > 0; end--)
	{
		Swap(&arr[0], &arr[end]);
		AdjustDwon(arr, end, 0);
	}
}

时间复杂度:O(N*logN)
空间复杂度:O(1)
稳定性:不稳定

快速排序

时间复杂度:O(N*logN)
空间复杂度:O(logN)
稳定性:不稳定

hoare版本

  • 思路
    将序列开始位置选做key。
    从序列最右侧(定义为right)向左找比key小的值,找到后停止。
    右侧找到后从序列最左侧(定义为left)向左找比key大的值,找到后left和right交换元素,继续从right开始找。
    当right和left相遇时,将相遇点元素和key的元素交换。
    此时key的左侧都是小于key的数,key的右侧都是大于key的数。
    将key的左右侧如此单趟排序,直到左右序列只有一个元素,或是左右序列不存在,递归结束。
    在这里插入图片描述

代码

// 快速排序hoare版本
int PartSort1(int* a, int left, int right)
{
	//设置key值的下标
	int keyi = left;
	while (left < right)
	{
		//右侧找比key小的值
		while (left < right && a[right] >= a[keyi])
		{
			right--;
		}
		//左侧找比key大的值
		while (left < right && a[left] <= a[keyi])
		{
			left++;
		}
		//交换两个值的位置
		Swap(&a[left], &a[right]);
	}
	//与key交换位置
	Swap(&a[keyi], &a[left]);
	return left;
}

void _QuickSort(int* a, int left, int right)
{
	if (left >= right)
		return;
	if (right - left + 1 <= 10)
	{
		InsertSort(a + left, right - left + 1);
		return;
	}
	int mid = PartSort1(a, left, right);
	//int mid = PartSort2(a, left, right);
	//int mid = PartSort3(a, left, right);
	_QuickSort(a, left, mid - 1);
	_QuickSort(a, mid + 1, right);
}

挖坑法

  • 思路
    类似hoare版本
    将序列开始位置的值选做key值。
    右侧先走,找到比key小的停止,将元素放入left(最开始就是key的位置)中。
    left找到比right大的停止,将元素放入right中,继续从right开始找。
    当right和left相遇时,将key直接放入相遇点。
    在这里插入图片描述
    代码
// 快速排序挖坑法
int PartSort2(int* a, int left, int right)
{
	//设置key值
	int key = a[left];
	while (left < right)
	{
		//右侧找比key小的值
		while (left < right && a[right] >= key)
		{
			right--;
		}
		//将小的保存在左侧
		a[left] = a[right];
		//左侧找比key大的值
		while (left < right && a[left] <= key)
		{
			left++;
		}
		//将大的保存在右侧
		a[right] = a[left];
	}
	//保存key的值
	a[left] = key;
	return left;
}

void _QuickSort(int* a, int left, int right)
{
	if (left >= right)
		return;
	if (right - left + 1 <= 10)
	{
		InsertSort(a + left, right - left + 1);
		return;
	}
	//int mid = PartSort1(a, left, right);
	int mid = PartSort2(a, left, right);
	//int mid = PartSort3(a, left, right);
	_QuickSort(a, left, mid - 1);
	_QuickSort(a, mid + 1, right);
}

前后指针法

  • 思路
    将序列开始位置选做key。
    prev指针指向序列起始位置,cur指针指向prev+1。
    当cur指向的元素小于key,则prev先向后移动一位,然后交换prev和cur指针的元素,然后cur指针++;若cur指向的元素大于key,则cur指针直接++。
    当cur越界时,交换key和prev指针的元素。
    在这里插入图片描述
// 快速排序前后指针法
int PartSort3(int* a, int left, int right)
{
	//取最小值的下标
	int min = GetMid(a, left, right);
	//如果和left不同,交换值
	if (min != left)
		Swap(&a[min], &a[left]);
	//设置key值的下标
	int keyi = left; 
	int cur = left+1;
	int prev = left;
	while (cur <= right)
	{
		if (a[cur] < a[keyi])
		{	
			if (cur != ++prev)
				Swap(&a[cur], &a[prev]);
		}
		cur++;
	}
	Swap(&a[keyi], &a[prev]);
	return prev;
}

void _QuickSort(int* a, int left, int right)
{
	if (left >= right)
		return;
	if (right - left + 1 <= 10)
	{
		InsertSort(a + left, right - left + 1);
		return;
	}
	//int mid = PartSort1(a, left, right);
	//int mid = PartSort2(a, left, right);
	int mid = PartSort3(a, left, right);
	_QuickSort(a, left, mid - 1);
	_QuickSort(a, mid + 1, right);
}

非递归

非递归类似二叉树的前序遍历,配合栈来模拟实现,将单趟排序的左右区间入栈,根据栈 先进后出的特性实现前序遍历。

// 快速排序 非递归实现
void _QuickSortNonR(int* a, int left, int right)
{
	Stack* s = NULL;
	StackInit(&s);
	if (left < right)
	{
		StackPush(s, right);
		StackPush(s, left);
	}
	while (!StackEmpty(s))
	{
		//取起始位置
		int _left = StackTop(s);
		StackPop(s);
		//取结束位置
		int _right = StackTop(s);
		StackPop(s);

		if (_right - _left + 1 <= 10)
		{
			InsertSort(a + _left, _right - _left + 1);
		}
		else
		{
			int mid = PartSort1(a, _left, _right);
			//入右侧
			if (mid + 1 < _right)
			{
				StackPush(s, _right);
				StackPush(s, mid + 1);
			}
			//入左侧
			if (_left < mid - 1)
			{
				StackPush(s, mid - 1);
				StackPush(s, _left);
			}
		}

	}
	StackDestroy(&s);
}

归并排序

时间复杂度:O(N*logN)
空间复杂度:O(N)
稳定性:稳定

  • 思路
    归并排序类似二叉树的后序遍历。
    将序列二分为左区间和右区间,如果左右区间有序,就可以合并左右区间,此时序列有序。
    左右区间递归继续二分直至二分到一个元素或不存在后递归结束,此时这一个元素单独有序。
    合并已经有序的左区间和右区间后返回直至到根部。
    在这里插入图片描述
    代码
// 归并排序递归实现
void _MergeSort(int* a, int* tmp, int left, int right)
{
	if (left >= right)
		return;
	//二分序列
	int mid = (left + right) / 2;
	_MergeSort(a, tmp, left, mid);
	_MergeSort(a, tmp, mid+1, right);
	
	//合并有序序列
	int begin_l = left, end_l = mid;
	int begin_r = mid + 1, end_r = right;
	int index = left;
	while (begin_l <= end_l && begin_r <= end_r)
	{
		if (a[begin_l] <= a[begin_r])
		{
			tmp[index++] = a[begin_l++];
		}
		else
		{
			tmp[index++] = a[begin_r++];
		}
	}
	while (begin_l <= end_l)
	{
		tmp[index++] = a[begin_l++];
	}
	while (begin_r <= end_r)
	{
		tmp[index++] = a[begin_r++];
	}
	memcpy(a + left, tmp + left, sizeof(int) * (right - left + 1));
}

void MergeSort(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	_MergeSort(a, tmp, 0, n-1);
	//_MergeSortNonR(a, tmp, n);
}

非递归

  • 思路
    非递归思路上就没有递归的模拟后序遍历麻烦,不需要二分序列,直接视每个元素单独有序开始操作。
    设值gap=1,每gap个元素为一组(每个元素为一组),每两个gap组两两合并(每两个元素合并,就是合并左右区间),直至合并到序列尾部,合并后gap*=2(之后就和递归版差不多,每组元素个数翻倍)。
    在这里插入图片描述
    代码
// 归并排序非递归实现
void _MergeSortNonR(int* a, int* tmp, int n)
{
	int grap = 1;
	while (grap < n)
	{
		//grap组归并
		for (int i = 0; i < n; i += grap * 2)
		{
			int begin_l = i, end_l = i + grap - 1;
			int begin_r = i + grap, end_r = i + grap*2 - 1;
			//左侧结束位置或右侧开始位置越界,不归并,直接下次循环
			if (end_l >= n || begin_r >= n)
				continue;
			//右侧结束位置越界,结束位置改为n-1
			if (end_r >= n)
				end_r = n - 1;
			int index = i;
			while (begin_l <= end_l && begin_r <= end_r)
			{
				if (a[begin_l] <= a[begin_r])
				{
					tmp[index++] = a[begin_l++];
				}
				else
				{
					tmp[index++] = a[begin_r++];
				}
			}
			while (begin_l <= end_l)
			{
				tmp[index++] = a[begin_l++];
			}
			while (begin_r <= end_r)
			{
				tmp[index++] = a[begin_r++];
			}
			memcpy(a + i, tmp + i, sizeof(int) * (end_r - i + 1));
		}
		grap *= 2;
	}

}

完整代码

sort.h

#pragma once
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
#include <assert.h>
#include "stack.h"


void PrintArray(int* a, int n);
void InsertSort(int* a, int n);
void ShellSort(int* arr, int n);
void SelectSort(int* arr, int n);
void HeapSort(int* arr, int n);
void BubbleSort(int* a, int n);

// 快速排序实现
void QuickSort(int* a, int n);

// 归并排序实现
void MergeSort(int* a, int n);

void CountSort(int* a, int n);

sort.c

#pragma once
#include "Sort.h"

//打印数组
void PrintArray(int* arr, int n)
{
	for (int i = 0; i < n; ++i)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}

//交换两个数
void Swap(int* a, int* b)
{
	int tmp = *a;
	*a = *b;
	*b = tmp;
}


//直接插入排序(升序)
void InsertSort(int* arr, int n)
{
	for (int i = 1; i < n; i++)
	{
		int end = i - 1;
		int tmp = arr[i];
		while (end >= 0)
		{
			if (tmp < arr[end])
			{
				arr[end + 1] = arr[end];
				--end;
			}
			else
			{
				break;
			}
		}

		arr[end + 1] = tmp;
	}
}


//希尔排序(升序)
void ShellSort(int* arr, int n)
{
	//设置组
	int gap = n;
	while (gap > 1)
	{
		gap = gap / 3 + 1;
		共gap组,循环gap组
		//for (int i = 0; i < gap; i++)
		//{
		//	//每组循环n / gap - 1次,每组数据内的间隔为gap,每次循环插入一个数据
		//	for (int j = i; j < n - gap; j += gap)
		//	{

		//		int end = j;
		//		//保存新插入的数据
		//		int tmp = arr[j + gap];
		//		//和组内数据比较
		//		while (end >= 0)
		//		{
		//			//如果新插入的数据小于上一个数据
		//			if (tmp < arr[end])
		//			{
		//				//将上一个数据向后挪动
		//				arr[end + gap] = arr[end];
		//				//再次对比上上个数据
		//				end -= gap;
		//			}
		//			else
		//				break;
		//		}
		//		//写入新插入的数据
		//		arr[end+gap] = tmp;
		//	}
		//}

		//共gap组,循环gap组  //每组循环n / gap - 1次,间隔为gap的数据为一组,每次循环插入一个数据
		for (int i = 0; i < n - gap; i++)
		{
			int end = i;
			//保存新插入的数据
			int tmp = arr[i + gap];
			//和组内数据比较
			while (end >= 0)
			{
				//如果新插入的数据小于上一个数据
				if (tmp < arr[end])
				{
					//将上一个数据向后挪动
					arr[end + gap] = arr[end];
					//再次对比上上个数据
					end -= gap;
				}
				else
					break;
			}
			//写入新插入的数据
			arr[end + gap] = tmp;

		}
	}
}



//选择排序
void SelectSort(int* arr, int n)
{
	
	for (int i = 0; i < n / 2; i++)
	{
		int mini = i;
		int max = i;
		for (int j = i+1; j < n-i; j++)
		{
			if (arr[j] > arr[max])
				max = j;
			if (arr[j] < arr[mini])
				mini = j;
		}
		Swap(&arr[i], &arr[mini]);
		// 如果i和maxi重叠,交换后修正一下
		if (max == i)
			max = mini;
		Swap(&arr[n-i-1], &arr[max]);
	}
}

// 堆排序
void AdjustDwon(int* arr, int n, int parent)
{
	int child = parent * 2 + 1;
	while (child < n)
	{
		if (child + 1 < n && arr[child + 1] > arr[child])
			child++;
		if (arr[child] > arr[parent])
		{
			Swap(&arr[child], &arr[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
			break;
	}
}


void HeapSort(int* arr, int n)
{
	//建大堆
	for (int parent = (n - 1 - 1) / 2; parent >= 0; parent--)
	{
		AdjustDwon(arr, n, parent);
	}
	//排序
	for (int end = n-1; end > 0; end--)
	{
		Swap(&arr[0], &arr[end]);
		AdjustDwon(arr, end, 0);
	}
}


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



// 快速排序递归实现
 

// 三数取中
int GetMid(int* a, int left, int right)
{
	int mid = (left + right) / 2;
	if (a[left] > a[mid])
	{
		if (a[mid] > a[right])
			return mid;
		else
			return right;
	}
	else
	{
		if (a[left] > a[right])
			return left;
		else
			return right;
	}
}

// 快速排序hoare版本
int PartSort1(int* a, int left, int right)
{
	//取最小值的下标
	int min = GetMid(a, left, right);
	//如果和left不同,交换值
	if (min != left)
		Swap(&a[min], &a[left]);
	//设置key值的下标
	int keyi = left;
	while (left < right)
	{
		//右侧找比key小的值
		while (left < right && a[right] >= a[keyi])
		{
			right--;
		}
		//左侧找比key大的值
		while (left < right && a[left] <= a[keyi])
		{
			left++;
		}
		//交换两个值的位置
		Swap(&a[left], &a[right]);
	}
	//与key交换位置
	Swap(&a[keyi], &a[left]);
	return left;
}

// 快速排序挖坑法
int PartSort2(int* a, int left, int right)
{
	//取最小值的下标
	int min = GetMid(a, left, right);
	//如果和left不同,交换值
	if (min != left)
		Swap(&a[min], &a[left]);
	//设置key值
	int key = a[left];
	while (left < right)
	{
		//右侧找比key小的值
		while (left < right && a[right] >= key)
		{
			right--;
		}
		//将小的保存在左侧
		a[left] = a[right];
		//左侧找比key大的值
		while (left < right && a[left] <= key)
		{
			left++;
		}
		//将大的保存在右侧
		a[right] = a[left];
	}
	//保存key的值
	a[left] = key;
	return left;

}

// 快速排序前后指针法
int PartSort3(int* a, int left, int right)
{
	//取最小值的下标
	int min = GetMid(a, left, right);
	//如果和left不同,交换值
	if (min != left)
		Swap(&a[min], &a[left]);
	//设置key值的下标
	int keyi = left; 
	int fast = left+1;
	int slow = left;
	while (fast <= right)
	{
		if (a[fast] < a[keyi])
		{	
			if (fast != ++slow)
				Swap(&a[fast], &a[slow]);
		}
		fast++;
	}
	Swap(&a[keyi], &a[slow]);
	return slow;
}

void _QuickSort(int* a, int left, int right)
{
	if (left >= right)
		return;
	if (right - left + 1 <= 10)
	{
		InsertSort(a + left, right - left + 1);
		return;
	}
	//int mid = PartSort1(a, left, right);
	//int mid = PartSort2(a, left, right);
	int mid = PartSort3(a, left, right);
	_QuickSort(a, left, mid - 1);
	_QuickSort(a, mid + 1, right);
}


// 快速排序 非递归实现
void _QuickSortNonR(int* a, int left, int right)
{
	Stack* s = NULL;
	StackInit(&s);
	if (left < right)
	{
		StackPush(s, right);
		StackPush(s, left);
	}
	while (!StackEmpty(s))
	{
		//取起始位置
		int _left = StackTop(s);
		StackPop(s);
		//取结束位置
		int _right = StackTop(s);
		StackPop(s);

		if (_right - _left + 1 <= 10)
		{
			InsertSort(a + _left, _right - _left + 1);
		}
		else
		{
			int mid = PartSort1(a, _left, _right);
			//入右侧
			if (mid + 1 < _right)
			{
				StackPush(s, _right);
				StackPush(s, mid + 1);
			}
			//入左侧
			if (_left < mid - 1)
			{
				StackPush(s, mid - 1);
				StackPush(s, _left);
			}
		}
	}
	StackDestroy(&s);
}

void QuickSort(int* a, int n)
{
	//_QuickSort(a, 0, n - 1);
	_QuickSortNonR(a, 0, n - 1);
}


// 归并排序递归实现
void _MergeSort(int* a, int* tmp, int left, int right)
{
	if (left >= right)
		return;
	int mid = (left + right) / 2;
	_MergeSort(a, tmp, left, mid);
	_MergeSort(a, tmp, mid+1, right);

	int begin_l = left, end_l = mid;
	int begin_r = mid + 1, end_r = right;
	int index = left;
	while (begin_l <= end_l && begin_r <= end_r)
	{
		if (a[begin_l] <= a[begin_r])
		{
			tmp[index++] = a[begin_l++];
		}
		else
		{
			tmp[index++] = a[begin_r++];
		}
	}
	while (begin_l <= end_l)
	{
		tmp[index++] = a[begin_l++];
	}
	while (begin_r <= end_r)
	{
		tmp[index++] = a[begin_r++];
	}
	memcpy(a + left, tmp + left, sizeof(int) * (right - left + 1));
}

// 归并排序非递归实现
void _MergeSortNonR(int* a, int* tmp, int n)
{
	int grap = 1;
	while (grap < n)
	{
		//grap组归并
		for (int i = 0; i < n; i += grap * 2)
		{
			int begin_l = i, end_l = i + grap - 1;
			int begin_r = i + grap, end_r = i + grap*2 - 1;
			//左侧结束位置或右侧开始位置越界,不归并,直接下次循环
			if (end_l >= n || begin_r >= n)
				continue;
			//右侧结束位置越界,结束位置改为n-1
			if (end_r >= n)
				end_r = n - 1;
			int index = i;
			while (begin_l <= end_l && begin_r <= end_r)
			{
				if (a[begin_l] <= a[begin_r])
				{
					tmp[index++] = a[begin_l++];
				}
				else
				{
					tmp[index++] = a[begin_r++];
				}
			}
			while (begin_l <= end_l)
			{
				tmp[index++] = a[begin_l++];
			}
			while (begin_r <= end_r)
			{
				tmp[index++] = a[begin_r++];
			}
			memcpy(a + i, tmp + i, sizeof(int) * (end_r - i + 1));
		}
		grap *= 2;
	}

}


void MergeSort(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	//_MergeSort(a, tmp, 0, n-1);
	_MergeSortNonR(a, tmp, n);
}

// 计数排序
void CountSort(int* a, int n)
{
	int max = 0;
	for (int i = 0; i < n; i++)
	{
		if (a[i] > max)
			max = a[i];
	}
	max++;
	int* tmp = (int*)malloc(sizeof(int) * max);
	memset(tmp, 0, sizeof(int) * max);
	for (int i = 0; i < n; i++)
	{
		tmp[a[i]]++;
	}
	int index = 0;
	for (int i = 0; i <= max-1; i++)
	{
		while (tmp[i]--)
		{
			a[index++] = i;
		}
	}
	free(tmp);
}

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值