这里写目录标题

  • 交换算法之冒泡排序
  • 交换算法之快速排序
  • 插入算法之插入排序
  • 插入排序之希尔排序
  • 选择排序之简单选择排序
  • 排序算法之归并排序
  • 排序算法之基数排序
  • 常用排序算法之堆排序


交换算法之冒泡排序

冒泡排序:冒泡排序是一种简单的排序算法,也是笔试题中出现最多的一种排序算法题。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。(‘排序动画’)

算法描述:

  • 从一个元素开始,两两比较,如果左边元素比右边大,就交换他们的位置。
  • 对每一个相邻元素做相同的工作,这样第一轮比较之后最大的元素就会在最右边。
  • 下一轮做重复的操作,但是不用比较最后一个元素。
  • 重复以上操作,知道排序完成。
//冒泡排序
public class BubbleSort {
	public static void main(String[] args) {
		int[] arr = new int[] {2,9,7,5,1,8,4,7,4,9};
		System.out.println(Arrays.toString(arr));
		BubbleSort(arr);
		System.out.println(Arrays.toString(arr));
	}

	private static void BubbleSort(int[] arr) {
		//比较length - 1轮
		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]) {
					//交换位置
					arr[j] = arr[j] ^ arr[j + 1];
					arr[j + 1] = arr[j] ^ arr[j + 1];
					arr[j] = arr[j] ^ arr[j + 1];
				}
			}
		}
	}
}

交换算法之快速排序

快速排序:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。(‘排序动画’)

算法描述

  • 设置数组第一个数为标准数,设置一个开始下标start和结束下标end
  • 拿结束下标指向的最后一个元素和标准数相比,如果比标准数大,下标就减一end–,指向倒数第二个元素,如果比标准数小,就用end指向的元素,替换掉start指向的元素
  • 拿开始下标指向的第一个元素和标准数相比,如果比标准数小,下标就加一start++,指向第二个元素,如果比标准数大,就用start指向的元素,替换掉end指向的元素
  • 重复23,开始下标和结束下标都重合,把标准数赋值到重合的位置,这样就得到了前部分的所有数比标准数小,后一部分的所有数比标准数大
  • 然后分别将前后的作为整体,再进行1234步骤,一次循环递归就会得到顺序数组
  • 注意:如果只有以上步骤没有条件判断就会死循环,所有需要加一个判断即,当开始下标小于结束下标时,才能比较排序
//快速排序
public class QuickSort {
	public static void main(String[] args) {
		int[] arr = new int[] {3,6,9,8,4,7,1,0,7,5,9,4};
		quickSort(arr,0,arr.length - 1);
		System.out.println(Arrays.toString(arr));
	}

	private static void quickSort(int[] arr, int start, int end) {
		//递归结束的条件,开始的位置和结束的位置之间要有元素
		if(start < end) {
			//把数组中第0个数作为标准数
			int pivot = arr[start];
			//记录需要排序的下标
			int low = start;
			int high = end;
			//循环找出比标准数大的数和比标准数小的数
			while(low < high) {
				//如果右边的数字比标准数大
				while(low < high && arr[high] >= pivot) {
					high--;
				}
				//如果右边的数字比标准数的小,就把左边的数字替换成右边的数字
				arr[low] = arr[high];
				//如果左边的数字比标准数小
				while(low < high && arr[low] <= pivot) {
					low++;
				}
				//如果左边的数字比标准数大,就把右边的数字替换成左边的数字
				arr[high] = arr[low];
			}
			//最后低位和高位重合,一轮循环结束,把标准数赋个重合的这个位置数
			arr[low] = pivot;
			//处理所有比标准数小的数字
			quickSort(arr, start, low);
			//处理所有比标准数大的数字
			quickSort(arr, low + 1, end);
		}
	}	
}

插入算法之插入排序

插入排序:插入排序(Insertion-Sort)的算法描述是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。(‘排序动画’)

算法描述

  • 从一个元素开始遍历(从前往后),如果当前元素比前一个元素大就继续遍历
  • 如果当前元素比前一个元素小,记录当前元素
  • 遍历当前元素前的所有元素(从后往前),如果当前元素比前一个元素小,那么前一个元素往后移一位,继续往前比较知道当前元素比前一个元素大,就把记录的元素放在当前位置
  • 知道第一步遍历结束,排序就完成了
//插入排序
public class InsetSort {
	public static void main(String[] args) {
		int[] arr = new int[] {2,7,9,4,7,3,8,1,0,5,8};
		insertSort(arr);
		System.out.println(Arrays.toString(arr));
	}

	private static void insertSort(int[] arr) {
		//遍历所有数字
		for (int i = 1; i < arr.length; i++) {
			//如果当前数字比前一个小
			if(arr[i] < arr[i - 1]) {
				//把当前遍历的数字存起来
				int temp = arr[i];
				int j;
				//遍历当前数字前面所有数字
				for (j = i - 1; j >= 0 && temp < arr[j]; j--) {
					//把前一个数字赋给后一个数字
					arr[j + 1] = arr[j];
				}
				//把临时变量(外层for循环的当前元素)赋给不满足条件的后一个元素
				arr[j + 1] = temp;
			}
		}
	}
}

插入排序之希尔排序

前言:假设有一个数组,数组元素为[3,4,5,6,7,2],如果这个数组用插入排序的话,排序的效率比较低。(‘排序动画’)

希尔排序:1959年Shell发明,第一个突破O(n2)的排序算法,是简单插入排序的改进版。它与插入排序的不同之处在于,它会优先比较距离较远的元素。希尔排序又叫缩小增量排序

算法描述

  • 将元素组的长度除以2得到步长d,根据步长将数组分组(例如:第一个元素是第一个arr[1],那么和第一个元素同组的第二个元素就是arr[1 + d],依次分组)
  • 分组的组内元素进行插入排序,第一次分组结束
  • 第二次分组再将步长除以2,再将组内的元素进行插入排序
  • 重复123知道步长为0,在进行插入排序后,排序完成
//希尔排序
public class ShellSort {
	public static void main(String[] args) {
		int[] arr = new int[] {1,7,9,3,7,9,0,3,5,7,2};
		shellSort(arr);
		System.out.println(Arrays.toString(arr));
	}

	private static void shellSort(int[] arr) {
		int k = 1;
		//遍历所有步长 
		for (int d = arr.length / 2; d > 0; d /= 2) {
			//遍历所有元素
			for (int i = d; i < arr.length; i++) {
				//遍历本组所有元素
				for (int j = i - d; j >= 0; j -= d) {
					//如果当前元素大于加上步长后的那个元素
					if(arr[j] > arr[j + d]) {
						arr[j] = arr[j] ^ arr[j + d];
						arr[j + d] = arr[j] ^ arr[j + d];
						arr[j] = arr[j] ^ arr[j + d];
					}
				}
			}
			System.out.println("第" + k + "次排序后的结果:" + Arrays.toString(arr));
			k++;
		}
	}
}

选择排序之简单选择排序

选择排序:选择排序(Selection-sort)是一种简单直观的排序算法。它的工作原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

算法描述

  • 先假设第一个为最小,依次遍历比较,如果比标记的数小就交换位置
  • 重复第一步知道排序结束
//选择排序
public class SelectSort {
	public static void main(String[] args) {
		int[] arr = new int[] {1,6,8,4,5,8,2,9,3};
		selectSort(arr);
		System.out.println(Arrays.toString(arr));
	}

	private static void selectSort(int[] arr) {
		//遍历所有数
		for (int i = 0; i < arr.length; i++) {
			int minIndex = i;
			//把当前的数依次后后面的数比较,记录最小的数
			for (int j = i + 1; j < arr.length; j++) {
				//如果后面的数比记录的数小
				if(arr[j] < arr[minIndex]) {
					minIndex = j;
				}
			}
			//如果最小的数和当前遍历数的下标不一致,说明下表为minIndex的数比当前遍历的数更小
			if(i != minIndex) {
				arr[i] = arr[i] ^ arr[minIndex]; 
				arr[minIndex] = arr[i] ^ arr[minIndex]; 
				arr[i] = arr[i] ^ arr[minIndex]; 
			}
		}
	}
}

排序算法之归并排序

归并排序:归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。

算法描述

  • 把长度为n的输入序列分为两个长度为n/2的子序列;
  • 对这两个子序列采用归并排序;
  • 将两个排序好的子序列合并成一个最终的排序序列。
public class MertgeSort {

	public static void main(String[] args) {
		int[] arr = new int[] {1,3,5,2,4,6,8};
		System.out.println(Arrays.toString(arr));
		mergeSort(arr, 0, arr.length - 1);
		System.out.println(Arrays.toString(arr));
	}
	
	//归并排序
	public static void mergeSort(int[] arr,int low ,int high) {
		int middle = (low + high) / 2;
		if (low < high) {
			//处理左边
			mergeSort(arr, low, middle);
			//处理右边
			mergeSort(arr, middle + 1, high);
			//归并
			merge(arr, low, middle, high);
		}
	}
	
	public static void merge(int[] arr,int low,int middle,int high) {
		//用于存储归并后的临时数组
		int[] temp = new int[high - low + 1];
		//记录第一个数组中需要遍历的下标
		int i = low;
		//记录第二个数组中需要遍历的下标
		int j = middle + 1;
		//用于记录在临时数组中存放的下标
		int index = 0;
		//遍历两个数字取出小的数字,放在临时数组中
		while(i <= middle && j <= high) {
			//第一个数组的数据更小
			if(arr[i] < arr[j]) {
				//把小的数字放入临时数组中
				temp[index] = arr[i];
				i++;
				index++;
			}else {
				temp[index] = arr[j];
				j++;
				index++;
			}
		}
		//处理多余的数据
		while(j <= high) {
			temp[index] = arr[j];
			j++;
			index++;
		}
		while(i <= middle) {
			temp[index] = arr[i];
			i++;
			index++;
		}
		//把临时数组中的值传到原数组
		for (int k = 0; k < temp.length; k++) {
			arr[k + low] = temp[k];
		}
	}
}

排序算法之基数排序

基数排序:基数排序是按照低位(个位)先排序,然后收集;再按照高位(十位百位等取决于最大数)排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序。最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前。

算法描述

  • 取得数组中的最大数,并取得位数;
  • arr为原始数组,从最低位开始每个位组成radix数组;
  • 对radix进行计数排序(利用计数排序适用于小范围数的特点)。
public class RadixSort {

	public static void main(String[] args) {
		int[] arr = new int[] {23,6,189,45,9,287,56,1,789,34,65,652,5};
		radixSort(arr);
		System.out.println(Arrays.toString(arr));
	}
	
	public static void radixSort(int[] arr) {
		//存数组中最大的数字
		int max = Integer.MAX_VALUE;
		for (int i = 0; i < arr.length; i++) {
			if (arr[i] > max) {
				max = arr[i];
			}
		}
		//计算最大数字是几位数
		int length = (max + "").length();
		//用于临时存储数据的数组
		int[][] temp = new int[10][arr.length];
		int[] counts = new int[arr.length];
		//根据最大长度的数决定比较的次数
		for (int i = 0,n = 1; i < length; i++,n*=10) {
			//把每一个数字分别计算余数
			for (int j = 0; j < arr.length; j++) {
				//计算余数
				int ys = arr[j]/n%10;
				//把当前遍历的数据放入指定二维数组
				temp[ys][counts[ys]] = arr[j];
				//记录数量
				counts[ys]++;
			}
			//记录取的元素需要放的位置
			int index = 0;
			//把数字取出来
			for (int k = 0; k < counts.length; k++) {
				//记录数量的数组中当前余数记录的数量不为0
				if(counts[k] != 0) {
					//循环取出元素
					for (int l = 0; l < counts[k]; l++) {
						//取出元素
						arr[index] = temp[k][l];
						//记录下一个位置
						index++;
					}
					//把数量设置为0
					counts[k] = 0;
				}
			}
		}
	}
}

常用排序算法之堆排序

大顶堆:父节点都大于子节点

小顶堆:父节点都小于子节点

升序排序用大顶堆,降序排序用小顶堆

public class HeapSort {
	public static void main(String[] args) {
		int[] arr = new int[] {9,6,8,7,0,1,10,4,2};
		heapSort(arr);
		System.out.println(Arrays.toString(arr));
	}
	
	public static void heapSort(int[] arr) {
		//开始位置是最后一个非叶子节点,即最后一个节点的父节点
		int start = (arr.length - 1)/2;
		//调整为大顶堆
		for (int i = start; i >= 0; i--) {
			maxHeap(arr, arr.length, i);
		}
		//先把数组中的第0个和堆中的最后一个数交换位置,再把前面的处理为大顶堆
		for (int i = arr.length - 1; i > 0; i--) {
			int temp = arr[0];
			arr[0] = arr[i];
			arr[i] = temp;
			maxHeap(arr, i, 0);
		}
	}
	
	public static void maxHeap(int[] arr,int size,int index) {
		//左子节点
		int leftNode = 2*index + 1;
		//右子节点
		int rightNode = 2*index + 2;
		int max = index;
		//和两个子节点分别对比,找出最大节点
		if (leftNode < size && arr[leftNode] > arr[max]) {
			max = leftNode;
		}
		if (rightNode < size && arr[rightNode] > arr[max]) {
			max = rightNode;
		}
		//交换位置
		if(max != index) {
			int temp = arr[index];
			arr[index] = arr[max];
			arr[max] = temp;
			//交换位置以后,可能会破坏之前拍好的堆,所以,之前的拍好的堆需要重新调整
			maxHeap(arr, size, max);
		}
	}
	
}