归并排序及统计数组逆序对



1.归并排序

《算法导论》P19

参考网址:

白话经典算法系列之五 归并排序的实现 - MoreWindows Blog -
博客频道 - CSDN.NET

http://blog.csdn.net/morewindows/article/details/6678165

#include "stdafx.h"
#include <iostream>
using std::cout;

#define ARRAY_LENGTH 11

//排序两个已经排好序的数组
//参数:data原始数组,copy组合成的数组,start,mid,end记录两个数组在原始数组中的位置
//      mid表示第一个数组的截止位置
void Merge(int data[],int copy[],size_t start,size_t mid,size_t end)
{
	//判断输入参数是否合法
	if ((data != nullptr) && (copy != nullptr) && (start <= mid) && (mid < end))
	{
		size_t i = start;    //左数组开始位置
		size_t j = mid + 1;  //右数组开始位置
		size_t index = start;//辅助数组开始位置
		while ((i <= mid) && (j <= end))
		{
			//比较两个值,较小者放入辅助数组中,并将指针前移
			if (data[i] <= data[j])
			{
				copy[index++] = data[i++];
			}
			else
			{
				copy[index++] = data[j++];
			}
		}

		//处理一个数组遍历完,另一个数组还有剩余元素的情况
		while (i <= mid)
		{
			copy[index++] = data[i++];
		}

		while (j<=end)
		{
			copy[index++] = data[j++];
		}

		//整理排序好后的data数组
		for (size_t k = 0; k <= end; k++)
		{
			data[k] = copy[k];
		}
	}
}

//归并算法核心算法
void Merge_Sort(int data[], int copy[], size_t start, size_t end)
{
	if ((data != nullptr) && (copy != nullptr))
	{
		if ((start < end))
		{
			size_t mid = (start + end) / 2;
			//分治思想
			Merge_Sort(data, copy, start, mid);
			Merge_Sort(data, copy, mid + 1, end);
			//对数组进行排序
			Merge(data, copy, start, mid, end);
		}
	}
	else
	{
		cout << "数组参数输入有误";
	}
}

//打印数组
void printf_array(int data[], size_t length)
{
	for (size_t i = 0; i < length; i++)
	{
		cout << data[i] << std::endl;
	}
}

int _tmain(int argc, _TCHAR* argv[])
{
	int data[] = {10,32,9,1,23,42,5,67,88,23,7};
	//printf_array(data, ARRAY_LENGTH);
	int copy[ARRAY_LENGTH];
	Merge_Sort(data, copy, 0, ARRAY_LENGTH - 1);
	printf_array(data, ARRAY_LENGTH);
	return 0;
}

2.使用归并排序处理相关问题,统计数组中的逆序对

《剑指Offer》P191

// InversePais1.cpp : 计算N个元素的数组的逆序对
//

#include "stdafx.h"

//计算逆序对函数
int get_InversePairs(int data[],int copy[],size_t start,size_t mid,size_t end)
{
	int count = 0;
	//判断数组是否为空,及输入的位置参数是否合法
	if ((data!=nullptr)&&(copy!=nullptr)&&(start <= mid)&&(mid < end))
	{
		int i = mid;
		int j = end;
		int index = end;

		while ((i >= start)&&(j >= mid + 1)&&(i * j >= 0))
		{
			//比较取最大者,放在辅助数组的末位
			if (data[i] > data[j])
			{
				copy[index--] = data[i--];
				count += (j - mid);//逆序对统计
			}
			else
			{
				copy[index--] = data[j--];
				//此时表示后者大于前者,不满足逆序对的定义,故count不进行操作
			}
		}
		//处理剩余数组(注:此情况count也不再会发生改变,因为已经统计完全)
		while ((i >= start) && (i >= 0))
		{
			copy[index--] = data[i--];
		}

		while ((j >= mid + 1) && (j >= 0))
		{
			copy[index--] = data[j--];
		}
		printf("原始数据:");
		for (size_t k = start; k <= end; k++)
		{
			printf("%d ", data[k]);
		}
		printf("\n当前数据:");
		for (size_t k = start; k <= end; k++)
		{
			data[k] = copy[k];
			printf("%d ", data[k]);
		}
		printf("\n");
		printf("逆序对数:%d\n", count);
	}
	return count;
}

int Reverse_Pairs(int data[], int copy[], size_t start, size_t end)
{
	if ((data == nullptr) || (copy == nullptr))
	{
		return 0;
	}
	if (start >= end)
	{
		return 0;
	}
	//分治策略
	int mid = (start + end) / 2;
	int left = Reverse_Pairs(data, copy, start, mid);
	int right = Reverse_Pairs(data, copy, mid + 1, end);
	int count = get_InversePairs(data, copy, start, mid, end);
	return left + right + count;

}

int _tmain(int argc, _TCHAR* argv[])
{
	int data[] = {7,5,6,4};
	const size_t LENGTH = 4;
	int copy[LENGTH];
	printf("%d\n", Reverse_Pairs(data, copy, 0, LENGTH-1));
	return 0;
}
时间: 2024-11-19 02:33:10

归并排序及统计数组逆序对的相关文章

求数组逆序对

思路:类似归并排序算法,在合并已经有序的相邻子数组的时候,计算前面数组相对于后面数组的逆序对数,整个递归过程可以算出所有逆序对#include <stdio.h> void merge(int A[], int front, int middle, int end, int &count) { if (front >= end) return; int i = front; int j = middle + 1; int k = 0; int *p = new int[end -

hdu 5193 分块 树状数组 逆序对

题意: 给出n个数,a1,a2,a3,...,an,给出m个修改,每个修改往数组的某个位置后面插入一个数,或者把某个位置上的数移除.求每次修改后逆序对的个数. 限制: 1 <= n,m <= 20000; 1 <= ai <= n 思路: 插入和删除用分块来处理,块与块之间用双向链表来维护,每一块用树状数组来求小于某个数的数有多少个. 外层可以使用分块维护下标,这样添加和删除元素的时候,也很方便,直接暴力.查找权值个数时,使用树状数组比较方便.内层通过树状数组维护权值. 每次更新即

Poj 2299 - Ultra-QuickSort 离散化,树状数组,逆序对

Ultra-QuickSort Time Limit: 7000MS   Memory Limit: 65536K Total Submissions: 52306   Accepted: 19194 Description In this problem, you have to analyze a particular sorting algorithm. The algorithm processes a sequence of n distinct integers by swappin

Bzoj 2789: [Poi2012]Letters 树状数组,逆序对

2789: [Poi2012]Letters Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 278  Solved: 185[Submit][Status][Discuss] Description 给出两个长度相同且由大写英文字母组成的字符串A.B,保证A和B中每种字母出现的次数相同. 现在每次可以交换A中相邻两个字符,求最少需要交换多少次可以使得A变成B. Input 第一行一个正整数n (2<=n<=1,000,000),表示字符串的长度

【树状数组逆序对】USACO.2011JAN-Above the median

[题意] 给出一串数字,问中位数大于等于X的连续子串有几个.(这里如果有偶数个数,定义为偏大的那一个而非中间取平均) [思路] 下面的数据规模也小于原题,所以要改成__int64才行.没找到测试数据,自己编的几组.简单来说读入每个数,大于等于中位数设为1,小于设为-1,前i个数的和建立一个树状数组,求逆序对. 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorit

BZOJ4989 [Usaco2017 Feb]Why Did the Cow Cross the Road 树状数组 逆序对

欢迎访问~原文出处--博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ4989 题意概括 一条马路的两边分别对应的序列A.B,长度为n,两序列为1到n的全排列.当Ai=Bj时,两边之间会连一条边.你可以选择序列A或序列B进行旋转(只能使队尾或队头位置上的数字变成队头或队尾上的数字)任意K(0<=K<n)步,如123,可以变成 231 或 312.求旋转后,最少的边的交叉数. 题解 两个都可以转,那么我们只需要分别转动两个并统计即可. 旋转一个,那么我们只需要统计逆序

Codevs 3286 火柴排队 2013年NOIP全国联赛提高组 树状数组,逆序对

题目:http://codevs.cn/problem/3286/ 3286 火柴排队  2013年NOIP全国联赛提高组 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 题解 题目描述 Description 涵涵有两盒火柴,每盒装有 n 根火柴,每根火柴都有一个高度.现在将每盒中的火柴各自排成一列,同一列火柴的高度互不相同,两列火柴之间的距离定义为:,其中 ai表示第一列火柴中第 i 个火柴的高度,bi表示第二列火柴中第 i 个火柴的高度.每列火柴中相

Ultra-QuickSort——[归并排序、分治求逆序对]

Description In this problem, you have to analyze a particular sorting algorithm. The algorithm processes a sequence of n distinct integers by swapping two adjacent sequence elements until the sequence is sorted in ascending order. For the input seque

总结之---树状数组+逆序对问题。

咳咳,这个图必须要的.... 首先,当有一个数组a数量非常大的时候,我们可能改变某个a[i]的值,要求a[n]的和,全部加起来,无疑是要O(n)的时间复杂度. 但是如果n非常大时,O(n)时间复杂度肯定要跪,所以,怎么办的,用神奇的树状数组. 树状数组代码简单,但是非常强大!更令人兴奋的是,它的时间复杂度值需要O(logn)!!! 好了,首先要的东西是把上图的c[n]表示出来,该怎么弄呢,代码如下: int lowbit(int t) { return t&(-t); } 这个代码,简单到爆,但