PAT-ADVANCED1067/Data Structures and Algorithms7-16——Sort with Swap(0, i)

我的PAT-ADVANCED代码仓:https://github.com/617076674/PAT-ADVANCED

我的Data Structures and Algorithms代码仓:https://github.com/617076674/Data-Structures-and-Algorithms

原题链接:

PAT-ADVANCED1067:https://pintia.cn/problem-sets/994805342720868352/problems/994805403651522560

Data Structures and Algorithms7-16:https://pintia.cn/problem-sets/16/problems/678

题目描述:

PAT-ADVANCED1067、Data Structures and Algorithms7-16:

题目翻译:

1067 用Swap(0, i)进行排序

给出任意一个由数字{0, 1, 2, ..., N - 1}组成的序列,很容易把它们排列成増序。但是如果你只能使用操作Swap(0, *)呢?距骨例子,为了排列{4, 0, 2, 1, 3},我们可以进行以下交换操作:

Swap(0, 1) => {4, 1, 2, 0, 3}
Swap(0, 3) => {4, 1, 2, 3, 0}
Swap(0, 4) => {0, 1, 2, 3, 4}

对于给定的N个非负整数,你需要计算出最少的交换次数。

输入格式:

每个输入文件包含一个测试用例,每个测试用例给出一个正整数N(<= 10 ^ 5),接下来跟着一串由{0, 1, 2, ..., N - 1}组成的序列。一行中的所有数字由一个空格分隔。

输出格式:

对每个测试用例,对于给定序列,你需要输出最少的交换次数。

输入样例:

10
3 5 7 2 6 4 9 0 8 1

输出样例:

9

知识点:贪心算法

思路一:递归实现(测试点1和测试点2会超时)

首先明确一个说法,元素在其本位,意思是0在0索引位置,1在1索引位置,2在2索引位置……

递归终止条件

如果当前序列已经有序,直接返回0,我们不需要进行任何交换。

递归过程

(1)如果当前序列的首元素是0,我们将首元素与第一个不在本位的元素进行交换后,交换次数加1,再递归调用该函数。

(2)如果当前序列的首元素不是0,我们交换两个元素的位置,一个元素是0,另一个元素是0所在索引值,交换次数加1,再递归调用该函数。

时间复杂度是O(N ^ 2)。空间复杂度是O(N)。

C++代码:

#include<iostream>
#include<algorithm>

using namespace std;

int countSwap(int* array, int N);

int main(){
	int N;
	scanf("%d", &N);
	int array[N];
	for(int i = 0; i < N; i++){
		scanf("%d", &array[i]);
	}
	printf("%d\n", countSwap(array, N));
	return 0;
}

int countSwap(int* array, int N){
	bool flag = true;
	for(int i = 0; i < N; i++){
		if(*(array + i) != i){
			flag = false;
			break;
		}
	}
	//flag = true说明array数组已经已经排好序了 
	if(flag){
		return 0;
	}
	//如果array数组的第一个元素是0 
	if(*array == 0){
		for(int i = 1; i < N; i++){
			if(*(array + i) != i){
				swap(*array, *(array + i));
				break;
			}
		}
		return 1 + countSwap(array, N);
	}else{
		for(int i = 0; i < N; i++){
			if(*(array + i) == 0){
				int another = -1;
				for(int j = 0; j < N; j++){
					if(i == *(array + j)){
						another = j;
						break;
					}
				}
				swap(*(array + i), *(array + another));
				break;
			}
		}
		return 1 + countSwap(array, N);
	} 
} 

C++解题报告:

思路二:寻找待排序序列中有多少个blocks

首先明确这里我的blocks的定义。

对于序列{4, 0, 2, 1, 3}而言,只有2在其本位。

对于0号索引,其值是4。

对于4号索引,其值是3。

对于3号索引,其值是1。

对于1号索引,其值是0

我们说0、4、3、1号索引构成一个blocks。而2号索引在其本位,所以该序列只有1个blocks。

对于序列{3, 5, 7, 2, 6, 4, 9, 0, 8, 1}而言,只有8在其本位。

对于0号索引,其值是3。

对于3号索引,其值是2。

对于2号索引,其值是7。

对于7号索引,其值是0

我们说0、3、2、7号索引构成一个blocks。

对于1号索引,其值是5。

对于5号索引,其值是4。

对于4号索引,其值是6。

对于6号索引,其值是9。

对于9号索引,其值是1

我们说1、5、4、6、9号索引构成一个blocks。而8号索引在其本位,且该序列中所有的索引都归类进了一个blocks,因此该序列共有2个blocks。

(1)我们首先处理0所在的blocks,其需要交换的次数是size - 1,这里size指的是该blocks中的元素个数。在这个操作后,0在其本位

(2)如果还有其他blocks,其交换的时候必然要把0给包含进来,也就是说需要先把0与该blocks中的任意元素进行交换,这个时操作包含了一次交换,再进行对包含了0的新blocks进行操作,这时新blocks中的元素要比原blocks中的元素多一个0,因此需要交换的次数是size + 1 - 1,因此需要进行的交换总次数就是1 + size + 1 - 1 = size + 1。

而如果一开始0就在本位,那么参照(2)中的情形进行处理即可。

因此如果0不在其本位,我们需要交换的总次数就是不在本位的元素个数再加上blocks的个数,再减2。而如果0在其本位,我们需要多额外2次交换操作。

时间复杂度是O(N)。空间复杂度是O(1)。

C++代码:

#include<iostream>
#include<algorithm>

using namespace std;

int main(){
	int N;
	scanf("%d", &N);
	int array[N];
	int tempArray[N];
	for(int i = 0; i < N; i++){
		scanf("%d", &array[i]);
		tempArray[i] = i;
	}
	bool visited[N];
	fill(visited, visited + N, false);
	int blocks = 0;
	int result = 0;
	for(int i = 0; i < N; i++){
		if(array[i] == tempArray[i]){
			visited[i] = true;
		}else{
			result++;
		}
		if(!visited[i]){
			visited[i] = true;
			int temp = i;
			while(true){
				int j = array[temp];
				if(j == i){
					break;
				}
				visited[j] = true;
				temp = j;
			}
			blocks++;
		}
	}
	result += blocks - 2;
	if(array[0] == 0){
		result += 2;
	}
	printf("%d\n", result);
	return 0;
}

C++解题报告:

思路三:对思路一的改进

在思路一中,我们每次递归都需要在循环中寻找一个不在本位上的数,每次都是从头开始枚举序列中的数,判断其是否在其本位上。更合适的做法是利用每个移回本位的数在后续操作中不再移动的特点,从整体上定义一个变量k,用来保存目前序列中不在本位上的最小数(初始为1),当交换过程中出现0回归本位的情况时,总是从当前的k开始继续增大寻找不在本位上的数,这样就能保证时间复杂度是O(N)。

空间复杂度是O(1)。

C++代码:

#include<iostream>
#include<algorithm>

using namespace std;

int main(){
	int N;
	scanf("%d", &N);
	int array[N];	//存放各数字当前所处的位置编号 
	int unOrdered = 0;	//unOrdered记录除0以外不在本位上的数的个数 
	int num; 
	for(int i = 0; i < N; i++){
		scanf("%d", &num);
		array[num] = i;
		if(array[i] != i && i != 0){
			unOrdered++;
		}
	}
	int k = 1;	//k存放除0以为当前不在本位上的最小数
	int result = 0;
	while(unOrdered > 0){
		//如果0在本位上,则寻找一个当前不在本位上的数与0交换 
		if(array[0] == 0){
			while(k < N){
				if(array[k] != k){
					swap(array[k], array[0]);
					result++;
					break;
				}
				k++;
			}
		}
		//只要0不在本位,就将0所在位置的数的当前所处位置与0的位置交换 
		while(array[0] != 0){
			swap(array[0], array[array[0]]);	//将0与array[0]交换 
			result++;	//交换次数加1 
			unOrdered--;	//不在本位上的数的个数减1 
		}
	} 
	printf("%d\n", result);
	return 0;
}

C++解题报告:

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
数据结构和算法是计算机科学中非常重要的概念。数据结构是组织和存储数据的方式,而算法是在给定的数据结构上执行操作的方法。数据结构和算法的学习对于编写高效和可维护的代码至关重要。 JavaScript是一种流行的编程语言,广泛应用于Web开发和移动应用程序开发。它是一种高级语言,具有灵活性和易读性,使得学习数据结构和算法用JavaScript实现变得容易。 在使用JavaScript实现数据结构和算法时,可以利用JavaScript提供的一些内置数据结构,例如数组和对象。数组可以用于存储和访问有序的数据集合,而对象可以用于存储键值对。此外,JavaScript还提供了一些内置的数据结构和操作,例如队列、堆栈和链表等。 在学习数据结构和算法时,可以使用JavaScript来实现各种常见的数据结构,例如栈、队列、链表、二叉树和图等。通过实践编写这些数据结构的JavaScript实现,可以更好地理解它们的原理和应用。同时,还可以通过使用JavaScript编写算法来解决各种问题,例如排序、搜索和图算法等。 使用JavaScript实现数据结构和算法的好处之一是可以直接在浏览器中运行和调试代码,无需额外的编译和配置。此外,JavaScript的简洁语法和灵活性使得代码编写变得简单和直观。 总之,数据结构和算法是计算机科学中的基础知识,而JavaScript是一种流行且易于学习和使用的编程语言。通过学习和实践数据结构和算法与JavaScript的结合,可以提高编写高效和可维护代码的能力。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值