算法排序(快速排序/冒泡排序/直接插入排序/堆排序等)汇总,原理,图解及代码案例


常用排序的时间及空间复杂度


一、快速排序
原理:
快速排序使用分治法策略把一个序列分成两个子序列,再简单来说,就是分成左右两个区,选择一个基数(固定数或者根据索引选择第一个),把大于这个基数的放在右边,小于放在左边,这里有个知识点要注意:
图片来源网络:


举个?
{31,78,29,10,96,65,12,46}
把数组第一个当作基数,从右开始执行一趟排序得到
{12 10 29 31 96 65 78 46}
1.如果当前被选中的值小于基数
从左向右填充
2.如果当前被选中的值等于基数
不移动
3.如果当前被选中的值大于基数
从最右往左填充
一趟快速排序的算法是:

1)设置两个变量i、j,排序开始的时候:i=0,j=N-1;

2)以第一个数组元素作为关键数据,赋值给key,即key=A[0];

3)从j开始向前搜索,即由后开始向前搜索(j--),找到第一个小于key的值A[j],将A[j]和A[i]互换;

4)从i开始向后搜索,即由前开始向后搜索(i++),找到第一个大于key的A[i],将A[i]和A[j]互换;

5)重复第3、4步,直到i=j; (3,4步中,没找到符合条件的值,即3中A[j]不小于key,4中A[i]不大于key的时候改变j、i的值,使得j=j-1,i=i+1,直至找到为止。找到符合条件的值,进行交换的时候i, j指针位置不变。另外,i==j这一过程一定正好是i+或j-完成的时候,此时令循环结束)
代码案例:

package com.portal;

import java.util.Arrays;

/**
 * 快速排序算法
 * @author xiaobei
 *
 */
public class QuickSort {
	/**
	 * 
	 * @param arr 要排序的数组
	 * @param low 区间开始地址 也就是索引
	 * @param high 区间结束地址
	 */
	public static void quickSort(int[] arr,int low,int high	) {
		int l = low; //区间开始地址
		int h = high;//区间结束地址
		
		if (l > h) {//防止索引越界
			return;
		}
		int temp = arr[low]; //数组的第一个值作为基数
		//1.创建l=0;h=5;temp=6
		//2.l=0;h=3;temp=6
		while (l < h) {
			//从右开始找出最小的数
			while (l < h && arr[h] > temp) {
				h--;
			}
			//从左开始找出最大的数
			while (l<h && arr[l] <= temp) {
				l++;
			}
			if (l<h) { //交换
				int swap = arr[l];
				arr[l] = arr[h];
				arr[h] = swap;
			}
		}
		//交换temp
		temp = arr[l];
		arr[l] = arr[low];
		arr[low] = temp;
		
		//对左边进行递归排序
		quickSort(arr, low, l - 1);
		
		//对右边进行递归排序
		quickSort(arr, l + 1, high);
	}

	public static void main(String[] args) {
		int[] arr={31,78,29,10,96,65,12,46};
		System.out.println(Arrays.toString(arr));
		quickSort(arr,0,arr.length - 1);
		System.out.println(Arrays.toString(arr));
		
	}
}

 

二、冒泡排序
原理:
每次比较两个相邻的元素,将较大的元素交换至右侧。

基本思想:每次冒泡排序都会将两个相邻的元素进行比较,看是否满足大小要求,如果不满足,就交换这两个相邻元素的次序,一次冒泡至少让一个元素移动到它应该排列的位置,重复N次,就完成了冒泡排序。

图片来源网络:【注意:图中每一竖列是一次比较交换】

代码案例:

public static void main(String[] args) {
		int[] arr = {8,12,4,23,6,7,25,10,2};
		for (int i = 1; i < arr.length; i++) {
			for (int j = 0; j < arr.length - i; j++) {
				if (arr[j] < arr[j + 1]) {
					int temp = arr[j];
					arr[j] = arr[j + 1];
					arr[j + 1] = temp;
				}
			}
		}
		System.out.println("************排序后:****************");
		System.out.print("\t"+Arrays.toString(arr));
	}

 三、直接插入排序

原理:每次从无序表中取出第一个元素,把它插入到有序表的合适位置,使有序表依然有序。

算法描述:

对一个有n个元素的数据序列,排序需要进行n-1趟插入操作:

第1趟插入,将第2个元素插入前面的有序子序列--此时前面只有一个元素,当然是有序的。

第2趟插入,将第3个元素插入前面的有序子序列,前面2个元素是有序的。

第n-1趟插入,将第n个元素插入前面的有序子序列,前面n-1个元素是有序的。

图片来源于网络:

代码案例:

public static void main(String[] args) {
		int[] data = {13,7,4,23,8,7,3,9,10,21};
		System.out.print("\r\n排序前:");
		System.out.print("\t" + Arrays.toString(data));
		for (int i = 1; i < data.length; i++) {
			int temp  = data[i];
			if (data[i] < data[i-1]) {
				int j = i-1;
				while (j >= 0 && data[j] > temp) {
					data[j + 1] = data[j];
					j--;
				}
				data[j + 1] = temp;
			}
		}
		System.out.print("\r\n排序后:");
		System.out.print("\t" + Arrays.toString(data));
	}

四、堆排序

什么是堆

堆通常是一个可以被看做一棵树的数组对象。

堆的时间复杂度O(N*logN),额外空间复杂度O(1),是一个不稳定的排序

堆的数据结构

大根堆:每个结点的值都大于其左孩子和右孩子结点的值

小根堆:每个结点的值都小于其左孩子和右孩子结点的值

如图:

 

上图结构映射成数组

 

完全二叉树有个特性

左边子节点位置=当前父节点的2倍+1

右边子节点位置=当前父节点的2倍+2

父:A编号

左:2A + 1

右:2A + 2

知识点:一般升序采用大顶堆,降序采用小顶堆。

代码案例:逻辑有点难理解,可以多写几遍并且跟着代码看

package com.portal;

import java.util.Arrays;

/**
 * 堆排序
 * @author xiaobei
 *
 */
public class HeapSort {
	
	public static void main(String[] args) {
		int[] arr = {5,1,9,10,4,6,18,21,3,7};
		sort(arr);
		System.out.println(Arrays.toString(arr));
	}
	public static void sort(int[] arr) {
		//1.构建大顶堆
		for (int i = arr.length/2-1; i >= 0; i--) {
			//1.1从第一个非叶子结点从下至上,从右至左调整结构
			addHeap(arr,i,arr.length);
		}
		//2.调整堆结构+交换堆顶元素和末尾元素
		for (int j = arr.length - 1; j > 0; j--) {
			swap(arr,0,j);//交换堆顶元素和末尾元素
			addHeap(arr, 0, j);//重新对堆进行调整
		}
		
	}
	public static void swap(int[] arr, int i, int j) {
		int temp = arr[i];
		arr[i] = arr[j];
		arr[j] = temp;
		
	}
	public static void addHeap(int[] arr, int i, int length) {
		int temp = arr[i];
		for (int j = i*2+1; j < length; j=j*2+1) {//从i结点的左子结点开始,也就是2i+1处开始
			if (j+1<length && arr[j]<arr[j+1]) {//如果左子结点小于右子结点,k指向右子结点
				j++;
			}
			if (arr[j] > temp) {//如果子结点大于父结点,将子结点值赋给父节点,不用交换
				arr[i] = arr[j];
				i=j;
			}else {
				break;
			}
		}
		arr[i] = temp;//将temp值放到最终的位置
		
	}
	

}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值