【算法】几种常用排序算法

1000道互联网大厂面试题:ZooKeeper+Dubbo,linux基础教程第二版课后答案 1、MySQL 中有哪几种锁? 2、MySQL 中有哪些不同的表格? 3、简述在 MySQL 数据库中 MyISAM 和 InnoDB 的区别 4、MySQL 中 InnoDB 支持的四种事务隔离级别名称,以及逐级之间的区别? 5、CHAR 和 VARCHAR 的区别? 6、主键和候选键有什么区别? 7、myisamchk 是用来做什么的? 8、如果一个表有一列定义为 TIMESTAMP,将发生什么? 9、你怎么看到为表格定义的所有索引? 10、… Java 并发编程(一) 1、在 java 中守护线程和 阅读详情

几种排序算法的对比
名称最差时间复杂度最佳时间复杂度空间复杂度备注
选择排序O(n^2)O  
插入排序O(n^2)   
冒泡排序O(n^2)O(n)  
归并排序O(nlogn)  时间效率高,但是归并时需要临时数组,空间复杂度较高
快速排序O(n^2)O(nlogn) (平均时间)  
堆排序O(nlogn)   
桶排序O(n+N)  限于小整数排序,时间效率高于前几种
基数排序O(dn)  限于小整数排序,时间效率高于前几种

1. 选择排序(Selection Sort)

选择排序的思想是从数组中选择最小,次小,次次小, .......,的元素依次加到前面的数组中。

Code:

package sort;

public class SelectionSort {
	public static void selectionSort(int[] list) {
		int index;
		int currentElement;
		for (int i = 0; i < list.length - 1; i++) {
			index = i;
			currentElement = list[i];
			for (int j = i + 1; j < list.length; j++) {
				if (list[j] < currentElement) {
					currentElement = list[j];
					index = j;
				}
			}
			if (index != i) {
				list[index] = list[i];
				list[i] = currentElement;
			}
		}
	}
	
	public static void main(String[] args) {
		int[] list = {7,3,5,7,3,2,1,2};
		selectionSort(list);
		for (int i = 0; i < list.length; i++) {
			System.out.println(list[i]);
		}
	}
}



2.插入排序(Insertion Sort)

插入算法的思想是从第二个元素开始,依次插入到前面排好序的序列中,注意插入遍历从后往前遍历数组,还有下标越界问题!。程序就参考教材上的比较简单。

Code:

package sort;

public class InsertionSort {
	public static int[] insertionSort(int[] list) {
		for (int i = 1; i < list.length; i++) {
			int k;
			int currentElement = list[i];
			for (k = i - 1; k >= 0 && list[k] > currentElement; k--) {
				list[k + 1] = list[k];
			}
			list[k + 1] = currentElement;
		}
		return list;
	}
	
	public static void main(String[] args) {
		int[] list = {7,3,5,7,3,2,1,2};
		list = insertionSort(list);
		for (int i = 0; i < list.length; i++) {
			System.out.println(list[i]);
		}
	}
}


3. 冒泡排序(Bubble Sort)

冒泡排序算法需要遍历几次数组,在每次遍历中,比较 连续相邻 的元素。如果某一对元素是降序,则互换它们的位置;否则,保持不变。冒泡排序法需要遍历 n-1 次数组,每次遍历最佳情况是不用交换,则时间复杂度为 O(n) ,最差情况是每次都要交换,对于第 k 个要交换 n-1-k 次,此时时间复杂度是 O(n^2)

Trick:如果在某次遍历中没有发生交换,说明所有元素都已排好序了,不需再次交换。使用此特性可以改进算法。

Code:

package test;

public class Test {
	public static void main(String[] args) {
		int[] list = {3,2,1};
		list = bubbleSort(list);
		for (int i = 0; i < list.length; i++) {
			System.out.println(list[i]);
		}
	}

	public static int[] bubbleSort (int[] list) {
		boolean nextPass = true;
		for (int i = 0; i < list.length - 1 && nextPass; i++) {
			nextPass = false;
			for (int j = 0; j < list.length - 1 - i; j++) {
				if (list[j] > list[j + 1]) {
					int tmp = list[j];
					list[j] = list[j + 1];
					list[j + 1] = tmp;
					nextPass = true;
				}
			}
		}
		return list;
	}
}

4. 归并算法(MergeSort)

归并算法可以递归地描述为:算法将数组分为两半,对每部分递归地应用归并排序。在两部分都排好序后,对它们进行归并。归并排序的复杂度为 O(n log n)。


Code:

package sort;

import java.awt.List;

public class MergeSort {
	public static int[] merge(int[] list) {
		int[] firstHalf = new int[list.length / 2];
		System.arraycopy(list, 0, firstHalf, 0, firstHalf.length);
		if (firstHalf.length > 1)
			firstHalf = merge(firstHalf);
		
		int[] secondHalf = new int[list.length - list.length / 2];
		System.arraycopy(list, list.length / 2, secondHalf, 0, secondHalf.length);
		if (secondHalf.length > 1)
			secondHalf = merge(secondHalf);
		
		list = merge(firstHalf, secondHalf);
		
		return list;
	}
	public static int[] merge(int[] list1, int[] list2) {
		int[] tmp = new int[list1.length + list2.length];
		int current1 = 0, current2 = 0, current3 = 0;
		while (current1 < list1.length && current2 < list2.length) {
			if (list1[current1] < list2[current2])
				tmp[current3++] = list1[current1++];
			else 
				tmp[current3++] = list2[current2++];
		}
		while (current1 < list1.length) {
			tmp[current3++] = list1[current1++];
		}
		while (current2 < list2.length) {
			tmp[current3++] = list2[current2++];
		}
		
		return tmp;
	}
	
	public static void main(String[] args) {
		int[] list = {4,2,7,4,7,9,8,1};
		list = merge(list);
		for (int i = 0; i < list.length; i++) {
			System.out.println(list[i]);
		}
	}
}


5. 快速排序 (QuickSort)

快速排序算法在数组中选择一个称为主元(pivot)的元素,将数组分为两部分,使得 第一部分中的所有元素都小于或等于主元,而第二部分的所有元素都大于主元。对第一部分递归地应用快速排序算法,然后对第二部分递归地应用快速排序算法。 

在最差情况下,划分由 n 个元素构成的数组需要进行 n 次比较和 n 次移动。因此划分所需时间为 O(n) 。最差情况下,每次主元会将数组划分为一个大的子数组和一个空数组。这个大的子数组的规模是在上次划分的子数组的规模减 1 。该算法需要 (n-1)+(n-2)+...+2+1= O(n^2) 时间。

 在最佳情况下,每次主元将数组划分为规模大致相等的两部分。设 T(n) 表示使用快速排序算法对包含 n 个元素的数组排序所需的时间,因此,和归并排序的分析相似,快速排序的 T(n)= O(nlogn)。

package sort;

import java.awt.List;

public class QuickSort {
	public static int[] quickSort(int[] list) {
		quickSort(list, 0, list.length - 1);
		return list;
	}
	
	public static int[] quickSort(int[] list, int first, int last) {
		if (first < last) {//递归地对主元(pivot)前后的数组进行快排
			int pivotIndex = partition(list, first, last);
			quickSort(list, first, pivotIndex - 1);
			quickSort(list, pivotIndex + 1, last);
		}
		return list;
	}
	
	public static int partition(int[] list, int first, int last) {//操作过程见下图
		int pivot = list[first], low = first + 1, high = last;
		//寻找前半数组中大于主元的元素下标和后半数组中小于或等于主元的元素下标
		while (high > low) {
			while (pivot >= list[low] && low <= high) 
				low++;
			while (pivot < list[high] && low <= high)
				high--;
			//交换两个元素
			if (low < high) {
				int tmp = list[low];
				list[low] = list[high];
				list[high] = tmp;
			}
		}
		//插入主元进适当位置
		while (list[high] >= pivot && high > first)
			high--;
		if (list[high] < pivot) {
			list[first] = list[high];
			list[high] = pivot;
			return high;
		}
		else {
			return first;
		}
	}
	
	public static void main(String[] args) {
		int[] list = {2,6,3,5,4,1,8,45,2};
		list = quickSort(list);
		for (int i = 0; i < list.length; i++) {
			System.out.println(list[i]);
		}
	}
}

---------------------------------------------------------------------------------修改于2016/5/21---------------------
现在看来上面的快排代码不是很清晰,在下面附上个清晰点儿的代码:
public class QuickSort {
	public static void main(String[] args) {
		int[] array = {1,1,1,1,1,1,1,1};
		QuickSort qs = new QuickSort();
		qs.quickSort(array);
		for(int ele : array)
			System.out.print(ele + " ");
	}
	
	public void quickSort(int[] array) {
		quickSort(array, 0, array.length - 1);
	}
	
	public void quickSort(int[] array, int start, int end) {
		if(start > end)	//注意判断终止条件
			return;
		int index = partation(array, start, end);//主元所在位置
		quickSort(array, start, index - 1);//递归地对主元前的数组进行快排
		quickSort(array, index + 1, end);//递归地对主元后的数组进行快排
	}
	
	public int partation(int[] array, int start, int end) {
		int pivot = array[start];
		int low = start;
		int high = end + 1;
		while(high > low) {
			//这里写法一定要注意,首先确保没有数组指针越界访问,然后要确保当low和high指向元素大小相同时不陷入死循环如
			while(low < end && array[++low] < pivot) {}			
			while(high > start && array[--high] > pivot) {}
			/* 如果用下面两行代码代替上面两行代码的话,当array[low] == array[high] 的时候就会陷入死循环
			while(array[low] < pivot) low++;
			while(array[high] > pivot) high--;
			*/





			if(high > low)
				swap(array, low, high);
		}
		swap(array, start, high);
			
		return high;
	}
	
	public void swap(int[] array, int a, int b) {
		int tmp = array[a];
		array[a] = array[b];
		array[b] = tmp;
	}
}

辨析:归并 VS 快排

归并和快排都用了分治法。

对于归并排序,大量的工作是将两个子线性表进行归并,归并是在子线性表都排好序之后进行的。

对于快速排序,大量的工作是将线性表划分为两个子线性表,划分是在子线性表排好序后进行的。

在最差情况下, 归并时间效率 > 快排时间效率 ,但是在平均情况下,两者的效率相同。

空间效率上, 归并空间效率 < 快排空间效率。 原因是 归并排序在归并两个子数组时需要一个临时数组,而快速排序不需要额外的数组空间。


6. 堆排序(HeapSort)

堆排序使用的是二叉堆,它是一棵完全二叉树。首先了解一下堆的结构和特性,这对我们理解堆排序有着重要的意义。

======================================扩展=============================================

扩展:堆(heap)是一棵具有以下属性的二叉树:

  • 它是一棵完全二叉树(除了最后一层没填满以及最后一层的叶子都是偏左放置的,如果一棵二叉树的每一层都是满的,那么这颗二叉树就是完全的)
  • 每个节点大于或等于它的任意一个孩子
堆的序号特点:
对于位置 i 处的结点,它的左孩子在位置 2i+1 处,右孩子在位置 2i+2 处,它的父亲在 (i-2)/2 处。
向堆添加一个新结点:
  1. 首先将新结点加到堆的末尾
  2. 将它与它的父亲比较,如果比它父亲大,就交换两者位置
  3. 重复 2 直到它小于或等于父亲
例:


删除根结点:
  1. 删除根结点
  2. 将根结点替换为堆的最后一个元素
  3. 比较替换的元素与子结点大小,交换替换的元素和较小元素的位置
  4. 重复 3 ,直到替换的元素到合适的位置
例:

=======================================================================================================

下面继续正题,由于堆保持 父结点总是大于子结点 的特性,我们可以将 list 中的元素依次 add 到堆中,重建堆的过程中已经排好序了,接下来我们利用 删除根结点 的办法依次获取由大到小的元素。

堆排序的时间复杂度与归并排序相同,为 O(n logn),但是堆排序不需要额外的数组空间,相较于归并排序,堆排序的空间效率更高。


7.桶排序(BucketSort)

上面所讲的四种算法可以用在比较任何键值类型(如 整数、字符串以及任何 Comparable 的对象)上,但是这些基于比较的排序算法中没有时间复杂度好过 O(n logn) 的算法。桶排序适用于 小整数 的排序,无需比较键值。

桶排序的工作方式如下。假设键值的范围是 0 到 N-1 。我们需要 N 个标记为 0, 1, ...,N-1 的桶。如果元素的键值是 i, 那么久将钙元素放入桶 i 中。每个桶中都存在和键值具有相同值的元素,可以使用 ArrayList 来实现一个桶。

桶排序的时间复杂度为 O(n+N) ,空间复杂度也为 O(n+N) ,其中 n 是指线性表的大小。


8. 基数排序 

当 N 过大时,在 5 中介绍的桶排序就不是很可取了。此时可以用基数排序。

基数排序是基于桶排序的,不过它只用 十个 桶。


通常,基数排序需要耗费 O(dn) 时间对带整数键值的 n 个元素排序,其中 d 是所有键值中基数位置的最大值。

PHP的几种常用排序算法 PHP的几种常用排序算法 所谓排序,就是对一组数据,按照某个顺序排列的过程。 冒泡排序(Bubble sort) 基本思想:如下图所示,将一组数据看作一排竖着的气泡,然后让最后一个数与倒数第二个数进行比较,大的就往前移。然后用相同的方法,将倒数第二个数与倒数第三个进行比较,大的往前移,依次类推,最后本轮循环结束后,第一个元素就是最大的了,然后继续循环,得到第二个,第三个…… 下面给出实现代码,已经优化过的冒泡排序 function BubbleSort(&$arr){ //必须是& 阅读详情

相关推荐

量子密钥分发系统的设计与实现(二):光路子系统初步讨论

本文主要对量子密钥分发系统中的光路子系统方案进行了讨论。

量子领域相关话题 2407

java常见面试题(160道)

java常见面试题

打造专业技术博客 9万+

避坑指南:OpenWRT改AP模式必做的5个关键设置(DHCP/防火墙/WAN口处理)

本文详细解析了将OpenWRT设备从路由模式切换为AP模式时,必须完成的五个关键设置步骤。核心在于理解AP模式本质是带无线的智能交换机,需彻底关闭DHCP服务、调整防火墙策略、正确桥接所有物理接口(包括WAN口处理),并集成无线网络,以确保设备能透明接入主路由网络,避免出现设备隔离或无法互访等常见问题。

t1u2v的博客 738

JAVA面试题大全(200+道题目)

面试题 一、Java 基础 1.JDK 和 JRE 有什么区别? 2.== 和 equals 的区别是什么? 3.两个对象的 hashCode()相同,则 equals()也一定为 true,对吗? 4.final在 java 中有什么作用? 5.java中的 Math.round(-1.5) 等于多少? 6.String 属于基础的数据类型吗? 7.java中操作字符串都有...

qq_38078190的博客 13万+

将赛灵思 SDK 工程移植到 Vitis 的分步指南

注:本文转自赛灵思中文社区论坛,源文链接在此。本文原作者为XILINX工程师。 以下为个人译文,仅供参考,如有疏漏之处,还请不吝赐教。 您是赛灵思软件开发套件 (SDK) 用户吗? 您是否正在计划将自己的工程移植到 Vitis 软件平台? 那么本篇工程移植分步指南将能祝您一臂之力。 从 2019.2 版开始,赛灵思 SDK 开发环境已统一整合到全功能一体化的 Vitis™ 统一软件平台中。 尊敬的 SDK 用户,您只需轻点几下鼠标即可体验这一功能丰富的工具! 马上开始将工程从赛灵思 S

weixin_45825101的博客 1732

5分钟搞定阿里云千问大模型API申请:手把手教你获取API Key

本文提供了一份高效的阿里云通义千问大模型API Key获取实战指南。通过清晰的步骤,帮助开发者在五分钟内完成从账号准备、精准导航控制台到创建并验证API Key的全过程,并强调了密钥安全管理和基础调用测试的重要性,助力快速集成AI能力。

ol789012345的博客 1356

10万字208道Java经典面试题总结(附答案)

1、JDK 和 JRE 有什么区别? JDK(Java Development Kit),Java开发工具包 JRE(Java Runtime Environment),Java运行环境 JDK中包含JRE,JDK中有一个名为jre的目录,里面包含两个文件夹bin和lib,bin就是JVM,lib就是JVM工作所需要的类库。 2、== 和 equals 的区别是什么? 对于基本类型,==比较的是值; 对于引用类型,==比较的是地址; equals不能用于基本类型的比较; 如果没有重写equa

学Java,找哪吒 106万+

生成式深度学习(第二版)-第一章-Conda环境下TensorFlow和Keras的安装和配置

按照书籍中的代码运行提示,需要配置Docker。个人的机器上正好有 conda4.14.0,索性就在本地环境进行配置。

GarryWang1248的博客 398

Java程序员不得不会的124道面试题(含答案)

double 和 long 都是64位宽,因此对这两种类型的读是分为两部分的,第一次读取第一个 32 位,然后再读剩下的 32 位,这个过程不是原子的,但 Java 中 volatile 型的 long 或 double 变量的读写是原子的。意思就是说,在你写一个 volatile 域时,能保证任何线程都能看到你写的值,同时,在写之前,也能保证任何数值的更新对所有线程是可见的,因为内存屏障会将其他所有写的值更新到缓存。如果你不这么做,在解析或者格式化日期的时候,可能会获取到一个不正确的结果。

Javaesandyou的博客 3万+

ControlCAN.dll开发适配USBCAN_E/2E_U设备

摘要:本文提供了USBCAN_E/2E_U设备的配置指南:1)安装专用驱动;2)获取最新的ControlCAN.dll文件;3)修改VCI调用顺序,新增VCI_SetReference函数设置波特率;4)使用工具计算波特率配置值,并给出了常见波特率对应的代码参考值(如500K对应0x060007)。配置过程强调使用特定工具计算精确的波特率参数,并提供了相关软件下载链接。

weixin_43969033的博客 392

【科研绘图系列】R语言绘制代谢物与临床表型相关性的森林图

本研究通过森林图展示了特定代谢物与三种临床比较之间的相关性分析结果。这种方法不仅直观地展示了每个代谢物的效应大小和不确定性,还允许我们比较不同代谢物在不同临床比较中的影响。森林图的结果表明,某些代谢物在特定临床比较中显示出显著的相关性,这可能为理解疾病的代谢机制提供了重要线索。

专注生信领域 566

java中常用的几种排序算法

以下列出java中常用的几种排序算法,只是简单实现了排序的功能,还有待改进,望指教(以下均假设数组的长度为n): 1)冒泡排序: 依次比较相邻的两个元素,通过一次比较把未排序序列中最大(或最小)的元素放置在未排序序列的末尾。   public class BubbleSort { public static void sort(int data[]) { for (int i = 0

ygc87的专栏 3万+

【华为OD机考】2025C+B+2024E+D卷真题【完全原创题解 | 详细考点分类 | 不断更新题目 | 六种主流语言Py+Java+Cpp+C+Js+Go】

可上练习华子OD、大厂真题绿色聊天软件戳od1441了解算法冲刺训练(备注【优快云】否则不通过)

weixin_48157259的博客 1万+

几种常用的排序算法(c语言实现)

概述最近重新回顾了一下数据结构和算法的一些基本知识,对几种排序算法有了更多的理解,也趁此机会通过博客做一个总结。

蜗牛的书的博客 4万+

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

常用的几种排序算法小结

常见的排序算法小结

2302_80442755的博客 1118

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

几种常用排序算法的复杂度

几种常用排序算法的复杂度

Follow__Heart的博客 1282

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

几种常用的查找和排序算法

几种常用的查找和排序算法发表于:2008年4月1日 13时53分7秒阅读(1)评论(0)本文链接:http://user.qzone.qq.com/40348105/blog/1207029187 几种常用的查找和排序算法 #includ

wflishh的专栏 803

几种常见的算法 和 几种常用的排序算法

算法: 解决问题的方法 总结一下常用的几种算法 1.递推法 递推是序列计算机中的一种常用算法。它是按照一定的规律来计算序列中的每个项,通常是通过计算机前面的一些项来得出序列中的指定项的值。其思想是把一个复杂的庞大的计算过程转化为简单过程的多次重复,该算法利用了计算机速度快和不知疲倦的机器特点。 例如 如果你手里4个球分别: 红、蓝、黑、黄, 而现在只有一个球里面装有答案, 你需要一个...

weixin_30338497的博客 418

常用的几种排序算法

常用的几种排序算法 什么是算法的稳定性? 稳定性的定义: 排序前后两个相等的数相对位置不变,则算法稳定。 那么稳定有什么好处吗? 从一个键上排序,然后再从另一个键上排序,第一个键排序的结果可以为第二个键排序所用 各排序算法的稳定性: 堆排序、快速排序、希尔排序、直接选择排序不是稳定的排序算法; 基数排序、冒泡排序、直接插入排序、折半插入排序、归并排序是稳定的排序算法。 1. 冒泡排序算法 这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。 它的时间复杂度为o(n^2)

eilot_c的博客 1258

常用几种排序算法

常用的几种排序算法 选择排序方法时,除了基本实现的要求外,还应当注重所选择算法的时间、空间复杂度既执行效率,另外在实际工作中往往还要注意算法的稳定性(排序后相同元素的相对位置是否发生改变)等。 一、冒泡、插入、选择 二、快排、归并 1、冒泡排序 冒泡排序只会操作相邻的两个数据。每次冒泡操作都会对相邻的两个元素进行比较,看是否满足大小关系要求,如果不满足则两者互换。 public static vo...

MrBxiong的博客 344

几种常用排序算法的比较

在计算机科学所使用的排序算法通常被分类为: 1-计算的复杂度(最差、平均、和最好性能),依据列表(list)的大小(n)。一般而言,好的性能是O(n log n),且坏的性能是O(n2)。对于一个排序理想的性能是O(n)。仅使用一个抽象关键比较运算的排序算法总平均上总是至少需要O(n log n)。 2-存储器使用量(以及其他电脑资源的使用) 3-稳定度:稳定排序算法会依照相等的关键(换言之

重回编程之路 1023

介绍几种常用的排序算法

通过重复地遍历要排序的序列,每次比较相邻的元素并交换它们的位置,使得每次遍历都将当前未排序部分中的最大(或最小)元素移动到末尾。:通过选择一个“基准”(pivot),将数据分为两部分,一部分比基准小,另一部分比基准大,然后对这两部分递归进行快速排序。:利用堆这种数据结构,将序列构造成一个大顶堆或小顶堆,然后反复将堆顶元素移到序列末尾,并重新调整堆结构。:通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。:适用于数据范围较小的整数排序,效率高,但需要较大的额外空间。

m0_48022770的博客 985

几种常用的排序算法

所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。排序算法,就是如何使得记录按照要求排列的方法。排序算法在很多领域得到相当地重视,尤其是在大量数据的处理方面。一个优秀的算法可以节省大量的资源。在各个领域中考虑到数据的各种限制和规范,要得到一个符合实际的优秀算法,得经过大量的推理和分析。 冒泡排序 冒泡排序(英语:Bubble Sort,台湾另外一种译名为:泡沫...

weixin_45410402的博客 372
上一篇: 【JAVA/读书随笔】Chapter 22 算法效率
下一篇: 【JAVA/辨析】Java 中Comparable和Comparator区别比较
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值