数组七大经典排序算法(冒泡、插入、希尔、快排、选择、归并、堆)Java实现

简介

这是一份常用经典排序算法的Java代码实现,可运行。

算法包括:冒泡排序、插入排序、希尔排序、快速排序、选择排序、归并排序、堆排序。

复杂度

算法复杂度,稳定性:
复杂度稳定性
忘记说希尔排序了~

希尔排序是不稳定的。因为,虽然一次插入排序是稳定的,但是分区后的插入可能导致不同分区之间的数相对位置发生改变,因此最终是不稳定的。

希尔排序的时间复杂度没有明确的说法,有的说平均复杂度是O(nlogn),有的说是O(n1.3),总之就是比O(n2)小啦。

希尔排序是按照不同步长对元素进行插入排序,当刚开始元素很无序的时候,步长最大,所以插入排序的元素个数很少,速度很快;当元素基本有序了,步长很小,插入排序对于有序的序列效率很高。所以,希尔排序的时间复杂度会比o(n^2)好一些

代码

话不多说,上代码:

package test1;

public class Test {
	/**
	 * 交换数组中两个数
	 * @param nums
	 * @param i
	 * @param j
	 */
	public static void swap(int nums[],int i,int j){
		int temp;
		temp = nums[i];
		nums[i] = nums[j];
		nums[j]=temp;
	}
	/**
	 * 冒泡排序
	 * @param nums
	 */
	public static void bubbleSort(int nums[]){
		for(int i = 0;i<nums.length;i++){
			for(int j = 0;j<nums.length-i-1;j++){
				if(nums[j]>nums[j+1]){
					swap(nums,j,j+1);
				}
			}
		}
	}
	/**
	 * 改进的冒泡排序
	 * @param nums
	 */
	public static void improvedBubbleSort(int nums[]){
		for(int i = 0;i<nums.length;i++){
			boolean isChanged = false;//标记一轮中,是否发生交换
			for(int j = 0;j<nums.length-i-1;j++){
				if(nums[j]>nums[j+1]){
					swap(nums,j,j+1);
					isChanged = true;
				}
			}
			if(!isChanged){//如果没有发生交换,则已经有序,直接结束排序
				break;
			}
		}
	}
	/**
	 * 插入排序
	 * @param nums
	 */
	public static void insertSort(int nums[]){
		int len = nums.length;
		for(int i = 1;i<len;i++){
			if(nums[i]<nums[i-1]){//如果有逆序,则向前找到一个不大于该值的位置,插到它后面
				int temp = nums[i];//保存当前值
				int j;
				//向前寻找
				for(j = i-1;j>=0&&nums[j]>temp;j--){
					nums[j+1] = nums[j];
				}
				nums[j+1] = temp;//赋值
			}
		}
	}
	/**
	 * 引入二分查找的插入排序
	 * @param nums
	 */
	public static void insertBinarySort(int nums[]){
		int len = nums.length;
		for(int i = 1;i<len;i++){
			if(nums[i]<nums[i-1]){//如果有逆序,则向前找到一个不大于该值的位置,插到它后面
				int temp = nums[i];//记录当前值
				int low = 0,h = i-1,mid=0;//二分查找的low,high,mid位置标记
				while(low<=h){
					mid = (low+h)/2;
					if(temp>nums[mid]){//如果当前值大于中间位置的值,则区间改为[mid+1,h]
						low = mid+1;
					}else{//如果当前值小于中间位置的值,则区间改为[low,mid-1]
						h = mid-1;
					}
				}
				//找到位置(就是low)后,从后向前移动数组,以腾出位置
				for(int j = i;j>low;j--){
					nums[j] = nums[j-1];
				}
				nums[low] = temp;//赋值
			}
		}
	}
	/**
	 * 希尔排序
	 * @param nums
	 */
	public static void shellSort(int nums[]){
		int len = nums.length;
		for(int gap = len/2;gap>0;gap/=2){//gap每次减半
			for(int i = 0;i<len;i+=gap){//比较的时候,每隔gap个数比较一次
				int temp = nums[i];//记录当前值
				int j;
				//如果当前值小于前面对应的gap位置的数,则把前面的数赋值到当前位置
				//j-=gap,将索引又移到前面gap处,以便于交换
				for(j = i;j>=gap&&temp<nums[j-gap];j-=gap){
					nums[j] = nums[j-gap];
				}
				//再把当前值赋值到前面的位置,本质就是交换了当前值和前面对应gap位置的数
				nums[j] = temp;
			}
		}
	}
	/**
	 * 选择排序
	 * @param nums
	 */
	public static void selectSort(int nums[]){
		int len = nums.length;
		for(int i = 0;i<len-1;i++){
			int min = i;//记录最小值的位置
			for(int j = i+1;j<len;j++){
				if(nums[j]<nums[min]){//如果有更小的,则更新最小值的位置
					min = j;
				}
			}
			//如果最小值位置改变了(不是i了),则交换,就是将最小值从后面换到前面
			if(min!=i){
				swap(nums,i,min);
			}
		}
	}
	/**
	 * 分而治之,找到划分基准,并交换
	 * @param nums
	 * @param left
	 * @param r
	 * @return
	 */
	public static int partation(int nums[],int left,int r){
		int pv = nums[left];//以最左边的数为基准
		int p = left;//记录基准位置
		//遍历数组,如果小于基准,则与基准交换
		for(int i = p+1;i<=r;i++){
			if(nums[i]<pv){
				p++;//基准前移
				if(p!=i){//如果是基准位置的下一个位置,则不用交换
					swap(nums,p,i);
				}
			}
		}
		//最后,交换 划分位置 和 最左边
		swap(nums,p,left);	
		return p;//返回划分位置
	}
	/**
	 * 快速排序
	 * @param nums
	 * @param left
	 * @param r
	 */
	public static void quickSort(int nums[],int left,int r){
		if(left<r){//切记,一定要左边小于右边时才分部处理
			int p = partation(nums,left,r);//找到一个基准位置p
			quickSort(nums,0,p-1);//以p为中心,将数组分成两个子数组,分而治之
			quickSort(nums,p+1,r);
		}
	}
	/**
	 * 快速排序
	 * @param nums
	 */
	public static void quickSort(int nums[]){
		int len = nums.length;
		quickSort(nums,0,len-1);
	}
	
	/**
	 * 合并
	 * @param nums
	 * @param temp
	 * @param left
	 * @param mid
	 * @param r
	 */
	public static void merge(int nums[],int temp[],int left,int mid,int r){
		//拷贝一份数组
		for(int i = left;i<=r;i++){
			temp[i] = nums[i];
		}
		//记录左右部分的索引
		int pa = left,pb = mid+1;
		int index = left;//合并后数组的索引
		
		while(pa<=mid&&pb<=r){
			if(temp[pa]<=temp[pb]){//哪一部分小,就拷贝哪一部分到新数组
				nums[index++] = temp[pa++];
			}else{
				nums[index++] = temp[pb++];
			}
		}
		//处理没有拷贝完的部分,直接赋值到新数组
		while(pa<=mid){
			nums[index++] = temp[pa++];
		}
		while(pb<=r){
			nums[index++] = temp[pb++];
		}
		
		
	}
	/**
	 * 归并排序
	 * @param nums
	 * @param temp
	 * @param left
	 * @param r
	 */
	public static void mergeSort(int nums[],int temp[],int left,int r){
		if(left<r){//左边小于右边时,分而治之
			int mid = (left+r)/2;
			mergeSort(nums,temp,0,mid);
			mergeSort(nums,temp,mid+1,r);
			//合并
			merge(nums,temp,left,mid,r);
		}
	}
	/**
	 * 归并排序
	 * @param nums
	 */
	public static void mergeSort(int nums[]){
		int len = nums.length;
		int temp[] = new int[len];
		mergeSort(nums,temp,0,len-1);
	}
	/**
	 * 调整堆
	 * @param nums
	 * @param i
	 * @param len
	 */
	public static void adjustHeap(int nums[],int i,int len){
		int temp = nums[i];//记录当前节点值(父节点)
		//向下遍历子节点
		for(int k = 2*i+1;k<len;k = k*2+1){
			//如果存在右子节点,且右子节点大于左子节点
			if(k+1<len&&nums[k]<nums[k+1]){
				k++;//指向右子节点
			}
			//如果子节点比当前节点(父节点)大,则将子节点值赋给父节点
			if(nums[k]>temp){
				nums[i] = nums[k];
				i = k;//记录父节点的位置,最终要落到的位置
			}else{//如果子节点小于父节点,则调整结束
				break;
			}
		}
		//将当前父节点的值,最终落位入座
		nums[i] = temp;
	}
	/**
	 * 堆排序,升序,最大堆
	 * 通过不断把根节点(最大值)放到最后一个节点处,
	 * 实现数组从小到大排序
	 * @param nums
	 */
	public static void heapSort(int nums[]){
		int len = nums.length;
		// build heap
		for(int i = len/2-1;i>=0;i--){
			//从最后一个非叶节点开始调整,使之满足最大堆
			adjustHeap(nums,i,len);
		}
		// exchange and adjust
		for(int j = len-1;j>=0;j--){
			//交换根节点与最后一个节点,通过不断把根节点(最大值)放到最后一个节点处,
			//实现数组从小到大排序
			swap(nums,0,j);
			//调整剩下的节点,使它们满足最大堆
			adjustHeap(nums,0,j);//j是当做长度传过去的,所以,去掉了最后一个节点
		}
		
	}
	
	public static void main(String[] args) {
		
		int nums[] = {2,4,5,7,2,3,4};
		System.out.println("-----排序前-----");
		for(int n:nums){
			System.out.println(n);
		}
//		bubbleSort(nums);//冒泡排序
//		improvedBubbleSort(nums);//改进的冒泡排序
//		insertSort(nums);//插入排序
//		insertBinarySort(nums);//二分插入排序
//		shellSort(nums);//希尔排序
//		selectSort(nums);//选择排序
//		quickSort(nums);//快速排序
//		mergeSort(nums);//归并排序
		heapSort(nums);//堆排序
		
		System.out.println("------排序后-----");
		for(int n:nums){
			System.out.println(n);
		}
		
 }

}

补充:

快速排序的思想:分治法

  • 1.先从数组中取出一个数作为基准数。
  • 2.分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。
  • 3.再对左右区间重复第二步,直到各区间只有一个数。

参考文章
堆排序讲解 https://www.cnblogs.com/chengxiao/p/6129630.html
各算法的性能及复杂度讲解 https://segmentfault.com/a/1190000004994003

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章