从零开始学数据结构与算法-八大排序算法(图解)

排序算法

时间复杂度和空间复杂度

通常,对于一个给定的算法,我们要做 两项分析。第一是从数学上证明算法的正确性,这一步主要用到形式化证明的方法及相关推理模式,如循环不变式、数学归纳法等。而在证明算法是正确的基础上,第二步就是分析算法的时间复杂度。

度量一个程序的执行时间通常有两种方法:
一、事后统计的方法
二、事前分析估算的方法

在各种不同算法中,若算法中语句执行次数为一个常数,则时间复杂度为O(1),另外,在时间频度不相同时,时间复杂度有可能相同,如T(n)=n2+3n+4与T(n)=4n2+2n+1它们的频度不同,但时间复杂度相同,都为O(n2)。 按数量级递增排列,常见的时间复杂度有:常数阶O(1),对数阶O(log2n),线性阶O(n), 线性对数阶O(nlog2n),平方阶O(n2),立方阶O(n3),…, k次方阶O(nk),指数阶O(2n)。随着问题规模n的不断增大,上述时间复杂度不断增大,算法的执行效率越低。

常见的算法时间复杂度由小到大依次为:Ο(1)<Ο(log2n)<Ο(n)<Ο(nlog2n)<Ο(n²)<Ο(n³)<…<Ο(2ⁿ)<Ο(n!)

冒泡排序

属于交换排序,平均时间复杂度 Ο(n²)

从零开始学数据结构与算法-八大排序算法(图解)_第1张图片

public class BubbleSort {
	public static void main(String[] args) {
		int[] arr = new int[] { 5, 9, 6, 3, 4, 7, 1, 8, 2 };
		System.out.println(Arrays.toString(arr));
		bubbleSort(arr);
		System.out.println(Arrays.toString(arr));
	}
	public static void bubbleSort(int[] arr) {
		// 控制共比较多少轮
		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]) {
					int temp = arr[j];
					arr[j] = arr[j + 1];
					arr[j + 1] = temp;
				}
			}
		}
	}
}

快速排序

属于交换排序,平均时间复杂度 O(nlog2n)
从零开始学数据结构与算法-八大排序算法(图解)_第2张图片

public class QuickSort {
	public static void main(String[] args) {
		int[] arr = new int[] { 5, 9, 6, 3, 4, 7, 1, 8, 2 };
		System.out.println(Arrays.toString(arr));
		quickSort(arr, 0, arr.length - 1);
		System.out.println(Arrays.toString(arr));
	}
	public static void quickSort(int[] arr, int start, int end) {
		// 记录排序所需的下标
		int low = start;
		int high = end;
		if (low >= high) {
			return;
		}
		// 将第一个数字作为标准数
		int stard = arr[low];
		while (low < high) {
			// 右边的数字比标准数大
			while (low < high && stard <= arr[high]) {
				high--;
			}
			// 右边的数替换左边的数
			arr[low] = arr[high];
			// 左边的数字比标准数小
			while (low < high && arr[low] <= stard) {
				low++;
			}
			arr[high] = arr[low];
		}
		// 把标准数赋给low所在的位置的元素
		arr[low] = stard;
		quickSort(arr, start, low-1);
		quickSort(arr, low + 1, end);
	}
}

直接插入排序

属于插入排序,平均时间复杂度 Ο(n²)
从零开始学数据结构与算法-八大排序算法(图解)_第3张图片

public class InsertSort {
    public static void main(String[] args) {
		int[] arr = new int[] { 5, 2, 1, 3, 4, 6, 9, 8, 7 };
		System.out.println(Arrays.toString(arr));
		insertSort(arr);
		System.out.println(Arrays.toString(arr));
	}
	public 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];
				}
				// 把当前数字赋给不满足条件的最后一个元素
				arr[j + 1] = temp;
			}
		}
	}
}

希尔排序

属于插入排序,平均时间复杂度 O(n^1.3)
从零开始学数据结构与算法-八大排序算法(图解)_第4张图片

public class ShellSort {
	public static void main(String[] args) {
		int[] arr = new int[] { 5, 9, 6, 8, 1, 4, 7, 2, 3 };
		System.out.println(Arrays.toString(arr));
		shellSort(arr);
		System.out.println(Arrays.toString(arr));
	}
	public static void shellSort(int[] arr) {
		// 遍历所有的步长
		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]) {
						int temp = arr[j];
						arr[j] = arr[j + d];
						arr[j + d] = temp;
					}
				}
			}
		}
	}
}

简单选择排序

属于选择排序,平均时间复杂度 Ο(n²)
从零开始学数据结构与算法-八大排序算法(图解)_第5张图片

public class SelectSort {
	public static void main(String[] args) {
		int[] arr = new int[] { 5, 9, 6, 8, 1, 4, 7, 2, 3 };
		System.out.println(Arrays.toString(arr));
		selectSort(arr);
		System.out.println(Arrays.toString(arr));
	}
	public 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[minIndex] > arr[j]) {
					minIndex = j;
				}
			}
			if(minIndex != i){
				int temp = arr[minIndex];
				arr[minIndex] = arr[i];
				arr[i] = temp;
			}
		}
	}
}

堆排序

属于选择排序,平均时间复杂度 O(nlog2n)

从零开始学数据结构与算法-八大排序算法(图解)_第6张图片

public class HeapSort {
	public static void main(String[] args) {
		int[] arr = new int[] { 9, 6, 8, 7, 10, 4, 2, 11, 
  52, 33, 77, 12, 32 };
		System.out.println(Arrays.toString(arr));
		heapSort(arr);
		System.out.println(Arrays.toString(arr));
	}

	public static void heapSort(int[] arr) {
		// 开始位置是最后的一个非叶子节点,即最后一个节点的父节点
		int start = (arr.length - 2) / 2;
		// 调整为大顶堆
		for (int i = start; i >= 0; i--) {
			maxHeap(arr, arr.length, i);
		}
		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 leftNodeIndex = 2 * index + 1;
		// 右子节点
		int rightNodeIndex = 2 * index + 2;
		int maxIndex = index;
		// 和子节点比较找出最大节点
		if (leftNodeIndex < size&&arr[leftNodeIndex] > arr[maxIndex]){
			maxIndex = leftNodeIndex;
		}
		if (rightNodeIndex < size&&arr[rightNodeIndex] > arr[maxIndex]){
			maxIndex = rightNodeIndex;
		}
		if (maxIndex == index) {
			return;
		}
		int temp = arr[index];
		arr[index] = arr[maxIndex];
		arr[maxIndex] = temp;
		// 交换位置之后,可能会破坏之前排好的堆,所以之前排好的堆需要调整
		maxHeap(arr, size, maxIndex);
	}
}

归并排序

平均时间复杂度 O(nlog2n)

从零开始学数据结构与算法-八大排序算法(图解)_第7张图片

public class MergeSort {
	public static void main(String[] args) {
		int[] arr = new int[] { 5, 9, 6, 8, 1, 4, 7, 2, 3 };
		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) {
		if (low >= high) {
			return;
		}
		int middle = (low + high) / 2;
		// 处理左边
		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++;
			} else {
				temp[index] = arr[j];
				j++;
			}
			index++;
		}
		while (i <= middle) {
			temp[index] = arr[i];
			i++;
			index++;
		}
		while (j <= high) {
			temp[index] = arr[j];
			j++;
			index++;
		}
		// 将临时数组中的数组替换到原数组
		for (int k = 0; k < temp.length; k++) {
			arr[low + k] = temp[k];
		}
	}
}

基数排序

平均时间复杂度 O(d(r+n))

从零开始学数据结构与算法-八大排序算法(图解)_第8张图片

public class RedixSort {
	public static void main(String[] args) {
		int[] arr = new int[] { 15, 59, 676, 228, 21, 4, 
  47, 72, 93, 335, 26, 70 };
		System.out.println(Arrays.toString(arr));
		redixSort(arr);
		System.out.println(Arrays.toString(arr));
	}
	public static void redixSort(int[] arr) {
		// 得出数组中最大的数字
		int max = Integer.MIN_VALUE;
		for (int i = 0; i < arr.length; i++) {
			if (arr[i] > max) {
				max = arr[i];
			}
		}
		// 计算最大数字是几位数
		int maxLength = String.valueOf(max).length();
		// 用于临时存储数据的数组
		int[][] temp = new int[10][arr.length];
		// 用于记录temp二维数组中存放数字的数量
		int[] counts = new int[10];
		// 根据最大长度的数决定比较的次数
		for (int i = 0, n = 1; i < maxLength; i++, n *= 10) {
			// 每个数字计算余数
			for (int j = 0; j < arr.length; j++) {
				// 计算余数
				int remainder = arr[j] / n % 10;
				// 把当前遍历的数据放入指定的数组
				temp[remainder][counts[remainder]] = arr[j];
				// counts记录数量
				counts[remainder]++;
			}
			// 记录取的元素需要放的位置
			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];
					}
					counts[k] = 0;
				}
			}
		}
	}
}

你可能感兴趣的:(从零开始学数据结构与算法)