排序算法介绍(更新中)

一、冒泡排序

  1. 基本介绍
    冒泡排序属于内部排序法,实现原理是重复扫描待排序的元素,并比较每一对相邻的元素,当该对元素顺序不正确时进行交换。一直重复这个过程,直到没有任何两个相邻元素可以交换,就表明完成了排序。

  2. 图例展示

  3. 代码实现

/**
 * 冒泡排序(从小到大)
 */
public class BubbleSort {

	public static void sort(int[] arr) {
		// 临时交换变量
		int temp = 0;
		// 标识变量,表示是否进行过交换
		boolean flag = false;

		for (int i = 0; i < arr.length - 1; i++) {

			for (int j = 0; j < arr.length - 1 - i; j++) {
				// 如果前面的数比后面的数大则交换
				if (arr[j] > arr[j + 1]) {
					flag = true;
					temp = arr[j];
					arr[j] = arr[j + 1];
					arr[j + 1] = temp;
				}
			}

			if (!flag) {
				// 在一趟排序中,一次交换都没有发生过,退出循环
				break;
			} else {
				// 重置flag,进行下次判断
				flag = false;
			}
		}
	}

}

二、选择排序

  1. 基本介绍
    选择排序属于内部排序法,是从待排序的元素中,按指定的规则选出某一元素,再依规定交换位置后达到排序的目的。

  2. 图例展示

  3. 代码实现

/**
 * 选择排序(从小到大)
 **/
public class SelectSort {

	public static void sort(int[] arr) {

		for (int i = 0; i < arr.length - 1; i++) {
			// 最小数的下标
			int minIndex = i;
			// 最小数的值
			int minVal = arr[i];
			for (int j = i + 1; j < arr.length; j++) {
				// 如果发现更小的数字,保存更小数字的下标和值
				if (minVal > arr[j]) {
					minVal = arr[j];
					minIndex = j;
				}
			}

			if (minIndex != i) {
				arr[minIndex] = arr[i];
				arr[i] = minVal;
			}

		}
	}

}

三、插入排序

  1. 基本介绍
    插入式排序属于内部排序法,基本思想是:把待排序的元素看成为一个有序表和一个无序表,排序过程中每次从无序表中取出第一个元素,把它的排序码依次与有序表元素的排序码进行比较,将它插入到有序表中的适当位置,使之成为新的有序表。

  2. 图例展示

  3. 代码实现

/**
 * 插入排序(从小到大)
 */
public class InsertSort {

	public static void sort(int[] arr) {
		// 定义待插入的数
		int insertVal;
		// 定义待插入数的下一个数的下标
		int insertIndex;
		for (int i = 1; i < arr.length; i++) {
			insertVal = arr[i];
			// 1. insertIndex >= 0 保证insertVal不越界
			// 2. insertVal < arr[insertIndex] 待插入的数,还没有找到插入位置
			// 3. arr[insertIndex + 1] = arr[insertIndex] 将 arr[insertIndex] 后移一位
			for (insertIndex = i - 1; insertIndex >= 0 && insertVal < arr[insertIndex]; insertIndex--) {
				arr[insertIndex + 1] = arr[insertIndex];// arr[insertIndex]
			}
			// 当退出while循环时,说明插入的位置找到,insertIndex + 1
			if (insertIndex + 1 != i) {
				arr[insertIndex + 1] = insertVal;
			}
		}
	}
}

四、希尔排序

  1. 基本介绍
    希尔排序属于内部排序法,它是插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序。希尔排序法基本思想是把待排序元素按下标的一定步长分组,对每组使用插入排序算法排序;随着增量逐渐减少,当增量减至1时,整个待排序的元素恰被分成一组,算法便终止。

  2. 图例展示

  3. 代码实现

/**
 * 希尔排序(从小到大)
 */
public class ShellSort {

	/**
	 * 希尔排序移位法
	 */
	public static void sort(int[] arr) {
		// 定义待插入的数
		int insertVal;
		// 定义待插入数的下一个数的下标
		int insertIndex;
		// 增量gap, 并逐步的缩小增量
		for (int gap = arr.length / 2; gap > 0; gap /= 2) {
			// 从第gap个元素,逐个对其所在的组进行直接插入排序
			for (int i = gap; i < arr.length; i++) {
				insertVal = arr[i];
				for (insertIndex = i - gap; insertIndex >= 0 && insertVal < arr[insertIndex]; insertIndex -= gap) {
					arr[insertIndex + gap] = arr[insertIndex];
				}

				if (insertIndex + gap != i) {
					arr[insertIndex + gap] = insertVal;
				}

			}
		}
	}

}

五、快速排序

  1. 基本介绍
    快速排序属于内部排序法,它是冒泡排序的一种改进算法。基本思想是:通过一趟排序将待排序的元素分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个待排序的元素变成有序序列。

  2. 图例展示

  3. 代码实现

/**
 * 快速排序(从小到大)
 */
public class QuickSort {
	
	public static void sort(int[] arr, int low, int high) {
		if (low >= high) {
			return;
		}
		int i = low;
		int j = high;
		// pivot就是基准位
		int pivot = arr[low];

		while (i < j) {
			// 右边依次往左递减
			while (pivot <= arr[j] && i < j) {
				j--;
			}
			// 左边依次往右递增
			while (pivot >= arr[i] && i < j) {
				i++;
			}
			// 满足条件则交换
			if (i < j) {
				int temp = arr[j];
				arr[j] = arr[i];
				arr[i] = temp;
			}

		}
		// 最后基准为low与i的数字交换,如果相同则不交换
		if(i != low) {
			arr[low] = arr[i];
			arr[i] = pivot;
		}
		
		// 递归调用左半数组
		sort(arr, low, j - 1);
		// 递归调用右半数组
		sort(arr, j + 1, high);
	}

}

六、归并排序

  1. 基本介绍
    归并排序属于外部排序法,该算法采用经典的分治策略,分的阶段是将问题分解成一些小的问题然后递归求解,而治的阶段则将分的阶段得到的各答案"修补"在一起。

  2. 图例展示

  3. 代码实现

/**
 * 归并排序(从小到大)
 */
public class MergetSort {

	public static void sort(int[] arr, int left, int right, int[] temp) {
		if (left < right) {
			int mid = (left + right) / 2; // 中间索引
			// 向左递归进行分解
			sort(arr, left, mid, temp);
			// 向右递归进行分解
			sort(arr, mid + 1, right, temp);
			// 合并
			merge(arr, left, mid, right, temp);

		}
	}

	/**
	 * 合并的方法
	 * 
	 * @param arr   排序的原始数组
	 * @param left  左边有序序列的初始索引
	 * @param mid   中间索引
	 * @param right 右边索引
	 * @param temp  做中转的数组
	 */
	public static void merge(int[] arr, int left, int mid, int right, int[] temp) {

		int i = left; // 左边有序序列的初始索引
		int j = mid + 1; // 右边有序序列的初始索引
		int tempIndex = 0; // 指向temp数组的当前索引

		// 把左右两边(有序)的数据按照规则填充到temp数组,直到左右两边的有序序列,有一边处理完毕为止
		while (i <= mid && j <= right) {
			if (arr[i] <= arr[j]) {
				temp[tempIndex++] = arr[i++];
			} else {
				temp[tempIndex++] = arr[j++];
			}
		}

		// 把有剩余数据的一边的数据依次全部填充到temp
		while (i <= mid) {
			temp[tempIndex++] = arr[i++];
		}
		while (j <= right) {
			temp[tempIndex++] = arr[j++];
		}

		// 将temp数组的元素拷贝到arr,注意:并不是每次都拷贝所有
		tempIndex = 0;
		int tempLeft = left; 
		while (tempLeft <= right) {
			arr[tempLeft++] = temp[tempIndex++];
		}

	}

}

七、基数排序

  1. 基本介绍
    基数排序属于外部排序法,它是通过键值的各个位的值,将要排序的元素分配至某些"桶"中,达到排序的作用。基数排序是经典的空间换时间的方式,占用内存很大, 当对海量数据排序时,容易造成 OutOfMemoryError 。

  2. 图例展示

  3. 代码实现

/**
 * 基数排序(从小到大)
 */
public class RadixSort {

	public static void sort(int[] arr) {

		// 得到数组中最大的数的位数
		int max = arr[0];
		for (int i = 1; i < arr.length; i++) {
			if (arr[i] > max) {
				max = arr[i];
			}
		}
		int maxLength = (max + "").length();

		// 定义一个二维数组,表示10个桶,每个桶是一个一维数组
		int[][] bucket = new int[10][arr.length];

		// 定义一个一维数组来记录各个桶的数据个数
		int[] bucketElementCounts = new int[10];

		for (int i = 0, n = 1; i < maxLength; i++, n *= 10) {
			// 针对每个元素的对应位进行排序处理
			for (int j = 0; j < arr.length; j++) {
				int digitOfElement = arr[j] / n % 10;
				bucket[digitOfElement][bucketElementCounts[digitOfElement]++] = arr[j];
			}

			int index = 0;
			// 将桶中的数据放入到原数组
			for (int k = 0; k < bucketElementCounts.length; k++) {
				if (bucketElementCounts[k] != 0) {
					for (int l = 0; l < bucketElementCounts[k]; l++) {
						arr[index++] = bucket[k][l];
					}
				}
				// 第i+1轮处理后,需要将每个 bucketElementCounts[k] = 0 !!!!
				bucketElementCounts[k] = 0;
			}
		}

	}
}

八、排序算法总结

n:数据规模
k:桶的个数

In-place:不占用额外内存
Out-place:占用额外内存

时间复杂度: 一个算法执行所耗费的时间
空间复杂度:运行完一个程序所需内存的大小

稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面
不稳定:如果a原本在b的前面,而a=b,排序之后a可能会出现在b的后面

内排序:所有排序操作都在内存中完成
外排序:由于数据太大,因此把数据放在磁盘中,而排序通过磁盘和内存的数据传输才能进行

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值