排序算法(Java实现)

目录

各种排序算法的时间复杂度

插入排序

选择排序

冒泡排序

哈希排序

快速排序

归并排序

堆排序


各种排序算法的时间复杂度

说明:以下各种算法设计都按照升序排序,所有图片均为百度图片。

插入排序

  1. 分为有序序列和无序序列,每次把无序序列中的一个插入有序序列,刚开始有序系列只有arr[0]
  2. 先把要插入的值保存在临时变量
  3. 然后一直往前比较,比要插入的值大就继续往前比,并把该位后移
  4. 直到找到比插入值小或相等的元素,然后临时变量的值插入在它的后面
  5. 记得加下标> = 0的条件,因为它可能会比有序序列所有数小。
public static void insertSort(int[] arr) {
	for (int i = 1; i < arr.length; i++) {
		int temp = arr[i];
		int j = i - 1;
		while (j >= 0 && arr[j]>temp) {
			arr[j + 1] = arr[j];
			j--;
		}
		arr[j + 1] = temp;
	}
}

选择排序

  1. 从第一个元素开始,它与它后面所有的值相比较,记录下最小值得那个元素的下标,每一次都与当前最小值比较,找出最小值,然后跟当前要排序的位置元素交换。
  2. 然后第二个,第三个,......,与后面所有值相比较。
public static void chooseSort(int[] arr) {
	int len = arr.length;
	for (int i = 0; i < len-1; i++) {
		int minIndex = i;
		for (int j = i + 1; j < len; j++) {
			if (arr[j] < arr[minIndex]) {
				minIndex = j; //记录下无序数列中最小数的下标
			}
		}
		if( minIndex != i) {
			int temp = arr[i];
			arr[i] = arr[minIndex];
			arr[minIndex] = temp;
		}
	}
}

冒泡排序

  1. 从第一个元素开始,与它后面一个元素比较,比它大就交换,让它在两个元素中始终后面的更大。
  2. 然后第二个跟第三个比较,......,到第n-1个个个与Ñ个元素比较后,最大的元素就被冒泡到了第Ñ个元素。
  3. 每一趟可以把一个无序序列中最大的元素放到最后,直到排序结束。
public static void bubblingSort(int[] arr) {
	int len = arr.length;
	for (int i = 0; i < len; i++) {
		for (int j = 0; j < len-1-i; j++) {
			if (arr[j + 1] < arr[j]) {
				int temp = arr[j];
				arr[j] = arr[j+1];
				arr[j+1] = temp;
			}
		}
	}
}

哈希排序

优点:作用于数据紧凑的序列排序速度快

缺点:使用范围狭小,条件苛刻,并且需要额外的辅助空间

适用于排序数据比较紧凑,并且明确知道数据范围才能使用,循环次数 = 数组长度 * 数据范围(最大值 - 最小值)

  1. 把想要排序的数组中的数映射到一个新的数组,统计每个数字多少个
  2. 从0开始把每个数字的个数还原到要排序的数组
public static void hashSort(int[] arr) {
	int[] temp = new int[101];
	for (int i = 0; i < arr.length; i++) {
		temp[ arr[i] ] ++;
	}
	int count = 0;
	for (int i = 0; i < temp.length; i++) {
		for(int j = 0;j<temp[i]; j++) {
			arr[count] = i;
			count++ ;
		}
	}
}

快速排序

优点:普遍情况下速度最快,不需要额外空间

缺点:  不稳定,最坏情况下会退化为冒泡排序

  1. 先取一个基值,然后把所有比它小的元素放到它的左边,所有比它大的元素放到它的右边
  2. 然后在它的左边跟右边一直递归下去,就会得到每个元素的左边都是比它小的,右边都是比它大的,也就是最后的有序序列
/**
   * 快速排序
 * @param arr 要排序的数组
 * @param start 开始元素的下标
 * @param end 末尾元素下标+1
 */
public static void quickSort(int[] arr, int start,int end) {
	if (start < end) {
		int i = start, j = end-1;
		int base = arr[start];
		while (i < j) {
			while (i < j && arr[j] >= base) {
				j--;
			}
            if(i<j){
                arr[i] = arr[j];
            }
			while (i < j && arr[i] <= base) {
				i++;
			}
			if(i<j){
                arr[j] = arr[i];
            }
		}
		arr[i] = base;
		quickSort(arr, start, i - 1);
		quickSort(arr, i+1, end-1);
	}
}

归并排序

采用分治的思想,先把序列分解为独立的数据,然后相邻的数据,组成一个有序的序列,再把各自有序的两个序列合并为有序的一个序列,一直这样合并,达到整个序列有序的结果,归并的次数 =  待排序数个数 - 1

优点:  排序速度快,时间复杂度不会根据数值的变化而变化,算法稳定

缺点:需要额外的辅助空间,会多用一倍的内存

/**
 * 归并排序
 * @param arr 要排序的数组
 * @param left 数组起始下标
 * @param right 数组结束下标
 * @param temp 附加数组,长度与要排序数组相同即可
 */
public static void mergeSort(int[] arr,int left,int right,int[] temp) {
	if(left < right) {
		int mid = (left + right)/2;
		//分左边
		mergeSort(arr,left,mid,temp);
		//分右边
		mergeSort(arr,mid + 1,right,temp);
		//合并
		merge(arr,left,mid,right,temp);	
	}
}
//合并方法
public static void merge(int[] arr, int left, int mid, int right, int[] temp) {
	int i = left ; //左边分组的起始位置
	int j = mid + 1 ; //右边分组的起始位置
	int t = 0;//附加数组的元素下标
	//把两组数据,从小到大拷贝到附加数组
	while(i<=mid && j<=right) {//两组数据各自有序,所以从小到大比较,然后拷贝
		if(arr[i] < arr[j]) {
			temp[t] = arr[i];
			t++ ;
			i++ ;
		}else {
			temp[t] = arr[j];
			t++ ;
			j++ ;
		}
	}
	//两组数据比较并拷贝,总有一组会提前拷贝完,然后就把剩余的部分拷贝到附加数组中
	while(i<=mid) {
		temp[t] = arr[i];
		t++ ;
		i++ ;
	}
	while(j<=right) {
		temp[t] = arr[j];
		t++ ;
		j++ ;
	}
	//把刚才拷贝到附加数组的有序序列拷贝回原始数组
	t = 0;
	while(left <= right) {
		arr[left] = temp[t];
		left++ ;
		t++ ;
	}
}

堆排序

基于完全二叉树的顺序存储

满二叉树:除最后一层无任何子节点外,每一层上的所有结点都有两个子结点的二叉树

完全二叉树:完全二叉树是由满二叉树而引出来的。对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树

对于完全二叉树

  • 第n个节点的左子节点下标为 (2 * n + 1)
  • 第n个节点的右子节点下标为 (2 * n + 2)
  • 第n个节点的父节点下标为 (n-1) / 2 
  • 节点总数为n的完全二叉树,非叶子节点为 n / 2 - 1 

大顶堆:所有节点的子节点都不大于父节点(适用于升序排序)

小顶堆:所有节点的子节点都不小于父节点(适用于降序排序)

算法思想: 

把完全二叉树调节成大顶堆,然后把根节点的数值与完全二叉树的最后一个节点交换,之前已经交换过的节点不再交换,它已经是有序的序列,然后再重新构建大顶堆,因为之前已经构建过了大顶堆,只是交换了根节点,所以只需要调节根节点以及调节根节点时被影响的子节点。每完全调节成为大顶堆之后,就会把最大的元素调节到根节点,然后交换到最后。

public static void heapSort(int[] arr) {
	
	//一颗n个节点的完全二叉树,有n/2-1个非叶子节点,调节每一个节点为大顶堆形式,从后往前调节
	for(int i=arr.length/2-1; i>=0; i--) { 
		getBigTopHeap(arr,i,arr.length); 
	}
	for(int j=arr.length-1; j>0; j--) {
		swap(arr,j,0);  //堆顶元素与最后一个节点交换
		//调节根节点为大顶堆的形式,并且会继续调整受影响的子节点
		//因为之前已经时大顶堆的形式,根节点交换后,
		//只有根节点不符合,只需要调节根节点与调节根节点受影响的子节点
		getBigTopHeap(arr,0,j);
	}
}
/**
 * 调节当前节点为大顶堆的形式,也就是左右子节点都比它小,并且会继续调整受影响的子节点
 * @param arr 要排序的数组
 * @param index 调节当前节点为大顶堆的形式,也就是左右子节点都比它小
 * @param len 要排序的长度,因为当二叉树完全就是一个大顶堆的时候,
 * 			    最大的元素就是根节点,也就是已经排好一个数,len就会减1
 */
public static void getBigTopHeap(int[] arr,int index,int len) {
	for(int j = 2*index+1; j < len ; j = j*2 + 1) {
		if( (j+1) < len && arr[j] < arr[j+1] ) {//左子节点比右子节点小
			j++ ; //让j指向右子节点
		}//结果j总是指向数值较大的那个节点
		if( arr[j] > arr[index]) { //如果子节点大于父节点
			swap(arr,j,index); //交换两个元素
			//指向交换数值的子节点,因为它交换数值之后,可能无法保证它比它的子节点都大,所以需要
			//再把它左右节点以及它本身最大的那个元素调上来。
			index = j; 
		}else {
			return;  //父节点最大时无需调整
		}
	}
}
/**
 * 交换数组中的两个元素
 * @param arr 目标数组
 * @param i 元素1的下标
 * @param j 元素2的下标
 */
public static void swap(int[] arr,int i,int j) {
	int temp = arr[i];
	arr[i] = arr[j];
	arr[j] = temp;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值