排序算法

1.冒泡排序

冒泡排序算法需要遍历几次数组。在每次遍历中,比较连续相邻的元素。如果某一对元素是降序,则互换他们的值;否则,保持不变。由于较小的值像“气泡”一样逐渐浮向顶部,而较大的值沉向底部,故称这种技术为冒泡排序或下沉排序。第一次排序后,最后一个元素成为数组中的最大数。第二次遍历后,倒数第二个元素成为数组中的第二大数。整个过程持续到所有的元素都已排好序。

原始的冒泡排序算法如下:

for(int k=1;k<list.length;k++)
	for(int i=0;ilist[i+1]){
			int temp;
			temp = list[i];
			list[i] = list[i+1];
			list[i+1] = temp;
		}


但是,若果在某次遍历中没有发生交换,那么就不必进行下一次遍历了,因为所有的元素都已经排好序了。使用这一条可以改进上述算法:

boolean nextTime = true;
for(int k=1;k<list.length&&nextTime;k++)
	for(int i=0;i<list.length-k;i++)
		if(list[i]>list[i+1]){
			int temp;
			temp = list[i];
			list[i] = list[i+1];
			list[i+1] = temp;
			nextTime = true;
		
		}

在最佳情况下,冒泡排序算法只要一次遍历就能确定数组已排好序,不需要进行下一次的遍历。由于第一次的遍历情况比较次数为,冒泡排序的时间为

在最坏的情况下,冒泡排序算法需要进行次遍历。第一次遍历需要次比较;第二次遍历需要次比较;依次进行,最后一次遍历需要1次比较。因此,比较的总次数为:


因此,在最坏的情况下,冒泡排序的时间为


2.归并排序

归并排序算法可以递归的描述为:算法将数组分成两半,对每部分递归的应用归并排序。在两部分都排好序后,对他们进行归并。该算法的伪代码如下:

public static void mergeSort(int[] list){
	if(list.length>1){
		mergeSort(list[0 ... list.length/2]);
		mergeSort(list[list.length/2+1 ... list.length]);
		merge list[0 ... list.length/2] with list[list.length/2+1 ... list.length];
	}

}

递归调用将持续将数组划分为子数组,直到每个子数组只包含一个元素。然后,该算法将这些小的数组归并为稍大的有序子数组,直到最后形成一个有序的数组。

算法的实际实现如下:

public static void mergeSort(int[] list){
	if(list.length>1){
		int[] firstHalf = new int[list.length/2];
		System.arraycopy(list,0,firstHalf,0,list.length/2);
		mergeSort(firstHalf);
		
		int secondHalfLength = list.length - list.length/2;
		int[] secondHalf = new int[secondHalfLength];
		System.arraycopy(list,list.length/2,secondHalf,0,secondHalfLength);
		mergeSort(secondHalf);
		
		int[] temp = merge(firstHalf,secondHalf);
		System.arraycopy(temp,0,list,0,temp.length);
	}

}

private static int[] merge(int[] list1,int[] list2){
	int[] temp = new int[list1.length+list2.length];
	int current1 = 0; //current index in list1
	int current2 = 0; //current index in list2
	int current3 = 0; //current index in temp
	
	while(current1<list1.length && current2<list2.length){
		if(list1[current1]<list2[current2])
			temp[current3++] = list1[current1++];
		else
			temp[current3++] = list2[current2++];
	}
	
	while(current1<list1.length)
		temp[current3++]=list1[current1++];
		
	while(current2<list2.length)
		temp[current3++]=list2[current2++];
		
		return temp;

}

来看看归并排序算法的算法复杂度。归并排序算法讲数组分为两个子数组,使用相同的算法对子数组进行递归排序,然后将子数组进行归并。


第一项是对前半部分排序所需的时间,第二项是对后半部分排序所需的时间,要归并两个数组,最多需要次比较两个子数组中的元素,以及次移动将元素移动到临时数组中。故:


复杂度为该算法优于选择排序、插入排序和冒泡排序。

其中,java.util.Arrays类中的sort方法就是使用归并排序算法的变体来实现的。


3.快速排序

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

public static void quickSort(int[] list){
	if(list.length>1){
		select a pivot;
		partition list into list1 and list2 such that 
			all elements in list1 <=pivot and
			all elements in list2 >pivot
		quickSort(list1);
		quickSort(list2);
	}
}

该算法的每次划分都将主元放在了恰当的位置。主元的选择会影响算法的性能。在理想的情况下,应该选择能平均划分两部分的主元。为了简单起见,假定讲数组的第一个元素选择为主元。

算法实现如下类,类中有两个重载的quickSort方法。第一个用来对数组进行排序,第二个是一个辅助方法,用于特定范围内的子数组进行排序。

public class QuickSort {
	public static void quickSort(int[] list) {
		quickSort(list, 0, list.length - 1);

	}

	private static void quickSort(int[] list, int first, int last) {
		// TODO Auto-generated method stub
		if (last > first) {
			int pivotIndex = partition(list, first, last);
			quickSort(list, first, pivotIndex - 1);
			quickSort(list, pivotIndex + 1, last);
		}
	}

	private static int partition(int[] list, int first, int last) {
		int pivot = list[first];
		int low = first + 1;
		int high = last;

		while (high > low) {
			while (low <= high && list[low] <= pivot)
				low++;

			while (low <= high && list[high] > pivot)
				high++;

			if (high > low) {
				int temp = list[high];
				list[high] = list[low];
				list[low] = temp;
			}
		}

		while (high > first && list[high] >= pivot)
			high--;

		if (pivot > list[high]) {
			list[first] = list[high];
			list[high] = pivot;
			return high;
		} else
			return first;
	}

	public static void main(String[] args){
		int[] list = {2,3,5,2,7,9,4,12,8,20};
		quickSort(list);
		
		for(int i=0;i<list.length;i++)
			System.out.print(list[i]+" ");
	} 
}
方法partition使用主元划分数组list[];将子数组的第一个元素宣威主元,在初始情况下,low指向子数组中的第二个元素,high指向子数组中的最后一个元素。

方法在数组中从左侧开始查找第一个大于主元的元素,然后从数组右侧开始查找第一个小于或者等于主元的元素,最后交换这两个元素。在while循环中重复相同的查找和交换操作,直到所有的元素都查找完为止。

如果主元被移动,方法返回将子数组分为两部分的主元的新下标;否则,返回主元的原始下标。

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

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


和归并排序的分析相似,快速排序的

归并排序和快速排序都使用了分治法。对于归并排序,大量的工作是将两个子表进行归并,并是在子表都排好序后进行的。对于拍拍,大量的工作是将线性表划分为两个子线性表,划分是在子线性表排好序之前进行的。在最差的情况下,归并排序的效率高于快排,但是,在平均的情况下,两者的效率相同。归并排序在归并两个子数组时需要一个临时数组,而快排不需要额外的数组空间。因此,快排的空间效率高于归并排序。


4.堆排序

堆排序使用的是二叉堆。其中,堆是一棵具有以下属性的二叉树:

(1)它是一棵完全二叉树;

(2)每个节点大于或等于它的任意一个孩子。

除了最后一层没填满以及最后一层的叶子都是偏左放置的,如果一棵二叉树的每一层都是满的,那么这棵二叉树就是完全的。

SQL Prompt是Red Gate Software公司开发的一款强大的SQL代码编辑和优化工具,主要面向数据库开发者和管理员。版本11.0.1.16766是一个更新版本,它提供了更高效、更便捷的SQL编写环境,旨在提升SQL代码的可读性、效率和一致性。这个安装包包含了所有必要的组件,用于在用户的计算机上安装SQL Prompt工具。 SQL Prompt的核心功能包括自动完成、智能提示、格式化和重构。自动完成功能能够帮助用户快速输入SQL语句,减少键入错误;智能提示则基于上下文提供可能的选项,加快编写速度;格式化功能允许用户按照自定义或预设的编码规范对SQL代码进行美化,提升代码的可读性;而重构工具则可以帮助用户优化代码结构,消除潜在问题。 在描述中提到的“代码格式化规则来源于网络”,指的是用户可以通过下载网络上的json文件来扩展或定制SQL Prompt的代码格式化规则。这些json文件包含了特定的格式设置,如缩进风格、空格使用、注释位置等。将这些文件复制到指定的目录(例如:C:\Users\用户名\AppData\Local\Red Gate\SQL Prompt 10\Styles)后,SQL Prompt会读取这些规则并应用到代码格式化过程中,使得用户可以根据个人偏好或团队规范调整代码样式。 以下几点请注意: 1. 经实测,此版本支持最新的Sql Server 2022版的SSMS21 2. 此安装包中不包括keygen,请自行解决
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值