数据结构和算法(四)--高级排序

news2024/11/24 7:33:38

目录

二、高级排序

2.1、希尔排序

2.2、归并排序

2.2.1、递归

2.2.2、归并排序


数据结构和算法(三)--排序

二、高级排序

冒泡排序,选择排序,插入排序,最坏的时间复杂度都是O(N^2),而平方阶,随着输入规模的增大,时间成本急剧上升,所以这些基本排序方法不能处理大规模的问题,接下来来学习一些高级的排序算法,争取降低算法的时间复杂度最高阶次幂。

2.1、希尔排序

希尔排序是插入排序的一种,又称"缩小增量排序",是插入排序算法的一种更高效的改进版本。

插入排序的时候,有一个很不好的事,如果已排序的分组元素为{2,5,7,9,10},未排序的分组元素为{1,8},那么下一个待插入元素为1,我们需要拿着1从后往前,依次和10,9,7,5,2进行交换位置,才能完成真正的插入,每次交换只能和相邻的元素交换位置。那么如果我们要提高效率,直观的想法就是一次交换,能把1放到更前面的位置,比如一次交换就能把1插到2和5之间,这样一次交换1就向前走了5个位置,可以减少交换次数,这样的需求如何实现呢?接下来我们来看看希尔排序的原理。

需求:

        排序前:{9,1,2,5,7,4,8,6,3,5}

        排序后:{1,2,3,4,5,5,6,7,8,9}

排序原理:
1.选定一个增长量h,按照增长量h作为数据分组的依据,对数据进行分组;
2.对分好组的每一组数据完成插入排序;
3.减小增长量,最小减为1,重复第二步操作。

 增长量h的确定:增长量h的值没有一个固定的规则,我们这里采用以下规则:

int h=1;
while(h<数组的长度/2){
    h=2h+1;
}
//循环结束后我们就可以确定h的最大值;
//h的减小规则为:h=h/2;

 希尔排序API设计:

类名Shell
构造方法Shell():创建Shell对象
成员方法

1.public static void sort(Comparable[] a):对数组内的元素进行排序

2.private static boolean greater(Comparable v, Comparable w):判断v是否大于w

3.private static void exch(Comparable[] a,int i,int j):交换a数组中,索引i和索引j处的值

代码实现:

public class Shell {

	/**
	 * a.对数组a中的元素进行排序
	 */
	@SuppressWarnings("rawtypes")
	public static void sort(Comparable[] a) {

		// 1.根据数组a的长度,确定增长量h的初始值
		int h = 1;
		while (h < a.length / 2) {
			h = 2 * h + 1;
		}

		// 2.希尔排序
		while (h >= 1) {
			// 2.1找到待插入的元素
			for (int i = h; i < a.length; i++) {
				// 2.2把待插入的元素插入到有序数列中
				for (int j = i; j >= h; j -= h) {
					// 待插入的元素是a[j],a[j-h]
					if (greater(a[j - h], a[j])) {
						exch(a, j - h, j);
					} else {
						// 待插入元素已经找到合适的位置,结束循环
						break;
					}
				}
			}

			// 减小h的值
			h = h / 2;
		}
	}

	/**
	 * a.比较v元素是否大于w元素
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	private static boolean greater(Comparable v, Comparable w) {
		return v.compareTo(w) > 0;
	}

	/**
	 * a.数组元素i和j交换位置
	 */
	@SuppressWarnings("rawtypes")
	private static void exch(Comparable[] a, int i, int j) {
		Comparable temp;
		temp = a[i];
		a[i] = a[j];
		a[j] = temp;
	}

}

测试类:

import java.util.Arrays;

public class ShellTest {

	public static void main(String[] args) {
		Integer[] arr = { 9, 1, 2, 5, 7, 4, 8, 6, 3, 5 };
		Shell.sort(arr);
		System.out.println(Arrays.toString(arr));
	}

}

希尔排序的时间复杂度分析

在希尔排序中,增长量h并没有固定的规则,有很多论文研究了各种不同的递增序列,但都无法证明某个序列是最好的。

我们可以使用事后分析法希尔排序和插入排序做性能比较。

自己做一个数据文件,里面存放的是从100000到1的逆向数据,我们可以根据这个批量数据进行测试。我们可以根据这个批量数据完成测试。测试的思想:在执行排序前记录一个时间,在排序完成后记录一个时间,两个时间差就是排序时间。

希尔排序和插入排序性能比较测试代码:

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

public class SortCompare {

	public static void main(String[] args) throws Exception {
		List<Integer> list = new ArrayList<>();
		BufferedReader reader = new BufferedReader(
				new InputStreamReader(SortCompare.class.getClassLoader().getResourceAsStream("reverse_arr.txt")));
		String line = null;
		while ((line = reader.readLine()) != null) {
			int i = Integer.parseInt(line);
			list.add(i);
		}
		reader.close();
		// 把list集合转换成数组
		Integer[] arr = new Integer[list.size()];
		list.toArray(arr);

		shellTest(arr);
		// InsertionTest(arr);
	}

	public static void shellTest(Integer[] arr) {
		long start = System.currentTimeMillis();
		Shell.sort(arr);
		long end = System.currentTimeMillis();
		System.out.println("希尔排序执行时间:" + (end - start) + "毫秒");// 希尔排序执行时间:30毫秒
	}

	public static void InsertionTest(Integer[] arr) {
		long start = System.currentTimeMillis();
		Insertion.sort(arr);
		long end = System.currentTimeMillis();
		System.out.println("插入排序执行时间:" + (end - start) + "毫秒");// 插入排序执行时间:21559毫秒
	}

}

通过测试发现,在处理大批量数据时,希尔排序的性能确实高于插入排序。

2.2、归并排序

2.2.1、递归

定义:定义方法时,在方法内部调用方法本身,称之为递归

	public void show() {
		show();
	}

作用:它通常把一个大型复杂的问题,层层转换为一个与原问题相似的,规模较小的问题来求解。递归策略只需要少量的程序就可以描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。

注意事项:在递归中,不能无限制的调用自己,必须要有边界条件,能够让递归结束,因为每一次递归调用都会在栈内存开辟新的空间,重新执行方法,如果递归的层次太深,很容易造成栈内存溢出。

2.2.2、归并排序

归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并

需求:

        排序前:{8,4,5,7,1,3,6,2}

        排序后:{1,2,3,4,5,6,7,8}

排序原理:
1.尽可能的一组数据拆分成两个元素相等的子组,并对每一个子组继续拆分,
直到拆分后的每个子组的元素个数是1为止。
2.将相邻的两个子组进行合并成一个有序的大组。
3.不断的重复步骤2,直到最终只有一个组为止。

归并排序API设计:

类名Merge
构造方法Merge():创建Merge对象
成员方法

1.public static void sort(Comparable[] a):对数组内的元素进行排序

2.public static void sort(Comparable[] a,int lo,int hi):对数组a中从索引lo到索引hi之间的元素进行排序

3.private static void merge(Comparable[] a,int lo,int mid,int hi):从索引lo到索引mid为一个子组,从索引mid+1到索引hi为另一个子组,把数组a中的这两个子组的数据合并成一个有序的大组(从索引lo到索引hi)

4.private static boolean less(Comparable v, Comparable w):判断v是否小于w

5.private static void exch(Comparable[] a,int i,int j):交换a数组中,索引i和索引j处的值

成员变量1.private static Comparable[] assist:完成归并操作需要的辅助数组

 

 

 代码实现:

public class Merge {

	// 辅助数组
	@SuppressWarnings("rawtypes")
	private static Comparable[] assist;

	/**
	 * a.对数组a中的元素进行排序
	 */
	@SuppressWarnings("rawtypes")
	public static void sort(Comparable[] a) {
		// 1.初始化辅助数组
		assist = new Comparable[a.length];
		// 2.定义lo变量和hi变量记录数组中最小的索引和最大的索引
		int lo = 0;
		int hi = a.length - 1;
		// 3.调用重载方法完成从索引lo到索引hi的元素的排序
		sort(a, lo, hi);
	}

	/**
	 * a.对数组a中从索引lo到索引hi之间的元素进行排序
	 */
	@SuppressWarnings("rawtypes")
	public static void sort(Comparable[] a, int lo, int hi) {

		if (hi <= lo) {
			return;
		}
		// 对lo到hi之间的数据进行分为两个组
		int mid = lo + (hi - lo) / 2;
		// 分别为每一组数据进行排序
		sort(a, lo, mid);
		sort(a, mid + 1, hi);

		// 再把两个组的数据进行归并
		merge(a, lo, mid, hi);
	}

	/**
	 * a.合并
	 */
	@SuppressWarnings("rawtypes")
	public static void merge(Comparable[] a, int lo, int mid, int hi) {
		// 定义三个指针
		int i = lo;
		int p1 = lo;
		int p2 = mid + 1;

		// 遍历+移动p1指针和p2指针,比较对应索引处的值,找出小的那个放到辅助数组的对应索引处
		while (p1 <= mid && p2 <= hi) {
			if (less(a[p1], a[p2])) {
				assist[i++] = a[p1++];
			} else {
				assist[i++] = a[p2++];
			}
		}

		// 遍历+如果p1的指针没有走完,那么顺序移动p1指针,把对应的元素放到辅助数组的对应索引处
		while (p1 <= mid) {
			assist[i++] = a[p1++];
		}
		// 遍历+如果p2的指针没有走完,那么顺序移动p2指针,把对应的元素放到辅助数组的对应索引处
		while (p2 <= hi) {
			assist[i++] = a[p2++];
		}
		// 把辅助数组中的元素拷贝到原数组中
		for (int j = lo; j <= hi; j++) {
			a[j] = assist[j];
		}

	}

	/**
	 * a.比较v元素是否小于w元素
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	private static boolean less(Comparable v, Comparable w) {
		return v.compareTo(w) < 0;
	}

	/**
	 * a.数组元素i和j交换位置
	 */
	@SuppressWarnings({ "rawtypes", "unused" })
	private static void exch(Comparable[] a, int i, int j) {
		Comparable temp;
		temp = a[i];
		a[i] = a[j];
		a[j] = temp;
	}

}

测试类:

import java.util.Arrays;

public class MergeTest {

	public static void main(String[] args) {
		Integer[] arr = { 8, 4, 5, 7, 1, 3, 6, 2 };
		Merge.sort(arr);
		System.out.println(Arrays.toString(arr));
	}

}

归并排序时间复杂度分析

归并排序是分治思想的最典型的例子

用树状图来描述归并,如果一个数组有8个元素,那么它将每次除以2找最小的子数组,共拆log8次,值为3,所以树共有3层,那么自顶向下第k层有2^k个子数组,每个数组的长度为2^(3-k)次比较,归并最多需要2^(3-k)次比较。因此每层的比较次数为2^k*2^(3-k)=2^3,那么3层总共为3*2^3.

假设元素的个数为n,那么使用归并排序拆分的次数为log2(n),所以共log2(n)层,那么使用log2(n)替换上面3*2^3中的3这个层数,最终得出的归并排序的时间复杂度为:log2(n)*2^(log2(n))=log2(n)*n,根据大O推到法则,忽略底数,最终归并排序的时间复杂度为O(nlogn)。

归并排序的缺点:

需要申请额外的数组空间,导致空间复杂度提升,是典型的以空间换时间的。

归并排序与希尔排序性能测试:

采用事后分析法进行性能测试。

自己做一个数据文件,里面存放的是从100000到1的逆向数据,我们可以根据这个批量数据进行测试。我们可以根据这个批量数据完成测试。测试的思想:在执行排序前记录一个时间,在排序完成后记录一个时间,两个时间差就是排序时间。

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

public class SortCompare {

	public static void main(String[] args) throws Exception {
		List<Integer> list = new ArrayList<>();
		BufferedReader reader = new BufferedReader(
				new InputStreamReader(SortCompare.class.getClassLoader().getResourceAsStream("reverse_arr.txt")));
		String line = null;
		while ((line = reader.readLine()) != null) {
			int i = Integer.parseInt(line);
			list.add(i);
		}
		reader.close();
		// 把list集合转换成数组
		Integer[] arr = new Integer[list.size()];
		list.toArray(arr);

		// shellTest(arr);
		// InsertionTest(arr);
		MergeTest(arr);
	}

	public static void shellTest(Integer[] arr) {
		long start = System.currentTimeMillis();
		Shell.sort(arr);
		long end = System.currentTimeMillis();
		System.out.println("希尔排序执行时间:" + (end - start) + "毫秒");// 希尔排序执行时间:30毫秒
	}

	public static void InsertionTest(Integer[] arr) {
		long start = System.currentTimeMillis();
		Insertion.sort(arr);
		long end = System.currentTimeMillis();
		System.out.println("插入排序执行时间:" + (end - start) + "毫秒");// 插入排序执行时间:21559毫秒
	}

	public static void MergeTest(Integer[] arr) {
		long start = System.currentTimeMillis();
		Merge.sort(arr);
		long end = System.currentTimeMillis();
		System.out.println("归并排序执行时间:" + (end - start) + "毫秒");// 归并排序执行时间:60毫秒
	}

}

干我们这行,啥时候懈怠,就意味着长进的停止,长进的停止就意味着被淘汰,只能往前冲,直到凤凰涅槃的一天!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/99651.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

想掌握日志分析系统的看过来

日志分析系统&#xff08;ELK&#xff09; 什么是ELK ELK是三个开源软件的缩写&#xff0c;分别表示&#xff1a;Elasticsearch , Logstash, Kibana , 它们都是开源软件。新增了一个FileBeat&#xff0c;它是一个轻量级的日志收集处理工具(Agent)&#xff0c;Filebeat占用资源…

day23【代码随想录】翻转二叉树、对称二叉树、完全二叉树的结点个数

文章目录前言一、翻转二叉树&#xff08;力扣226&#xff09;1、递归法1、使用前序遍历2、使用后序遍历2、迭代法1、层序遍历二、对称二叉树&#xff08;力扣101&#xff09;三、完全二叉树的结点个数&#xff08;力扣222&#xff09;前言 1、翻转二叉树 2、对称二叉树 3、完全…

【图像处理】直方图均衡化

直方图均衡化 简介 直方图均衡化(Histogram Equalization)是一种增强图像对比度(Image Contrast)的方法,其主要思想是将一副图像的直方图分布变成近似均匀分布,从而增强图像的对比度。 直方图均衡化是将原图像通过某种变换,得到一幅灰度直方图为均匀分布的新图像的方法。…

RegNet:设计网络设计空间

文章目录摘要1. 介绍2. 相关工作3.设计空间设计3.1. 设计空间设计的工具3.2. AnyNet设计空间3.3. RegNet设计空间3.4. 设计空间概化4. 分析RegNetX设计空间5. 与现有网络的比较5.1. 最先进的比较:移动体系5.2. 标准基线比较:ResNe(X)t5.3. 最先进的比较:整个体系6. 结论附录A:测…

Kafka 核心源码解读【四】--状态机模块

文章目录1 TopicDeletionManager&#xff1a; Topic是怎么被删除的&#xff1f;1.1 课前导读1.2 TopicDeletionManager 概览1.3 DeletionClient 接口及其实现1.4 TopicDeletionManager 定义及初始化1.5 TopicDeletionManager 重要方法1.6 总结2 ReplicaStateMachine&#xff1a…

EEGLAB系列教程5:数据预处理2(ICA去伪迹)

EEGLAB系列教程5&#xff1a;数据预处理2&#xff08;ICA去伪迹&#xff09; 今天介绍EEG数据处理系列教程5&#xff0c;在前面的几期中&#xff0c;已经介绍了数据基本处理过程&#xff0c;可以参见以下链接: EEGLAB系列教程1&#xff1a;安装和启动 EEGLAB EEGLAB系列教程…

Java项目:SSM网上零食超市商城

作者主页&#xff1a;源码空间站2022 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文末获取源码 项目介绍 本项目分为前后台&#xff0c;前台普通用户登录&#xff0c;后台管理员登录&#xff1b; 用户角色包含以下功能&#xff1a; 提交订单,支付页面…

Axios异步通信

四.Axios异步通信 1 什么是Axios&#xff1f; Axios是一个类库&#xff0c;基于Promise管理的HTTP 库&#xff0c;是前端通信框架&#xff0c;可以用在浏览器和 node.js 中。axios实现了对ajax的封装&#xff0c;常用于Ajax请求。注解&#xff1a;promise是Java Script的一个…

(附源码)Springboot校园健康检测管理系统 毕业设计 151047

Springboot校园健康检测管理系统的设计 摘 要 亚健康低龄化和高职业压力使高校师生健康问题凸显&#xff0c;不及早进行干预和控制将严重影响师生健康。如何理解和应用健康管理的理论和方法&#xff0c;探索高校师生健康管理工作模式&#xff0c;是目前高校医院的重点工作 校园…

Thymeleaf 预处理表达式__${表达式}__使用案例

目录一. 前期准备1.1 国际化项目获取类1.2 国际化配置文件类1.3 项目配置文件1.4 国际化资源文件二. __${表达式}__预处理表达式2.1 在Thymeleaf中使用Spring的Bean2.2 通过#{}获取国际化资源2.3 预处理表达式__${表达式}__的使用三. 效果四. 表格案例一. 前期准备 1.1 国际化…

图神经网络 # GNN基础(干货版)

注意&#xff1a;本篇文章不是系统性的图神经网络或者图深度学习的入门教程&#xff0c;而是介绍如何入门图神经网络的学习指南以及相关的干货笔记&#xff0c;文中会列出一些视频和文章学习资料&#xff0c;并在本文作一些重点的摘要&#xff0c;读者需要自行前往文本超链接的…

【C语言】通讯录《信息写到文件版本》

&#x1f6a9;write in front&#x1f6a9; &#x1f50e;大家好&#xff0c;我是謓泽&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流&#x1f50e; &#x1f3c5;2021年度博客之星物联网与嵌入式开发TOP5&#xff5…

非零基础自学Golang 第9章 结构体 9.5 结构体方法

非零基础自学Golang 文章目录非零基础自学Golang第9章 结构体9.5 结构体方法9.5.1 指针类型接收者9.5.2 值类型接收者第9章 结构体 9.5 结构体方法 Go语言中&#xff0c;一个方法就是一个包含了接收者的函数。 对于结构体方法&#xff0c;接收者可以是结构体类型的值或是指针…

【图像融合】对比度增强结合多尺度边缘保持分解红外与可见光图像融合【含Matlab源码 1886期】

⛄一、对比度增强结合多尺度边缘保持分解红外与可见光图像融合简介 1 引言 夜视成像技术在军事与执法领域&#xff0c;如监控&#xff0c;侦查&#xff0c;情报搜集和安保等方面有着重要应用。不同类型的夜视成像传感器在记录同一场景时&#xff0c;在其提供的信息中有部分是冗…

目标检测论文解读复现【NO.21】基于改进YOLOv7的小目标检测

前言 此前出了目标改进算法专栏&#xff0c;但是对于应用于什么场景&#xff0c;需要什么改进方法对应与自己的应用场景有效果&#xff0c;并且多少改进点能发什么水平的文章&#xff0c;为解决大家的困惑&#xff0c;此系列文章旨在给大家解读最新目标检测算法论文&#xff0…

Java项目:基于SSM的高校运动会管理系统的设计与实现

作者主页&#xff1a;源码空间站2022 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文末获取源码 项目介绍 该高校运动会信息管理系统是以B/S架构为设计基础并基于SSM框架开发的系统。系统采用了Java语言和MySQL数据库来实现。 系统按预定的算法完成了创…

数据结构---删去k个数字后的最小值

删去k个数字后的最小值思路贪心算法JAVA实现1JAVA实现2给出一个整数&#xff0c;从该整数中去掉k个数字&#xff0c;要求剩下的数字形成的新整数尽可能小。应该如何选取被去掉的数字&#xff1f; 其中整数的长度大于或等于k&#xff0c;给出的整数的大小可以超过long类型的数字…

计算机毕业设计 基于HTML+CSS+JavaScript 大气的甜品奶茶美食餐饮文化网页设计与实现23页面

&#x1f468;‍&#x1f393;静态网站的编写主要是用HTML DIVCSS JS等来完成页面的排版设计&#x1f469;‍&#x1f393;,常用的网页设计软件有Dreamweaver、EditPlus、HBuilderX、VScode 、Webstorm、Animate等等&#xff0c;用的最多的还是DW&#xff0c;当然不同软件写出的…

Java+SSM房屋租赁系统房屋出租房产(含源码+论文+答辩PPT等)

项目功能简介: 该项目采用的技术实现如下 后台框架&#xff1a;Spring、SpringMVC、MyBatis UI界面&#xff1a;jQuery 、JSP 数据库&#xff1a;MySQL 系统功能 系统分为前台用户界面和后台系统管理&#xff1a; 1.前台用户界面 用户注册、用户登录、用户中心、浏览房源、房源…

代码随想录训练营第54天|休息日 小结

打家劫舍系列 198. 打家劫舍对于当前的房间&#xff0c;无非就两种选择&#xff1a;偷与不偷。如果当前房间偷&#xff0c;那么前一个房间就不偷&#xff0c;即dp[i] dp[i-2] nums[i]&#xff1b;如果当前房间不偷&#xff0c;那么dp[i] dp[i-1]&#xff0c;因此递推公式为…