算法学习02

一、归并排序

  1. 整体就是一个简单递归,左边排好序、右边排好序、让其整体有序
  2. 让其整体有序的过程里用了排外序方法
  3. 利用master公式来求解时间复杂度
  4. 归并排序的实质

时间复杂度O(N*logN),额外空间复杂度O(N)
选择、冒泡、插入 的时间复杂度高O(N^2)因为浪费了许多比较行为

递归,L到R范围排好序,求出中点位置,先让左侧有序,再让右侧有序,最后合并两侧
合并思想:123 256
准备一个辅助空间,谁小拷贝谁进辅助空间,辅助空间有序之后再拷贝会原数组达到原数组有序,相等时保证右组先拷贝

左侧有一个下标指向1,右侧有一个下标指向2,如果左侧小于等于右侧先拷贝左侧到合并的数组中,如果不小于等于右侧就拷贝右侧的数组中。
在这里插入图片描述

	public static void mergeSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		mergeSort(arr, 0, arr.length - 1);
	}
	public static void process(int[] arr, int L,int R){
		if(L == R){
			return;
		}
		int mid = L + ((R - L) >> 1);
		process(arr, L,mid);
		process(arr, mid + 1,R);
		merge(arr, L, mid,R);
	}

	public static 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);
	}

	public static void merge(int[] arr, int l, int m, int r) {
		int[] help = new int[r - l + 1];
		int i = 0;
		int p1 = l;
		int p2 = m + 1;
		while (p1 <= m && p2 <= r) {
			help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
		}
		while (p1 <= m) {
			help[i++] = arr[p1++];
		}
		while (p2 <= r) {
			help[i++] = arr[p2++];
		}
		for (i = 0; i < help.length; i++) {
			arr[l + i] = help[i];
		}
	}

归并排序的扩展

1.小和问题

在一个数组中,每一个数左边比当前数小的数累加起来,叫做这个数组的小和。求一个数组的小和。

更换思路,求一个数右边有多少个数比它大。

利用归并排序时生成左右两个有序数组,当右侧数比左侧数大,使用下标差计算有多少个数比左侧数大,记录下来
当左侧数比右侧大时,记录下来,当左右相等时,要先拷贝右组的数并且不产生小和,当数组合并完成时可以得出小和。

	public static int smallSum(int[] arr) {
		if (arr == null || arr.length < 2) {
			return 0;
		}
		return mergeSort(arr, 0, arr.length - 1);
	}

	public static int mergeSort(int[] arr, int l, int r) {
		if (l == r) {
			return 0;
		}
		int mid = l + ((r - l) >> 1);
		return mergeSort(arr, l, mid) 
				+ mergeSort(arr, mid + 1, r) 
				+ merge(arr, l, mid, r);
	}

	public static int merge(int[] arr, int l, int m, int r) {
		int[] help = new int[r - l + 1];
		int i = 0;
		int p1 = l;
		int p2 = m + 1;
		int res = 0;
		while (p1 <= m && p2 <= r) {
			res += arr[p1] < arr[p2] ? (r - p2 + 1) * arr[p1] : 0;
			help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
		}
		while (p1 <= m) {
			help[i++] = arr[p1++];
		}
		while (p2 <= r) {
			help[i++] = arr[p2++];
		}
		for (i = 0; i < help.length; i++) {
			arr[l + i] = help[i];
		}
		return res;
	}

2.逆序对问题

在一个数组中,左边的数如果比右边的数大,则这两个数构成一个逆序对,请打印所有逆序对。

等同于小和问题,都是由mergeSort改写的题

荷兰国旗问题

问题一

给定一个数组arr,和一个数num,请把小于等于num的数放在数 组的左边,大于num的 数放在数组的右边。要求额外空间复杂度O(1),时间复杂度O(N)

选定小于等于区,如果指针位置值小于等于num值,将指针位置值与小于等于区下一个值交换,小于等于区向前移动,i++;
在这里插入图片描述

问题二(荷兰国旗问题)

给定一个数组arr,和一个数num,请把小于num的数放在数组的 左边,等于num的数放 在数组的中间,大于num的数放在数组的 右边。要求额外空间复杂度O(1),时间复杂度O(N)

	public static int[] partition(int[] arr, int l, int r, int p) {
		int less = l - 1;
		int more = r + 1;
		while (l < more) {
			if (arr[l] < p) {
				swap(arr, ++less, l++);
			} else if (arr[l] > p) {
				swap(arr, --more, l);
			} else {
				l++;
			}
		}
		return new int[] { less + 1, more - 1 };
	}

在这里插入图片描述
要么让 i 往左走压缩待定区域,让小于区域推着等于区域奔向大于区域
要么让 i 发货到大于区域,让大于区域往左扩,来压缩待定区域
在这里插入图片描述

二、快速排序

快排1.0

选择数组最后一个数字作为num,让数组中最后一个数字之前做到小于等于 num 的数放在左边,大于 num 的数放在右边;让num和大于区域的第一个数做交换,使小于等于 num 区域扩充,右侧全是大于等于 num,这样 num 就排好了,让左侧和右侧递归的进行这个行为
在这里插入图片描述

快排2.0

每次搞定中间一批数,将等于 num 的都放到中间,一次确定所有等于 num 的数
在这里插入图片描述
在数组有序时1.0版本和2.0版本快速排序最差情况的时间复杂度退化到 O(N^2)
出现这种情况的原因是划分池打的很偏

快排3.0

从数组中随机选择一个数,将它放到最后一个数没然后将它当成 num 来划分,小于放左边,等于放中间,大于放右边。

	public static void quickSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		quickSort(arr, 0, arr.length - 1);
	}

	//arr[l..r]排好序
	public static void quickSort(int[] arr, int l, int r) {
		if (l < r) {
			swap(arr, l + (int) (Math.random() * (r - l + 1)), r);
			int[] p = partition(arr, l, r);
			quickSort(arr, l, p[0] - 1);// < 区
			quickSort(arr, p[1] + 1, r);// > 区
		}
	}

	//这是一个处理arr[l..r]的函数
	//默认以arr[r]做划分,arr[r] -> p    <p     ==p    >p
	//返回等于区域(左边界,右边界),所以返回一个长度为2的数组res,res[0] res[1]
	public static int[] partition(int[] arr, int l, int r) {
		int less = l - 1;// <区右边界
		int more = r;// >区左边界
		while (l < more) {// L 表示当前数的位置 arr[R] -> 划分值
			if (arr[l] < arr[r]) {// 当前数 < 划分值
				swap(arr, ++less, l++);
			} else if (arr[l] > arr[r]) {// 当前数 > 划分值
				swap(arr, --more, l);
			} else {
				l++;
			}
		}
		swap(arr, more, r);
		return new int[] { less + 1, more };
	}

	public static void swap(int[] arr, int i, int j) {
		int tmp = arr[i];
		arr[i] = arr[j];
		arr[j] = tmp;
	}

快速排序额外空间复杂度O(logN),最差时间复杂度O(N)

总结

归并排序,归并排序的扩展,通过荷兰国旗问题引出快速排序,优化快速排序,基于比较的排序。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值