数据结构第六章-排序

一、排序的概念及其运用

1.排序的概念

2.排序运用

3.常见的排序算法

1.插入排序

1.直接插入排序

2.希尔排序

2.选择排序

1.选择排序

2.堆排序

3.交换排序

1.冒泡排序

2.快速排序

4.归并排序

1.归并排序

二、常见排序算法的实现

1.插入排序

核心思想:在数组[0,end-1]之间已经排序完成,现加入一个新的元素array[end],与前面end个元素进行比较,插入到合适的位置中

void Swap(int* pa, int* pb)
{
	int tmp = *pa;
	*pa = *pb;
	*pb = tmp;
}

void PrintArray(int* array, int n)
{
	assert(array);

	for (int i = 0; i < n; i++)
	{
		printf("%d ", array[i]);
	}
	printf("\n");
}

// 插入排序
//传入数组的地址array,数组中的元素个数n
void InsertSort(int* array, int n)
{
	assert(array);

	for (int i = 0; i < n; i++)
	{
		int end = i;//单次排序中最后一个元素的下标
		while (end > 0)
		//当end不是首元素时
		{
			//当尾元素小于前一个元素时,两者互换
			if (array[end] < array[end - 1])
			{
				Swap(&array[end], &array[end - 1]);
				end--;
			}
			//当尾元素不小于前一个元素时,退出循环
			else
				break;
		}
	}
}

// 希尔排序
//传入数组的地址array,数组中的元素个数n
//希尔排序的思想:在插入排序的基础上,扩大其间隔,减少重复次数
void ShellSort(int* array, int n)
{
	assert(array);

	int gap = n;
	while (gap > 1)
	{
		gap = gap / 3 + 1;
		//gap以3的速度下降,并保证其必能取到1
		for (int i = 0; i < n - gap; i++)
		//i为控制的尾元素
		{
			int end = i + gap;
			//end为某一次的尾元素,且保证end<n
			//推出i<n-gap
			while (end >= gap)
			{
				if (array[end] < array[end - gap])
				{
					Swap(&array[end], &array[end - gap]);
					end = end - gap;
				}
				else
					break;
			}
		}
	}
}

2.选择排序

核心思想:遍历一遍数组,选出数组中最大/最小的元素,进行排序

void Swap(int* pa, int* pb)
{
	int tmp = *pa;
	*pa = *pb;
	*pb = tmp;
}

void PrintArray(int* array, int n)
{
	assert(array);

	for (int i = 0; i < n; i++)
	{
		printf("%d  ", array[i]);
	}
	printf("\n");
}

//选择排序-排升序
//传入数组地址array,传入数据个数n(n>0)
void SelectSort(int* array, int n)
{
	assert(array);

	int mini = 0;
	int maxi = n - 1;
	int Mini = 0;
	while (Mini < n / 2)
	{
		for (int i = Mini; i < n - Mini; i++)
		{
			if (array[i] < array[mini])
				mini = i;
			if (array[i] > array[maxi])
				maxi = i;
		}

		Swap(&array[Mini], &array[mini]);

		if (0 == maxi)
		{
			maxi = mini;
		}

		Swap(&array[n - Mini - 1], &array[maxi]);
		Mini++;
	}
}


// 堆排序-建大堆
//传入一个数组的地址array,数组中的元素个数n
//数组中需要排序的下标root
void AdjustDown(int* array, int n, int root)
{
	assert(array);

	int parent = root;
	int child = parent * 2 + 1;

	//当child下标在数组中时,进入循环
	while (child < n)
	{
		//选出左右孩子中的大的那个
		if (child + 1 < n && array[child + 1] > array[child])
		{
			child = child + 1;
		}

		//比较孩子和父亲,选出大的向上走
		if (array[child] > array[parent])
		{
			Swap(&array[child], &array[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
			break;
	}
	//出循环,说明已经调整好了
}

//堆排序-排升序
//传入一个数组的地址arrray,传入该数组中的元素个数n
void HeapSort(int* array, int n)
{
	assert(array);

	//向下调整建堆
	for (int root = (n - 1 - 1) / 2; root >= 0; root--)
	//n-1为最后一个数据的下标,(n-1-1)/2为其父元素的下标
	{
		AdjustDown(array, n, root);
	}

	//建好堆后进行排序
	int end = n - 1;//end为最后一个数据的下标
	while (end > 0)
	{
		Swap(&array[0], &array[end]);//交换最后一个数据与第一个数据
		//选出最大数据来
		AdjustDown(array, end, 0);
		end--;
	}
}

3.交换排序

核心思想:遍历数组,不断比较前后两个数据的大小,若前数据大于后数据,交换两个数据 

//0.打印数组
//传入一个数组地址array,传入该数组中元素个数n
void PrintArray(int* array, int n)
{
	assert(array);

	for (int i = 0; i < n; i++)
	{
		printf("%d ", array[i]);
	}
	printf("\n");
}

//0.交换数据
//传入需交换数据的两个地址pa和pb
void Swap(int* pa, int* pb)
{
	int tmp = *pa;
	*pa = *pb;
	*pb = tmp;
}

//1.冒泡排序-升序(从小到大)
//传入一个数组地址array,传入该数组中元素个数n
void BubbleSort(int* array, int n)
{
	assert(array);

	for (int j = 1; j < n; j++)
	{
		//调整一个数据
		for (int i = 0; i < n - j; i++)
		//这里的的n-j可以视为从下标为0到下标为n-j中找到那个数据
		{
			if (array[i] > array[i + 1])
			{
				Swap(&array[i], &array[i + 1]);
			}
		}
	}
	
}

//2.快速排序-递归实现
//2.1快速排序hoare版本
//传入一个数组的地址array,传入数组中起始(左边)位置的下标值left,传入数组中终止(右边)位置的下标值right
//传出排序完成后的下标值
int PartSort1(int* array, int left, int right)
{
	assert(array);

	//标记左边第一个数据的位置
	int key = left;

	//正常进行排序-left<right
	while (left < right)
	{
		//右边先开始向左走,找小于等于key位置的数据
		while (left < right && array[right] > array[key])
		{
			right--;
		}
		//出循环,说明此时1.array[right] <= array[key]
		//2.right与left重合

		//左边再往右走,找大于等于key位置的数据
		while (left < right && array[left] <= array[key])
		{
			left++;
		}
		//出循环,说明此时1.array[left] > array[key]
		//2.right与left重合

		Swap(&array[left], &array[right]);
	}
	//出循环,说明此时right与left重合

	//交换key位置和right位置中的数据,将right位置给key
	Swap(&array[right], &array[key]);

	return right;
}

//2.2快速排序挖坑法
//传入一个数组地址,传入需排序的起始坐标和末尾坐标
//排序一趟的做法,排好该数组的第一个数据应该在的位置
int PartSort2(int* array, int left, int right)
{
	assert(array);

	int key = array[left];//记录第一个数据的大小
	int pit = left;//记录坑位的位置

	//正常情况,左下标小于等于右下标,开始进行排序
	while (left < right)
	{
		//从最右边的数据开始找起,当右边的数据大于key时,向左找一个数据
		while (left < right && array[right] >= key)
		{
			right--;
		}
		//出循环,说明此时1.left与right重合
		//2.array[right] < key

		array[pit] = array[right];//把小于key的值赋值给坑位
		pit = right;//更新坑位

		while (left < right && array[left] <= key)
		{
			left++;
		}

		array[pit] = array[left];
		pit = left;
	}
	//出循环,说明左下标此时等于右下标,且array[left]为一个小于key的值

	array[pit] = key;
	return pit;
}

//2.3快速排序前后指针法
//传入一个数组地址,传入需排序的起始坐标和末尾坐标
//排序一趟的做法,排好该数组的第一个数据应该在的位置
int PartSort3(int* array, int left, int right)
{
	assert(array);

	int key = array[left];

	//定义两个走的不同的整数下标
	int slow = left;
	int fast = left;

	while (fast < right)
	{
		fast++;//快下标走一步

		if (array[fast] < key)
		{
			slow++;
			Swap(&array[slow], &array[fast]);
		}
	}

	Swap(&array[left], &array[slow]);
	return slow;
}

//传入一个数组的地址array,传入数组起始位置的下标begin,传入数组终止位置的下标end
//快速排序,排好array数组中从begin到end下标中的数据
void QuickSort(int* array, int begin, int end)
{
	assert(array);

	//非正常情况(停止情况),当左下标大于等于右下标时,此时无需排序,退出排序
	if (begin >= end)
	{
		return;
	}

	//正常情况(运行情况),当左下标小于右下标时,排好第一个元素在数组中的位置
	int key = PartSort1(array, begin, end);

	//进行递归,排好array数组中,剩余两侧的位置
	QuickSort(array, begin, key - 1);
	QuickSort(array, key + 1, end);
}

三、排序算法复杂度及稳定性分析

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值