趣学数据结构--第九章:排序

插入排序

直接插入排序

  • 直接插入排序是最简单的排序方法,每次将一个待排序的记录,插入已经排好序的数据序列中,得到一个新的长度增1的有序表。
#include <iostream>
using namespace std;

#define Maxsize 100

//直接插入排序
void StraightInsertSort(int r[], int n)
{
	int i, j;
	for (i = 2; i <= n; i++)  //r[i]插入有序子表
	{
		if (r[i] < r[i - 1])   //r[i]和前一个元素r[i-1]比较
		{
			r[0] = r[i];  //r[i]暂存到r[0]中,r[0]有监视哨的作用
			r[i] = r[i - 1];   //r[i-1]后移一位
			for (j = i - 2; r[j] > r[0];j--)  //从后向前寻找插入位置,逐个后移,直到找到插入位置
			{
				r[j + 1] = r[j];  //r[j]后移一位
			}
			r[j + 1] = r[0];   //将r[0]插入到r[j+1]位置
		}
	}
}

int main()
{
	int i, n, r[Maxsize + 1];
	cout << "请输入数列中元素个数n: " << endl;
	cin >> n;
	cout << "请依次输入数列中的元素:" << endl;
	for (i = 1; i <= n; i++)
		cin >> r[i];
	StraightInsertSort(r, n);
	cout << "直接插入排序结果:" << endl;
	for (i = 1; i <= n; i++)
		cout << r[i] << " ";
	return 0;
}

交换排序

冒泡排序

  • 冒泡排序是一种最简单的交换排序算法,通过两两比较关键字,如果逆序就交换,使关键字大的记录像泡泡一样冒出来放在尾部,重复执行若干次冒泡排序,最终得到有序序列。
#include <iostream>
using namespace std;

#define Maxsize 100

//冒泡排序
void BubbleSort(int r[], int n)
{
	int i, j, temp;
	bool flag;
	i = n - 1;
	flag = true;
	while (i > 0 && flag)  // i 层循环(比较趟数)
	{
		flag = false;  // flag 标志位:当某一趟排序无交换时,停止循环,减少运算量
		for (j = 0; j < i; j++)  // j 层循环 (每趟对比次数)
		{
			if (r[j] > r[j + 1])
			{
				flag = true;
				temp = r[j];
				r[j] = r[j + 1];
				r[j + 1] = temp;
			}
		}
		for (j = 0; j <= i; j++)//测试每趟排序结果
			cout << r[j] << " ";
		cout << endl;
		i--;
	}
}

int main()
{
	int i, n, r[Maxsize];
	cout << "请输入数列中元素个数n: " << endl;
	cin >> n;
	cout << "请依次输入数列中的元素:" << endl;
	for (i = 0; i < n; i++)
		cin >> r[i];
	BubbleSort(r, n);
	cout << "冒泡排序结果:" << endl;
	for (i = 0; i < n; i++)
		cout << r[i] << " ";
	return 0;
}

在这里插入图片描述

快速排序

快速排序算法是基于分治策略的,其思想为:

  • 分解:先从数列中取出一个元素作为基准元素。以基准元素为标准,将序列分解为两个子序列,使小于或等于基准元素的子序列在左侧,使大于基准元素的子序列在右侧。
  • 治理:对两个子序列进行快速排序
  • 合并:将排好序的两个子序列合并在一起,得到原问题的解
#include <iostream>
using namespace std;

// 划分函数
/*
对原序列进行分解,将其分解为两个子序列,以基准元素pivot为界
左侧子序列小于等于pivot,右侧子序列大于pivot。
*/
int Partition(int r[], int low, int high)
{
	int i = low, j = high, pivot = r[low];  // 基准元素
	while (i < j)
	{
		while (i<j && r[j]>pivot)  // 从右向左扫描
			j--;
		if (i < j)
			swap(r[i++], r[j]);  //r[i]和r[j]交换后, i+1 右移一位
		while (i < j&& r[i]<=pivot)  // 从左向右扫描
			i++;
		if (i < j)
			swap(r[i], r[j--]);  // r[i]和r[j]交换 后j-1左移一位
	}
	return i;  //返回最终划分完成后基准元素所在的位置
}


// 划分函数改进
/*
从右向左扫描,找小于等于pivot的数R[i]
从左向右扫描,找大于pivot的数R[j]
让R[i]和R[j]交换
一直交替进行,直到i = j,这时将基准元素与R[i]交换
*/
int Partition2(int r[], int low, int high)
{
	int i = low, j = high, pivot = r[low];
	while (i < j)
	{
		while (i<j && r[j]>pivot) j--;  // 向左扫描
		while (i<j && r[i]<=pivot) i++; // 向右扫描
		if (i < j)
			swap(r[i++], r[j--]);  // r[i]和r[j]交换,然后i++,j--
	}
	if (r[i] > pivot)
	{
		swap(r[i - 1], r[low]);//  r[i-1]和r[low]交换
		return i - 1;
	}
	swap(r[i], r[low]);
	return i;
}


// 快速排序
/*
首先对原序列执行划分,得到划分的中间位置mid,
然后以中间位置为界,分别对左半部分(low,mid-1)执行快排
右半部分(mid+1,high)执行快排
*/
void QuickSort(int R[], int low, int high)
{
	int mid;
	if (low < high)
	{
		mid = Partition2(R, low, high);  // 返回基准元素位置
		QuickSort(R, low, mid - 1);   // 左区间递归快速排序
		QuickSort(R, mid + 1, high);  // 右区间递归快速排序
	}
}


int main()
{
	int a[100];
	int i, n;
	cout << "请先输入要排序的数据的个数:" << endl;
	cin >> n;
	cout << "请输入要排序的数据:" << endl;
	for (i = 0; i < n; i++)
		cin >> a[i];
	cout << endl;
	QuickSort(a, 0, n - 1);
	cout << "排序后的序列为:" << endl;
	for (i = 0; i < n; i++)
		cout << a[i] << "  ";
	cout << endl;
	return 0;
}

选择排序

简单选择排序

  • 最简单的选择排序算法,每次从待排序序列中选择一个最小的放在前面
#include <iostream>
using namespace std;

#define Maxsize 100

// 简单选择排序
void SimpleSelectSort(int r[], int n)
{
	int i, j, k, temp;
	for (i = 0; i < n-1; i++)  // n-1趟
	{
		k = i;
		for (j = i + 1; j < n; j++)  //找最小值
		{						//若r[k],即r[i]正好是最小的,	
			if (r[j] < r[k])	// 则不交换,i++,执行下一次循环
				k = j;
		}
		if (k != i)
		{
			temp = r[i];
			r[i] = r[k];
			r[k] = temp;
		}
		for (j = 0; j < n; j++)//测试每趟排序结果
			cout << r[j] << " ";
		cout << endl;
	}
}

int main()
{
	int i, n, r[Maxsize];
	cout << "请输入数列中的元素个数n为:" << endl;
	cin >> n;
	cout << "请依次输入数列中的元素:" << endl;
	for (i = 0; i < n; i++)
		cin >> r[i];
	SimpleSelectSort(r, n);
	cout << "简单选择排序结果:" << endl;
	for (i = 0; i < n; i++)
		cout << r[i] << " ";
	return 0;
}

堆排序

  • 堆可以看做一棵完全二叉树的顺序存储结构。
  • 在这棵完全二叉树中,如果每一个结点的值都大于等于左右孩子结点的值,称为最大堆(大顶堆),
  • 如果每一个结点的值都小于等于左右孩子结点的值,称为最小堆(小顶堆)。

堆排序充分利用堆顶记录最大(最小)的性质进行排序,每次将堆顶记录交换到最后,剩余记录调整为堆即可。

大顶堆:从小到大排列
小顶堆:从大到小排列

#include <iostream>
using namespace std;

#define maxN 100
int r[maxN];


// 下沉操作
void Sink(int k, int n)
{
	while (2 * k <= n)  // 如果有左孩子,k的左孩子为2k,右孩子为2k+1
	{
		int j = 2 * k; // j 指向左孩子
		if (j < n && r[j] < r[j + 1]) // 如果有右孩子,且左孩子比右孩子小
			j++;  // j 指向右孩子
		if (r[k] >= r[j])  // 比 “较大的孩子” 大,
			break;  // 已满足堆
		else
			swap(r[k], r[j]);  // 与较大的孩子交换
		k = j;  //k指向交换后的新位置,继续向下比较,一直下沉到叶子
	}
}


// 构建初始堆
/*
首先按照完全二叉树的顺序构建一棵完全二叉树,
然后从最后一个分支节点 n/2 开始调整,
依次将序号为 n/2 - 1,n/2-2, ..., 1的结点
执行下沉操作调整为堆
*/
void CreatHeap(int n)
{
	for (int i = n / 2; i > 0; i--) //从最后一个分支结点n/2开始调整为堆,直到第一个结点
		Sink(i, n);
}


// 堆排序
void HeapSort(int n)
{
	CreatHeap(n);  // 构建初始堆
	while (n > 1)
	{
		swap(r[1], r[n--]); // 堆顶和最后一个记录交换,然后n-1
		Sink(1, n); // 堆顶下沉
	}
}

void print(int n)//输出
{
	for (int i = 1; i <= n; i++)
		cout << r[i] << "\t";
	cout << endl;
}


int main()
{
	int n;
	cout << "请输入待排序记录个数:" << endl;
	cin >> n;
	cout << "请输入 n 个整数:" << endl;
	for (int i = 1; i <= n; i++)
		cin >> r[i];
	cout << endl;
	HeapSort(n);//堆排序
	print(n);	
	return 0;
}

合并排序

  • 合并排序采用分治策略,将一个大问题分成若干个小问题,先解决小问题,再通过小问题解决大问题。
  • 可以把待排序序列分解成两个规模大致相等的子序列,如果不易解决,再将得到的子序列继续分解,直到子序列中包含的元素个数为1
  • 因为单个元素的序列本身是有序的,此时便可以进行合并,从而得到一个完整的有序序列
#include <iostream>
using namespace std;


// 完整的合并
void  Merge(int A[], int low, int mid, int high)
{
	int* B = new int[high - low + 1]; // 申请一个辅助数组
	int i = low, j = mid + 1, k = 0;
	while (i <= mid && j <= high)  // 按从小到大存放到辅助数组B[]中
	{
		if (A[i] <= A[j])
			B[k++] = A[i++];
		else
			B[k++] = A[j++];
	}
	while (i <= mid) B[k++] = A[i++]; // 将数组剩下的元素放置B中
	while (j <= high) B[k++] = A[j++]; 
	for (i = low, k = 0; i <= high; i++)
		A[i] = B[k++];
	delete []B; // 释放空间
}

// 合并排序
/* 采用递归形式
将序列分为两个子序列,然后对子序列进行递归排序,再把两个
已排好的子序列合并成一个有序的序列
*/
void MergeSort(int A[], int low, int high)
{
	if (low < high)  //将序列一分为二,二分为四,...,直到分成单个元素
	{
		int mid = (high + low) / 2;  //取中点
		MergeSort(A, low, mid);  //对A[low:mid]中的元素合并排序
		MergeSort(A, mid + 1, high);  //对A[mid+1:high]中的元素合并排序
		Merge(A, low, mid, high);//完整的合并
	}
}


int main()
{
	int n, A[100];
	cout << "请输入数列中的元素个数n为:" << endl;
	cin >> n;
	cout << "请依次输入数列中的元素:" << endl;
	for (int i = 0; i < n; i++)
		cin >> A[i];
	cout << endl;

	MergeSort(A, 0, n - 1);

	cout << "合并排序结果:" << endl;
	for (int i = 0; i < n; i++)
		cout << A[i] << " ";
	cout << endl;
	return 0;
}

分配排序

  • 分配排序不需要比较关键字的大小,根据关键字各位上的值,进行若干趟“分配”和“收集”,实现排序

桶排序

  • 将待排序序列划分为划分为若干个空间,每个区间可形象的看做一个桶,如果桶中的记录多于一个,则使用较快的排序方法进行排序,把每个桶中的记录收集起来,最终得到有序序列。

基数排序

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一记绝尘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值