归并排序专题

文章介绍了归并排序的两种实现方法(递归和非递归),以及如何利用归并分治的思想解决数组中计算小数和的问题。通过实例展示了如何在保持数组有序的同时,计算跨越数组界限的小数和。
摘要由CSDN通过智能技术生成

归并排序的代码:时间复杂度O(N*logN);

1.递归写法

#include<bits/stdc++.h>
using namespace std;
int help[6];
void merge(int *arr, int l, int m, int r) {
	int a = l;
	int b = m + 1;
	int i = l;
	while (a <= m && b <= r) {
		help[i++] = arr[a] <= arr[b] ? arr[a++] : arr[b++];
	}
	while (a <= m)
		help[i++] = arr[a++];
	while (b <= r)
		help[i++] = arr[b++];
	while (l <= r){
        arr[l]=help[l];
        l++;
    }

}
void mergesort(int* arr, int l, int r) {
	if (l == r) return ;
	int mid = l + ((r - l) >> 1);

	mergesort(arr, l, mid);
	mergesort(arr, mid + 1, r);
	merge(arr, l, mid, r);

}
int main() {
	int arr[5] = {1, 2, 3, 4, 5};
	mergesort(arr, 0, 4);
	for (int i = 0; i < 5; i++)
		cout << arr[i] << " ";
	return 0;
} 

2.非递归写法

#include<bits/stdc++.h>
using namespace std;
int help[6];
void merge(int *arr, int l, int m, int r) {
	int a = l;
	int b = m + 1;
	int i = l;
	while (a <= m && b <= r) {
		help[i++] = arr[a] <= arr[b] ? arr[a++] : arr[b++];
	}
	while (a <= m)
		help[i++] = arr[a++];
	while (b <= r)
		help[i++] = arr[b++];
	for(int i=l;i<=r;i++)
        arr[i]=help[i];

}
int main() {
	int arr[5] = {1, 2, 3, 4, 5};
	int step = 1;
	int l = 0, r = 0, m = 0;
	for (l, r, step; step < 5; step <<= 1) {
		l = 0;
		while (l < 5) {
			m = l + step - 1;
			if (m + 1 >= 5) break;
			r = min(5 - 1, l + 2 * step - 1);
			merge(arr, l, m, r);
			l = r + 1;

		}

	}

	for (int i = 0; i < 5; i++)
		cout << arr[i] << " ";
	return 0;
}

具体练习可在数组排序

 归并排序和分治的综合应用:

视频连接归并分治

 我们用小数和 来举例说明:

描述

数组小和的定义如下:

∑i=1n​fi​ ​ (其中 fi​  的定义是第 i 个数的左侧小于等于 si​  的个数)。例如,数组 s = [1, 3, 5, 2, 4, 6] ,在 s[0] 的左边小于或等于 s[0] 的数的和为 0 ; 在 s[1] 的左边小于或等于 s[1] 的数的和为 1 ;在 s[2] 的左边小于或等于 s[2] 的数的和为 1+3=4 ;在 s[3] 的左边小于或等于 s[3] 的数的和为 1 ;

在 s[4] 的左边小于或等于 s[4] 的数的和为 1+3+2=6 ;在 s[5] 的左边小于或等于 s[5] 的数的和为 1+3+5+2+4=15 。所以 s 的小和为 0+1+4+1+6+15=27

给定一个数组 s ,实现函数返回 s 的小和

输入描述:

第一行有一个整数N。表示数组长度
接下来一行N个整数表示数组内的数

输出描述:

一个整数表示答案

要求一个数组的小数的和,可以将数组划分为左右两部分,分别求左数组的小数和和右数组的小数

和,显然这并不是答案,因为左边数组的元素可能是右边数组元素的小数,所以还得加上左边数组

跨越右数组的小数和。当我们在求跨左右数组的小数和时,如果左右数组的元素分别有序,当我们

求右边数组第一个元素在左边数组的小数和时,可以安排一个指针遍历左数组来求;那么第二个元

素在左数组的小数和可以在第一个元素的基础上继续遍历左数组累加,所以只需遍历一遍左右数组

就可以求出跨左右数组的小数和,更便于我们计算,那么,这就符合归并分治的思想。


#include <iostream>
using namespace std;
int help[6];

int merge(int l, int mid, int r,int* nums) {
	int sum = 0, ans = 0;
	int a = l;
	for (int i = mid + 1; i <= r; i++) {
		while (nums[i] >= nums[a]&&a<=mid) {//找跨左右小数的和
			sum += nums[a];
			a++;
		}
		ans += sum;
	}
	int i = l,j=mid+1;
	int pos = l;
	while (i <= mid && j <= r) {//归并过程 
		if (nums[i] >= nums[j])
			help[pos++] = nums[j++];
		else help[pos++] = nums[i++];
	}
	while (i <= mid)
		help[pos++] = nums[i++];
	while (j <= r)
		help[pos++] = nums[j++];
	 for(int i=l;i<=r;i++){
        nums[i]=help[i];
 }
	return ans;
	
}
 int min_num(int l, int r,int* nums) {
	if (l == r) return 0;
	int mid = l + ((r - l) >> 1);
	return  min_num(l, mid,nums) + min_num(mid + 1, r,nums ) + merge(l,mid,r,nums);


}
int main() {
	int nums[5] = {1,2,3,4,5};
	int result= min_num(0, 4,nums);
	cout << result;
	return 0;
}

类似的题目:翻转对

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值