目录
数据结构:数组、链表、队列、栈、前(中、后)缀表达式、哈希表、二叉树、图
算法:递归、回溯、排序、分治、动态规划、暴力匹配、KMP、贪心、 普利姆-Prim(解决修路)、克鲁斯卡尔-Kruskal(解决公交)、 迪杰斯特拉-Dijkstra(解决最短路径)、弗洛伊德-Floyd(解决最短路径) 、骑士周游(用到回溯、贪心);https://www.bilibili.com/video/BV1E4411H73v?p=86
练习5种:冒泡排序、选择排序、插入排序、希尔排序、计数排序
比较排序:快速排序、归并排序、冒泡排序、堆排序 (每个数必须和其他数比较,才能确定自己的位置) ;在快速排序、归并排序之类的排序中,问题规模通过分治法消减为logN次,所以时间复杂度平均O(nlogn) ;冒泡排序之类的排序中,问题规模为n,又因为需要比较n次,所以平均时间复杂度为O(n2)
优势:比较排序适用于各种规模的数据,也不在乎数据的分布,适用于一切需要排序的情况
非比较排序:计数排序、基数排序、桶排序 (通过确定每个元素之前,应该有多少个元素来排序,则唯一确定了arr[i]在排序后数组中的位置);非比较排序只要确定每个元素之前已有的元素个数,所以一次遍历即可解决,算法时间复杂度O(n)
特点:时间复杂度低,但由于需要占用空间来确定唯一位置,所以对数据规模和数据分布有一定的要求
冒泡排序
1、冒泡排序:从第一个开始,依次两两比较,大的放在后面(升序)
最佳情况:T(n) = O(n)、最坏:T(n) = O(n^2)、平均:T(n) = O(n^2)
// 1、冒泡排序:从第一个开始,依次两两比较,大的放在后面(升序)
// 最佳情况:T(n) = O(n)、最坏:T(n) = O(n^2)、平均:T(n) = O(n^2)
public static int[] bubbleSort(int[] array) {
int k = array.length - 1; // k用来记录每趟排序的最大的交换位置
int pos = 0; // pos记录最后一次交换的位置
for (int i = 0; i < array.length - 1; i++) {
int flag = 0; // 每一趟前都将flag标志先置为0
for (int j = 0; j < k; j++) {
if (array[j] > array[j + 1]) {
int temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
flag = 1; // 元素发生了交换,flag置为1
pos = j; // pos存放循环里最后一次交换的位置j
}
}
k = pos; // 下一内层循环仅循环到0到这次得到的k之间
// 如果一趟下来flag没有变化,即元素本来就是有序,就直接return
if (flag == 0) {
return array;
}
}
return array;
}
选择排序
2、选择排序:每次找到最小数,记录下序号,与第i趟对换
表现最稳定的排序算法之一 ,无论什么数据进去都是O(n^2)的时间复杂度public static int[] selectionSort(int[] array) { int minIndex, j, temp = 0; for (int i = 0; i < array.length - 1; i++) { minIndex = i; for (j = i + 1; j < array.length; j++) { if (array[minIndex] > array[j]) { minIndex = j; // 比较n-i个数,找出最小值的序号 } } temp = array[minIndex]; array[minIndex] = array[i]; array[i] = temp; } return array; }
插入排序
3、插入排序:从未排序序列取出数,和已排序序列从后往前比较,小于,已排序序列就空出一个位置,由该数插入
最佳情况:T(n) = O(n)、最坏情况:T(n) = O(n^2)、平均情况:T(n) = O(n^2)public static int[] insertSort(int[] array) { int sortNum = 0; // 需要比较的数 int preIndex = 0; // 已排序序列最后一个数 for (int i = 0; i < array.length - 1; i++) { preIndex = i; sortNum = array[i + 1]; // 需比较的数 一直和 已排序的序列比较 while (preIndex >= 0 && sortNum < array[preIndex]) { array[preIndex + 1] = array[preIndex]; preIndex--; } array[preIndex + 1] = sortNum; // 把需要比较的数 放到 已排序序列 } return array; }
希尔排序
4、希尔排序:升级后的插入排序,将数组 按希尔增量序列(n/2,n/4,...) 依次分组,对每组单独做插入排序,直到 gap为1
最佳情况:T(n) = O(nlog2 n)、最坏情况:T(n) = O(nlog2 n)、平均情况:T(n) =O(nlog2n)
public static int[] shellSort(int[] array) { int temp, gap = array.length / 2; while (gap > 0) { // 分组后 for (int i = gap; i < array.length; i++) { // gap后的数为 需比较的数 int preIndex = i - gap; temp = array[i]; while (preIndex >= 0 && array[preIndex] > temp) { array[preIndex + gap] = array[preIndex]; preIndex -= gap; } array[preIndex + gap] = temp; //放置 需比较的数 } gap /= 2; } return array; }
归并排序
5、归并排序:建立在归并操作的一种排序算法,采用分治法(Divide and Conquer)的一个典型应用
先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并;始终是O(n log n)的时间复杂度,代价是需要额外的内存空间public static int[] mergeSort(int[] array) { if (array.length < 2) return array; int mid = array.length / 2; int[] left = Arrays.copyOfRange(array, 0, mid); int[] right = Arrays.copyOfRange(array, mid, array.length); return merge(mergeSort(left), mergeSort(right)); } public static int[] merge(int[] left, int[] right) { int[] result = new int[left.length + right.length]; for (int index = 0, i = 0, j = 0; index < result.length; index++) { if (i >= left.length) result[index] = right[j++]; else if (j >= right.length) result[index] = left[i++]; else if (left[i] > right[j]) result[index] = right[j++]; else result[index] = left[i++]; } return result; }
快速排序
6、快速排序:从数组中找一个数作基准值,小于基准值的放前面,大于的放后面,等于的放任一边,递归的找基准值
最佳情况:T(n) = O(nlogn)、最差情况:T(n) = O(n2)、平均情况:T(n) = O(nlogn)public static void quickSort(int[] array, int low, int high) { if (low < high) { // 找寻基准数据的正确索引 int index = getIndex(array, low, high); // 进行迭代对index之前和之后的数组进行相同的操作使整个数组变成有序 quickSort(array, low, index - 1); quickSort(array, index + 1, high); } } public static int getIndex(int[] arr, int low, int high) { // 基准数据 int tmp = arr[low]; while (low < high) { // 当队尾的元素大于等于基准数据时,向前挪动high指针 while (low < high && arr[high] >= tmp) { high--; } // 如果队尾元素小于tmp了,需要将其赋值给low arr[low] = arr[high]; // 当队首元素小于等于tmp时,向前挪动low指针 while (low < high && arr[low] <= tmp) { low++; } // 当队首元素大于tmp时,需要将其赋值给high arr[high] = arr[low]; } // 跳出循环时low和high相等,此时的low或high就是tmp的正确索引位置 // 由原理部分可以很清楚的知道low位置的值并不是tmp,所以需要将tmp赋值给arr[low] arr[low] = tmp; return low; // 返回tmp的正确位置 }
堆排序
7、堆排序算法:利用堆数据结构设计,满足堆积的性质,即子结点的键值或索引总是小于(或者大于)它的父节点
最佳情况:T(n) = O(nlogn)、最差情况:T(n) = O(nlogn)、平均情况:T(n) = O(nlogn)static int len; //声明全局变量,用于记录数组array的长度; public static int[] heapSort(int[] array) { len = array.length; if (len < 1) return array; //1.构建一个最大堆 buildMaxHeap(array); //2.循环将堆首位(最大值)与末位交换,然后在重新调整最大堆 while (len > 0) { swap(array, 0, len - 1); len--; adjustHeap(array, 0); } return array; } // 建立最大堆 public static void buildMaxHeap(int[] array) { //从最后一个非叶子节点开始向上构造最大堆 //for循环这样写会更好一点:i的左子树和右子树分别2i+1和2(i+1) for (int i = (len / 2 - 1); i >= 0; i--) { adjustHeap(array, i); } } public static void swap(int[] array, int i, int j) { int temp = array[i]; array[i] = array[j]; array[j] = temp; } // 调整使之成为最大堆 public static void adjustHeap(int[] array, int i) { int maxIndex = i; //如果有左子树,且左子树大于父节点,则将最大指针指向左子树 if (i * 2 < len && array[i * 2] > array[maxIndex]) maxIndex = i * 2; //如果有右子树,且右子树大于父节点,则将最大指针指向右子树 if (i * 2 + 1 < len && array[i * 2 + 1] > array[maxIndex]) maxIndex = i * 2 + 1; //如果父节点不是最大值,则将父节点与最大值交换,并且递归调整与父节点交换的位置。 if (maxIndex != i) { swap(array, maxIndex, i); adjustHeap(array, maxIndex); } }
计数排序
8、计数排序:使用一个额外的数组C,其中第i个元素是待排序数组A中值为i的元素个数,然后根据数组C来将A中的元素排到正确的位置,它只能对整数进行排序
最佳情况:T(n) = O(n+k)、最差情况:T(n) = O(n+k)、平均情况:T(n) = O(n+k)public static int[] countingSort(int[] array) { if (array.length == 0) { return array; } int bias, min = array[0], max = array[0]; for (int i = 1; i < array.length; i++) { if (array[i] > max) { max = array[i]; } if (array[i] < min) { min = array[i]; } } bias = 0 - min; int[] bucket = new int[max - min + 1]; Arrays.fill(bucket, 0); for (int i = 0; i < array.length; i++) { bucket[array[i] + bias]++; } int index = 0, i = 0; while (index < array.length) { if (bucket[i] != 0) { array[index] = i - bias; bucket[i]--; index++; } else { i++; } } return array; }
桶排序
9、桶排序:假设输入数据服从均匀分布,将数据分到有限数量的桶里,每个桶再分别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序)
设置一个BucketSize,作为每个桶能放置多少个不同数值(例如BucketSize==5时,该桶可以存放{1,2,3,4,5}这几种数字,但是容量不限,即可以存放100个3)
最佳情况:T(n) = O(n+k)、最差情况:T(n) = O(n+k)、平均情况:T(n) = O(n2)public static ArrayList<Integer> bucketSort(ArrayList<Integer> array, int bucketSize) { if (array == null || array.size() < 2) return array; int max = array.get(0), min = array.get(0); // 找到最大值最小值 for (int i = 0; i < array.size(); i++) { if (array.get(i) > max) max = array.get(i); if (array.get(i) < min) min = array.get(i); } int bucketCount = (max - min) / bucketSize + 1; ArrayList<ArrayList<Integer>> bucketArr = new ArrayList<>(bucketCount); ArrayList<Integer> resultArr = new ArrayList<>(); for (int i = 0; i < bucketCount; i++) { bucketArr.add(new ArrayList<Integer>()); } for (int i = 0; i < array.size(); i++) { bucketArr.get((array.get(i) - min) / bucketSize).add(array.get(i)); } for (int i = 0; i < bucketCount; i++) { if (bucketSize == 1) { // 如果带排序数组中有重复数字时 for (int j = 0; j < bucketArr.get(i).size(); j++) resultArr.add(bucketArr.get(i).get(j)); } else { if (bucketCount == 1) bucketSize--; ArrayList<Integer> temp = bucketSort(bucketArr.get(i), bucketSize); for (int j = 0; j < temp.size(); j++) resultArr.add(temp.get(j)); } } return resultArr; }
基数排序
10、基数排序:基数排序也是非比较的排序算法,对每一位进行排序,从最低位开始排序,复杂度为O(kn),k为数组中的数的最大的位数;
public static int[] radixSort(int[] array) { if (array == null || array.length < 2) return array; // 1.先算出最大数的位数; int max = array[0]; for (int i = 1; i < array.length; i++) { max = Math.max(max, array[i]); } int maxDigit = 0; while (max != 0) { max /= 10; maxDigit++; } int mod = 10, div = 1; ArrayList<ArrayList<Integer>> bucketList = new ArrayList<ArrayList<Integer>>(); for (int i = 0; i < 10; i++) { bucketList.add(new ArrayList<Integer>()); } for (int i = 0; i < maxDigit; i++, mod *= 10, div *= 10) { for (int j = 0; j < array.length; j++) { int num = (array[j] % mod) / div; bucketList.get(num).add(array[j]); } int index = 0; for (int j = 0; j < bucketList.size(); j++) { for (int k = 0; k < bucketList.get(j).size(); k++) array[index++] = bucketList.get(j).get(k); bucketList.get(j).clear(); } } return array; }