常见的排序算法

1、冒泡排序

  • 思想
    遍历数组,对当前尚未排好序的范围内的全部数,从前往后对相邻的两个数依次进行比较和调整,较大的数往后沉,较小的数往前冒,每一趟遍历,都会沉下一个最大的数,已沉下的数无需再参与比较,所以每一轮比较的次数也逐渐减少。
  • 实现
public class Demo {
	
	public static void main(String[] args) {
		int[] a = {3,-1,9,7,-5,4,13,6,2};
		System.out.println(Arrays.toString(a));
		//bubble(a);
		bubble2(a);
		System.out.println(Arrays.toString(a));
	}

	// (1) 从前往后,大数往下沉
	private static void bubble(int[] a) {
		for(int i=0;i<a.length-1;i++) {
			for(int j=0;j<a.length-1-i;j++) {
				if(a[j]>a[j+1]) {
					swap(a,j,j+1);
				}
			}
		}
	}
	
	// (2) 从后往前,小数往上冒
	private static void bubble2(int[] a) {
		for(int i=0;i<a.length-1;i++) {
			for(int j=a.length-1;j>i;j--) {
				if(a[j]<a[j-1]) {
					swap(a, j, j-1);
				}
			}
		}
	}
	
	/**
	 * 交换数组中两个索引位置的值
	 */
	private static void swap(int[] a, int m, int n) {
		int t = a[m];
		a[m] = a[n];
		a[n] = t;
	}
}
  • 性能
    • 比较次数 :n(n-1)/2
    • 时间复杂度:O(n^2) 平方级

2、选择排序

  • 思想
    首先,找到数组中最小的元素,将它与数组中第一个元素交换位置;再次,在剩下的元素中找到最小的元素,将它与数组中第二个元素交换位置,如此重复,直至整个数组有序。
  • 实现
public class Demo {
	
	public static void main(String[] args) {
		int[] a = {3,-1,9,7,-5,4,13,6,2};
		System.out.println(Arrays.toString(a));
		selection(a);
		System.out.println(Arrays.toString(a));
	}

	private static void selection(int[] a) {
		for(int i=0;i<a.length-1;i++) {
			// 假设第i个最小
			int min = i;
			// 遍历数组与最小值比较
			for(int j=i+1;j<a.length;j++) {
				if(a[j]<a[min])
					// 记录最小值的索引
					min = j;
			}
			// 优化: 如果原先假定的就是最小值,无需交换
			if(min!=i)
				swap(a, min, i);
		}
	}

	/**
	 * 交换数组中两个索引位置的值
	 */
	private static void swap(int[] a, int m, int n) {
		int t = a[m];
		a[m] = a[n];
		a[n] = t;
	}
}

  • 性能
    • 交换 N 次
    • 比较 n(n-1)/2 次
    • 时间复杂度 O(n^2) 平方级

3、插入排序

  • 思想
    在待排的元素中,假设前面 n-1 个数已经是有序的,现将第 n 个数插入排好序的序列中,调整位置,使得插入第 n 个数的序列也是有序的。
  • 实现
public class Demo {
	
	public static void main(String[] args) {
		int[] a = {3,-1,9,7,-5,4,13,6,2};
		System.out.println(Arrays.toString(a));
		insertion(a);
		System.out.println(Arrays.toString(a));
	}

	private static void insertion(int[] a) {

		for(int i=1;i<a.length;i++) {
			// 目标元素起始位置
			int index = i;
			for(int j=i-1;j>=0;j--) {
				if(a[index]>=a[j])
					break;
				else {
					swap(a, j, index);
					// 交换之后,目标元素的位置发生变化
					index = j;
				}
			}
		}
	}

	/**
	 * 交换数组中两个索引位置的值
	 */
	private static void swap(int[] a, int m, int n) {
		int t = a[m];
		a[m] = a[n];
		a[n] = t;
	}
}

  • 性能
    • 数组有序的情况:比较 n-1 次,交换 0 次,时间复杂度 O(n)
    • 数组杂乱的情况:O(n^2)
    • 时间复杂度:O(n)~ O(n^2)

4、快速排序

  • 思想
    分治 的排序算法,先取一个切分元素(一般取第一个或最后一个元素),然后从数组的左端向右扫描直至找到一个大于等于它的元素,再从数组的右端开始向左扫描,直至找到一个小于等于它的元素,交换左右指针位置的元素,继续两端扫描,直至左右指针相遇,把切分元素放至该位置,递归 进行切分元素两边的数组,重复以上步骤。
  • 实现
public class Demo {

	public static void main(String[] args) {
		
		int[] a = {3, -1, 9, 7, -5, 4, 13, 6, 2};
		System.out.println(Arrays.toString(a));

		quick(a);

		System.out.println(Arrays.toString(a));
	}

	/**
	 * 快速排序
	 * @param a
	 */
	private static void quick(int[] a) {
		quick(a,0,a.length-1);
	}

	private static void quick(int[] a, int start, int end) {
		// 递归收敛条件
		if(start>=end)
			return;
		
		// 左右指针
		int left = start;
		int right = end;
		
		// 选最后一个元素作为 轴/切分元素
		int p = a[end];
		
		// 左右指针移动,相遇后,获得轴的正确位置
		while(left<right) {
			
			while(left<right && a[left]<p)
				left++;
			
			while(left<right && a[right]>=p)
				right--;
			
			swap(a,left,right);
		}
		// 把轴放到正确位置
		int pIndex = left;
		swap(a, pIndex, end);
		
		quick(a,0,pIndex-1);
		quick(a, pIndex+1, end);
	}

	/**
	 * 交换数组中两个索引位置的值
	 * @param a
	 * @param i
	 * @param j
	 */
	private static void swap(int[] a, int i, int j) {
		int tmp = a[i];
		a[i] = a[j];
		a[j] = tmp;
	}
}

  • 性能
    • 时间复杂度 :0(n*logN) 线性对数级

5、归并排序

  • 思想
    归并排序就是将两个或两个以上有序表合并成一个新的有序表,即把待排序的序列分为若干个子序列,每个子序列都是有序的,然后再把有序子序列合并为整体有序序列。
  • 实现
public class Demo {

	public static void main(String[] args) {
		
		int[] a = {3, -1, 9, 7, -5, 4, 13, 6, 2};
		System.out.println(Arrays.toString(a));

		merge(a);

		System.out.println(Arrays.toString(a));
	}

	/**
	 * 自上而下的归并排序
	 * @param a
	 */
	// 归并所需的辅助数组
	private static int[] tempArr; 
	private static void merge(int[] a) {
		// 一次性分配空间
		tempArr = new int[a.length];
		merge(a,0,a.length-1);
	}
	
	private static void merge(int[] a, int start, int end) {
		// 递归终止条件
		while(end<=start)
			return;
		
		int mid = start + (end-start)/2;
		// 左半边有序
		merge(a,start,mid);
		// 右半边有序
		merge(a,mid+1,end);
		// 原地归并
		merge(a,start,mid,end);
	}
	// 原地归并
	private static void merge(int[] a, int start, int mid, int end) {
		
		int i = start;
		int j = mid+1;
		// 先将 a[start...end] 先复制到 辅助数组中
		for(int k=start;k<=end;k++) {
			tempArr[k] = a[k];
		}
		for(int k=start;k<=end;k++) {
			// 左半边用尽,取右半边元素
			if(i>mid)
				a[k] = tempArr[j++];
			// 右半边用尽,取左半边元素
			else if(j>end)
				a[k] = tempArr[i++];
			// 右半边的元素小,取右半边元素
			else if(tempArr[j]<tempArr[i])
				a[k] = tempArr[j++];
			// 左半边元素小,取左半边元素
			else
				a[k] = tempArr[i++];
		}
	}

}

  • 性能
    • 递归深度 :logN
    • 每次归并的平均时间复杂度:O(n)
    • 时间复杂度:O(nlogN) 线性对数级
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值