常见的排序算法

一、冒泡排序

// 外层循环控制从第几个数组元素开始
		for (int i = 0; i < num.length - 1; i++) { // i num[j + 1]) {
					// 通过引入变量a使前后交换顺序:1.把前面的num[j]交给一个变量a来记住;
					// 2.把后面的num[j+1]赋值给num[j];3.把变量a赋值给num[j+1]

					int a = num[j]; // 1.把前面的num[j]交给一个变量a来记住;
					num[j] = num[j + 1]; // 2.把后面的num[j+1]赋值给num[j];
					num[j + 1] = a; // 3.把变量a赋值给num[j+1]
				}
			}
		}

二、插入排序

// 插入排序
	public static int[] insertionSort(int[] arr) {
		int len = arr.length;
		for (int i = 1; i < len; i++) {
			// j表示当前元素的位置,将其和左边的元素比较,若当前元素小,就交换,也就相当于插入
			// 这样当前元素位于j-1处,j--来更新当前元素,j一直左移不能越界,因此应该大于0
			for (int j = i; j > 0 && arr[j] < arr[j - 1]; j--) {
				int temp = arr[j]; // 元素交换
				arr[j] = arr[j - 1];
				arr[j - 1] = temp;
			}
		}
		return arr;
	}

三、归并排序

// 分 与 和的方法  归并排序额外占用一个空间
	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);
		}
	}

	// 合并的方法
	/**
	 * 
	 * @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;// 初始化i,左边有序序列的初始索引
		int j = mid + 1;// 初始化j,右边有序序列的初始索引
		int t = 0;// temp数组的指针,指向temp数组

		// (一)
		// 先把左右两边(有序的)数据按照规则填充到temp数组中
		// 直到有一边处理完为止
		while (i <= mid && j <= right) {
			// 如果左边的元素小于右边的,将左边的拷贝到temp数组中
			// 同时i和t后移
			if (arr[i] < arr[j]) {
				temp[t] = arr[i];
				t++;
				i++;
			} else {// 反之,将右边的拷贝到temp中去
				temp[t] = arr[j];
				t++;
				j++;

			}
		}

		// (二)
		// 把有剩余的一边全部拷贝到temp中去
		while (i <= mid) {// 如果左边有剩余,将剩余的拷贝到temp中去
			temp[t] = arr[i];
			t++;
			i++;
		}

		while (j <= right) {// 如果右边有剩余,将剩余的拷贝到temp中去
			temp[t] = arr[j];
			t++;
			j++;
		}
		// (三)
		// 将temp数组中的元素全部拷贝到arr中
		// 并不是将所有的数据一下全部拷贝回去
		t = 0;
		int tempLeft = left;
		System.out.println("tempLeft = " + tempLeft + "  right = " + right);
		System.out.println(Arrays.toString(temp));
		while (tempLeft <= right) {
			// 第一次合并tempLeft = 0,right = 1;//第二次合并:tempLeft = 2,right = 3
			// 最后一次:tempLeft = 0,right = 7.
			arr[tempLeft] = temp[t];
			t++;
			tempLeft++;
		}
	}

四、快速排序

public static void quickSort(int[] arr, int left, int right) {
		// 递归退出条件
		if (left >= right) {
			return;
		}

		int l = left;
		int r = right;
		// 其中array[left]并未发生改变,是r和l在变
		while (r > l) {
			while (r > l && arr[r] >= arr[left]) { // 从右向左
				r--;
			}
			while (r > l && arr[l] <= arr[left]) { // 从左向右
				l++;
			}
			if (r == l) { // 说明第一次快排结束
				// 使基数(我们选的那个参照数,这里是指第一个数)到中间
				//
				int temp = arr[l];
				arr[l] = arr[left];
				arr[left] = temp;
			} else {
				// 引入变量temp作为中间值,使arr[l]和arr[r]交互
				// 最终还是要进入到r==l,结束第一次快排
				int temp = arr[r];
				arr[r] = arr[l];
				arr[l] = temp;
			}
		}
		// 递归调用
		quickSort(arr, left, l - 1);// 此时l和r相等
		quickSort(arr, r + 1, right);
	}

五、基数排序(与桶排序相似)

基数排序:稳定性排序,但是消耗的内存较大;对于正数使用,负数不适用

// 基数排序的方法
	public static void radixSort(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个桶,每个桶就是一个一维数组
		// 说明
		// 1、二维数组包含10个一维数组
		// 2、为了防止在放入的时候,数据溢出,则每一个一维数组(桶),大小定为arr.length
		// 3.很明确,基数排序是使用空间换时间的经典算法
		int[][] bucket = new int[10][arr.length];

		// 为了记录每个桶中,实际存放了多少个数据,我们定义一个一维数组来记录各个桶每次放入的数据个数
		// 可以这样理解:buckElementCounts[0]代表的就是第一个桶中存放了多少个数据
		int[] bucketElementCounts = new int[10];

		// 定义一个for循环
		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];
				bucketElementCounts[digitOfElement]++;
			}

			// 按照这个桶的顺序(一维数组的下标依次取出数据,放入原来的数组中)
			int index = 0;
			for (int k = 0; k < bucketElementCounts.length; k++) {
				// 如果桶内有数据,我们才放入到原数组
				if (bucketElementCounts[k] != 0) {
					// 循环第k个桶(即第k个一维数组),放入原数组中
					for (int l = 0; l < bucketElementCounts[k]; l++) {
						// 取出元素放入到arr中
						arr[index++] = bucket[k][l];
					}
				}

				// 第一轮处理后,将10个桶内寸的数据的个数的数据清零,即bucketElementCounts[k] = 0
				bucketElementCounts[k] = 0;
			}
		}

	}

六、选择排序

// 选择排序
	/**
	 * 原理: 第一轮比较:将第一个元素和后面的每一个元素比较,后面的哪一个元素比第一个元素小,则将他们交换位置,结果是数组的第一个元素为最小的
	 * 第二轮比较:以此类推,第二轮的结果是数组的第二个元素为第二小的元素 .......
	 * 
	 * @param args
	 */

	// 选择排序的代码实现
	public static void selectionSort(int[] arr) {
		for (int i = 0; i < arr.length; i++) {
			int index = i;// 将i记录下来,后面有用
			for (int j = 1 + i; j < arr.length; j++) {
				if (arr[index] > arr[j]) {
					int temp = arr[index];
					arr[index] = arr[j];
					arr[j] = temp;
				}
			}
		}
	}

七、希尔排序

基于插入排序,但是不是很稳定

public static void shellSort(int[] str) {

		if (str == null || str.length == 0) {
			return;
		}

		for (int temp = str.length / 2; temp >= 1; temp /= 2) {
			// 1.外边第一轮循环将0-->64比较,-4与-8,3与28,
			// 外边第一轮结束后,数组变成了0 -8 3 64 -4 28
			// 2.外边第二轮循环,将数组的第一个元素和第二个比较,若符合条件,则交换位置;
			// 再将第二个元素和第三个比较,依此类推.....
			for (int i = temp; i < str.length; i++) {
				int flag = str[i];
				int j = i - temp;
				while (j >= 0) {
					if (str[j] > flag) {
						str[j + temp] = str[j];
						j -= temp;
					} else {
						break;
					}
				}
				str[j + temp] = flag;
			}
		}
	}

未完待续...

你可能感兴趣的:(排序算法,算法)