逆序数问题(归并排序,C++)

在求解八数码问题时,因为要进行逆序数的计算判断两个结点的可达性,同奇偶的逆序数才能可达。如果只是八数,暴力解法还好,当数字多了之后如何知道逆序数呢。

题目描述

通过计算八数码节点的逆序数判断。如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。一个排列中逆序的总数就称为这个排列的逆序数。逆序数为偶数的排列称为偶排列;逆序数为奇数的排列称为奇排列。如 2431 中,21,43,41,31 是逆序,逆序数是 4,为偶排列。
计算八数码节点的逆序数时将代表空格的 0 去除,如初始状态排列为 (1,3,2,4,5,6,7,8) 逆序数为:0+1+0+0+0+0+0+0=1 即为奇排列
目标状态排列为(1,2,3,8,4,7,6,5) 逆序数0+0+0+4+0+2+1+0=7 即为奇排列,具有同奇或同偶排列的八数码才能移动可达,否则不可达。

思路一

暴力穷举,每次扫描一个数时,遍历后方所有的数,那么这样的代价很大,O(n2)的时间复杂度。

思路二

分治的思想,自上而下划分,将大数组划分为小数组,只到划分到为数组中只有一个时,通过自下而上的排序的过程,计算出本数组中的数与相邻数组的逆序数,其实本质就是归并排序的过程。时间复杂度nlog2n。
逆序数问题(归并排序,C++)_第1张图片
那么怎么在归并排序的过程中进行分组呢?
例如下方,先考察相邻数组块的最大值,比较后若左侧大于右侧,则逆序数加上右侧剩余数目(因为两侧数据皆为有序),并加大数填入最终的位置,并将大数所在数组指针向右侧移动。直到指针全部移到最左端。当一侧的数据全部填入之后,将剩余部分填入最前端。
逆序数问题(归并排序,C++)_第2张图片

代码实现

作为数组,可以省略“分”的步骤,看成已经将每个数分成一个单位,直接进行“治”的过程。

`int sort(int *b, int start,int step,int num)//排序相邻的数据块
{
	int result = 0;
	if (start + step >= num)//如果是最后只剩一个数组块,不需要比较排序
	{
		return 0;
	}
	else//如果最后剩余两个数组块
	{
		int c[20];//临时保存最终排序完成的数组,从大到小排列,共rightend-leftend+1个元素
		int n = 0;
		int leftstart = start;
		int leftend = start + step - 1;//前一个数组的起始和最后
		int rightstart = start + step;
		int rightend;//后一个数组的起始和最后
		if (start + 2 * step > num)//最后一个数组块不足step个
		{
			rightend = num-1;
		}
		else//两个比较的数据块相同个数都为step个 
		{
			rightend = start + 2 * step - 1;
		}
		int left = leftend;
		int right = rightend;
		while (left>=leftstart&&right>=rightstart)
		{
			if (b[left] < b[right])
			{
				c[n++] = b[right];
				right--;
			}
			else
			{
				c[n++] = b[left];
				left--;
				result += right - rightstart + 1;
			}
		}
		int begin = start;
		if (left >= leftstart)//最小的一部分在左侧
		{
			begin = left + 1;
		}
		else//最小的一部分在右侧
		{
			for (int i = rightstart; i <= right; i++)
			{
				b[begin++] = b[i];
			}
		}
		for (int i = begin; i <= rightend; i++)
		{
			b[i] = c[--n];
		}
		return result;
	}

}
int inverse_number(int *a, int num)//归并排序
{
	int b[20];
	for (int i = 0; i < num; i++)
	{
		b[i] = a[i];
	}
	int sum = 0;
	int step = 1;
	while (step < num)
	{
		for (int i = 0; i < num; i =i + 2*step)
		{
			sum += sort(b, i, step,num);
		}
		step = step * 2;
	}
	return sum;
}`

优化

当经过一轮排序,所有数据未发生改变即可停止。

你可能感兴趣的:(算法C++)