排序算法-java实现

文章详细介绍了冒泡排序、选择排序、插入排序和快速排序这四种基本排序算法的工作原理、时间复杂度、稳定性,并提供了Java代码实现。针对每种排序算法,还提出了代码优化策略,如冒泡排序的循环次数和状态判断优化,选择排序的循环减半优化等。
摘要由CSDN通过智能技术生成

冒泡排序

原理:

依次比较两个相邻的元素,如果它们顺序错误就把它们交换过来。
在这里插入图片描述
时间复杂度:

若文件的初始状态是正序的,一趟扫描即可完成排序。所需的关键字比较次数C和记录移动次数M均达到最小值:C min = n-1 ,M min =0;
所以,冒泡排序最好的时间复杂度为 O(n)。

若初始文件是反序的,需要进行 n-1 趟排序。每趟排序要进行 n-i 次关键字的比较(1≤i≤n-1),且每次比较都必须移动记录三次来达到交换记录位置。在这种情况下,比较和移动次数均达到最大值:
在这里插入图片描述

冒泡排序的最坏时间复杂度为O(n^2)

综上,因此冒泡排序总的平均时间复杂度为 O(n^2)

稳定性:
冒泡排序是一种稳定排序算法。

代码实现:


	public static void sort(int[] array){
		for(int i=0;i<array.length-1;i++){
			for(int j=0;j<array.length-1;j++){
				if(array[j]>array[j+1]){
					int tmp=0;
					tmp=array[j];
					array[j]=array[j+1];
					array[j+1]=tmp;
				}
			}
		}
	}
	public static void main(String[] args){
		int[] array={10,5,3,7,6};
		sort(array);
		System.out.println(Arrays.toString(array));
	}

代码优化:
(1)对循环次数进行优化:
在第一轮比较完之后,会找到元素的最大值,放在最右边,这样在下轮比较时就不用再比较第一个元素了,所以内部循环可以优化为 j < array.length- 1 - i
(2)对排序完成进行优化:
对于状态已经变为按顺序进行排列的元素,这时候就已经排序完成了,不需要再进行循环遍历交换了,可以使用一个标志位flag来判断排序是否已经完成了。如果执行交换代码了,则证明还需要排序,flag=false,如果没有执行交换代码,不需要再进行排序了,直接return。

public static void sort(int[] array){
	for(int i=0;i<array.length-1;i++){
	boolean flag=false;
		for(int j=0;i<ayyay.length-i-1;j++){
			if(array[j]>array[j+1]){
				int tmp=0;
				tmp=array[j];
				array[j]=array[j+1];
				array[j+1]=tmp;
				flag=true;
			}
		}
		if(flag==false){
			return;
		}
	}
}

选择排序

原理:
第一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后再从剩余的未排序元素中寻找到最小(大)元素,然后放到已排序的序列的开头。以此类推,直到全部待排序的数据元素的个数为零。

时间复杂度:
最好情况是,已经有序,交换0次。
最坏情况交换n-1次,逆序交换n/2次。

稳定性:
选择排序是不稳定的排序方法

代码实现:

public static void selectSort(int[] nums){
	for(int i=0;i<nums.length-1;i++){
		int tmp=i;
		for(int j=i+1;j<nums.length;j++){
			if(nums[j]<nums[tmp]){
				swap(nums,j,tmp);
			}
		}
	}
}
public static void swap(int[] nums,int i,int j){
	int temp=nums[i];
	nums[i]=nums[j];
	nums[j]=temp;
}

代码优化:
在一次循环后,将最小值放在最前端,最大值放在最后端(或者最小的在最后端,最大的在最前端),这样可以实现循环减半。


public static void sort(int[] nums){
	if(nums.length<2){
	return;
	}
	int left=0;//每次排序最左位置
	int right=nums.length-1;//每次排序最右位置
	int minpos=0;//每次选出的最小值的数组下标
	int maxpos=0;//每次选出的最大值的数组下标
	while(left<right){
		for(int i=left;i<=right;i++){
			//找出值更小的元素,记下数组下标
			if(nums[i]<nums[minpos]){
				minpos=i;
			}
			//找出值更大的元素,记下数组下标
			if(nums[i]>nums[maxpos]){
				maxpos=i;
			}
			//如果本次循环的最小元素不是最左边的元素,则交换他们的位置
			if(minpos != left){
				swap(nums,minpos,left);
			}
			//如果maxpos位置是left,则maxpos的值要修改为minpos(因为在上面已将left位置的值替换为minpos位置的值)(下面图形解释)
			if(maxpos==left){
				maxpos=minpos;
			}
			//如果本次循环的最大元素不是最右边的元素,则交换他们的位置
			if(maxpos != right){
				swap(nums,right,maxpos);
			}
			left++;
			right--;
		}
	}
}
public staic void swap(int[] nums,int i,int j){
	int temp=nums[i];
	nums[i]=nums[j];
	nums[j]=temp;	
}

此段代码解释:

		if(maxpos==left){
			maxpos=minpos;
		}
012345
952347

第一次循环:
left=0
right=5
minpos=2
maxpos=0 (maxpos=left)

		if(minpos != left){
			swap(nums,minpos,left);
		}
012345
259347

此时因为left和minpos的元素进行了交换,而恰好最开始maxpos=left,所以这个最大元素的位置被换到了minpos的位置上,所以要设置maxpos=minpos

插入排序

原理:
在待排序的元素中,假设前面n-1个元素已经是排序好的,现在将第n个数据插入到前面已经排好的序列中,然后找到合适的位置,使得插入的第n个数的这个序列也是排好顺序的。按照此法对所有元素进行插入,直到整个序列排为有序的过程,称为插入排序。

时间复杂度:
最好情况下,当前数组是有序的,只需要比较n-1次就可,时间复杂度为O(N)
最坏情况下,排序数组是逆序,时间复杂度是O(N^2)

稳定性:

如果待排序的序列中存在两个或两个以上具有相同关键词的数据,排序后这些数据的相对次序保持不变,即它们的位置保持不变,通俗地讲,就是两个相同的数的相对顺序不会发生改变,则该算法是稳定的;如果排序后,数据的相对次序发生了变化,则该算法是不稳定的。

代码实现:

public static void sort(int[] nums){
	for(int i=1;i<nums.length;i++){
		int valueToSort=nums[i];
		int j=i;
		while(j>0 && nums[j-1]>valueToSort){
			nums[j]=nums[j-1];
			j--;
		}
		nums[j]=valueToSort;
	}
}

快速排序

原理:

快速排序算法通过多次比较和交换来实现排序,其排序流程如下:
(1)首先设定一个分界值,通过该分界值将数组分成左右两部分。
(2)将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。此时,左边部分中各元素都小于分界值,而右边部分中各元素都大于或等于分界值。
(3)然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理。
(4)重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左、右两个部分各数据排序完成后,整个数组的排序也就完成了。

时间复杂度:

稳定性:

代码实现:

//双指针法
private int sort(int[] nums,int startIndex,int endIndex){
	int pivot=nums[startIndex];
	int leftPoint=startIndex;
	int rightPoint=endIndex;
	while(leftPoint < rightPoint){
		//从右向左找到比pivot小的数据
		while(leftPoint<rightPoint && nums[rightPoint]>pivot){
			rightPoint--;
		}
		//从左向右找到比pivot大的数据
		while(leftPoint<rightPoint && nums[leftPoint]<=pivot){
			leftPoint++;
		}
		//没有过界则交换
		if(leftPoint<rightPoint){
			int temp=nums[leftPoint];
			nums[leftPoint]=nums[rightPoint];
			nums[rightPoint]=temp;
		}
	}
	//最终将分界值与当前指针数据交换
	nums[startIndex]=nums[rightPoint];
	nums[rightIndex]=pivot;
	//返回分界值所在下标
	return rightPoint;
}

希尔排序

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值