插入排序还是堆排序(第十二周编程题)

7-3 插入排序还是堆排序 (25 分)
根据维基百科的定义:

插入排序是迭代算法,逐一获得输入数据,逐步产生有序的输出序列。每步迭代中,算法从输入序列中取出一元素,将之插入有序序列中正确的位置。如此迭代直到全部元素有序。

堆排序也是将输入分为有序和无序两部分,迭代地从无序部分找出最大元素放入有序部分。它利用了大根堆的堆顶元素最大这一特征,使得在当前无序区中选取最大元素变得简单。

现给定原始序列和由某排序算法产生的中间序列,请你判断该算法究竟是哪种排序算法?

输入格式:
输入在第一行给出正整数 N (≤100);随后一行给出原始序列的 N 个整数;最后一行给出由某排序算法产生的中间序列。这里假设排序的目标序列是升序。数字间以空格分隔。

输出格式:
首先在第 1 行中输出Insertion Sort表示插入排序、或Heap Sort表示堆排序;然后在第 2 行中输出用该排序算法再迭代一轮的结果序列。题目保证每组测试的结果是唯一的。数字间以空格分隔,且行首尾不得有多余空格。

输入样例 1:
10
3 1 2 8 7 5 9 4 6 0
1 2 3 7 8 5 9 4 6 0
输出样例 1:
Insertion Sort
1 2 3 5 7 8 9 4 6 0
输入样例 2:
10
3 1 2 8 7 5 9 4 6 0
6 4 5 1 0 3 2 7 8 9
输出样例 2:
Heap Sort
5 4 3 1 0 2 6 7 8 9

我的想法:

这道题目很容易读懂,就是两个排序——插入排序和堆排序,给出中间两个序列,判断排序方式,然后输出下一个序列,没别的内容了。其实完全可以按部就班的建堆,一点一点插入,然后一步一步排列,排列一次判断一次,直到找到样例,然后输出下一次。找不到就是插入排序,再根据插入排序算法计算到样例那一步,然后输出下一步。(傻瓜做法,但傻人有傻福,完全可以AC)【我不知道我没试过会不会超时】

仔细观察其实会发现,有好多空子可以钻:
1。堆排序的样例,中间任何一步中的第一个元素肯定大于第二个,而相应的插入排序的第一个元素肯定会小于第二个元素,所以可以根据这个判断排序方式。
2。堆排序根本不需要写建堆的插入函数,Shiftup也不需要,因为中间的任何一步的前面肯定是一个堆,而后面的内容从大到小往回排列已经排好了。

我卡在了某处

我一开始判断插入排序排到的位置的时候,有一个点出现了段错误(还好是特么段错误,要是答案错误我会开始怀疑堆排序的代码有问题,做完根本没的睡了就)。
先给你看一下我这部分代码:

int needsort=0;
		for (int i = N; i >= 1; i--) {
			if (a[i] != sample[i]) {
				needsort = i + 1; break;
			}
		}

我是判断的第二行和第一行是不是一致,如果一致说明还没动(还没排到这里)。然而段错误?因为一开始needsort我没有赋值。
是这样的样例没有考虑:
8
1 2 3 4 5 6 7 8
1 2 3 4 5 6 7 8
这样的样例一定要考虑,以为有一个点是这样的。它肯定是插入排序,因为堆排序的中间步骤的堆顶不可能是1。而插入排序对于有序数列是从头到尾都不改变的——这和题目中的答案唯一并不冲突,因为每一步的结果都是唯一的答案——1 2 3 4 5 6 7 8.
所以将判断位置的函数改成这样:

int needsort = 0, temp; bool next = false;
		for (int i = 2; i <= N; i++) {
			for (int j = i; j > 1; j--) {
				if (a[j] < a[j - 1]) {
					temp = a[j];
					a[j] = a[j - 1];
					a[j - 1] = temp;
				}
				else break;
			}

就,按部就班写了两层循环,O(n^2)的时间复杂度,完全按照插入排序方式来寻找位置。

AC代码:

#include<iostream>
using namespace std;
int a[105] = { -1 };
int sample[105] = { -1 };
int main() {
	int N; cin >> N;
	for (int i = 1; i <= N; i++)cin >> a[i];
	for (int i = 1; i <= N; i++)cin >> sample[i];
	if (sample[1] > sample[2]) {//HeapSort
		int stepnumber = 0;
		for (int i = 3; i <= N; i++) {
			if (sample[1] < sample[i]) {
				stepnumber = i; break;
			}
		}
		int temp = sample[1];
		sample[1] = sample[stepnumber - 1];
		sample[stepnumber - 1] = temp;
		stepnumber -= 2;
		int n = 1, max;
		for (; n * 2 <= stepnumber;) {
			if (n * 2 == stepnumber)max = stepnumber;
			else
				max = sample[n * 2] < sample[n * 2 + 1] ? n * 2 + 1 : n * 2;
			if (sample[n] < sample[max]) {
				temp = sample[max];
				sample[max] = sample[n];
				sample[n] = temp;
				n = max;
			}
			else break;
		}
		cout << "Heap Sort" << endl << sample[1];
		for (int i = 2; i <= N; i++)cout << " " << sample[i];
	}
	else {//插入排序
		int needsort = 0, temp; bool next = false;
		for (int i = 2; i <= N; i++) {
			for (int j = i; j > 1; j--) {
				if (a[j] < a[j - 1]) {
					temp = a[j];
					a[j] = a[j - 1];
					a[j - 1] = temp;
				}
				else break;
			}
			if (next) {
				cout << "Insertion Sort" << endl << a[1];
				for (int i = 2; i <= N; i++)cout << " " << a[i]; system("pause");
				return 0;
			}
			for (int i = 1; i <= N; i++) {
				if (sample[i] != a[i]) {
					next = false; break;
				}
				next = true;
			}
		}
	}
}
  • 4
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是六种排序算法的比较: 1. 插入排序 插入排序是一种简单直观的排序算法,它的基本思想是将一个记录插入到已经排好序的有序表中,从而得到一个新的、记录数增加1的有序表。插入排序的时间复杂度为O(n^2),适用于数据量较小的排序。 2. 希尔排序 希尔排序是一种改进的插入排序,它的基本思想是将待排序的数组按照一定的间隔分成若干个子序列,对每个子序列进行插入排序,然后逐步缩小间隔,直到间隔为1,最后对整个数组进行插入排序。希尔排序的时间复杂度为O(nlogn),适用于数据量较大的排序。 3. 选择排序 选择排序是一种简单直观的排序算法,它的基本思想是每次从待排序的数组中选择最小的元素,放到已排序的数组的末尾,直到所有元素都排序完毕。选择排序的时间复杂度为O(n^2),适用于数据量较小的排序。 4. 冒泡排序 冒泡排序是一种简单直观的排序算法,它的基本思想是每次比较相邻的两个元素,如果它们的顺序错误就交换它们的位置,直到所有元素都排序完毕。冒泡排序的时间复杂度为O(n^2),适用于数据量较小的排序。 5. 堆排序 堆排序是一种树形选择排序,它的基本思想是将待排序的数组构建成一个二叉堆,然后依次将堆顶元素与堆底元素交换,再重新调整堆,直到所有元素都排序完毕。堆排序的时间复杂度为O(nlogn),适用于数据量较大的排序。 6. 快速排序 快速排序是一种分治的排序算法,它的基本思想是选择一个基准元素,将数组分成两个子数组,小于基准元素的放在左边,大于基准元素的放在右边,然后递归地对子数组进行排序。快速排序的时间复杂度为O(nlogn),是一种效率比较高的排序算法

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值