数据结构 排序
文章目录
-
- 📕1. 直接插入排序
- 📕2. 希尔排序
- 📕3. 冒泡排序
- 📕4. 选择排序
- 📕5. 堆排序
- 📕6. 归并排序
- 📕7. 快速排序
📕1. 直接插入排序
稳定性 : 稳定
时间复杂度 : 最好情况O(N) 最坏情况O(N^2) 平均情况O(N ^2)
空间复杂度 : O(1)
算法思路 :
- 从第一个元素开始,该元素可以认为已经被排序;
- 取出下一个元素,在已经排序的元素序列中从后向前扫描;
- 如果该元素(已排序)大于新元素,将该元素移到下一位置;
- 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;
- 将新元素插入到该位置后;
- 重复步骤2~5。
public static void insertSort(int[] array){ for (int i = 1; i < array.length; i++) { int tmp = array[i]; int j = i-1; for (; j >=0 ; j--) { if (array[j]>tmp){ array[j+1] = array[j]; }else { break; } } array[j+1] = tmp; } }
📕2. 希尔排序
稳定性 : 不稳定
空间复杂度 : O(1)
算法思路 :
- 选择一个步长序列,建议初始步长 n/2,每次减半直到步长为1
- 对每个步长,对数组进行分组,对应位置相隔为步长的元素视为一组
- 对每一组使用插入排序进行排序
- 减小步长,重复步骤2和3,直到步长减少到1
- 当步长为1时,相当于对整个数组做一次插入排序,此时数组已基本有序,所需的比较和移动次数大大减少
public static void shellsort(int[] array){ int gap = array.length/2; while (gap>0){ for (int i = gap; i < array.length; i++) { int tmp = array[i]; int j = i - gap; for (; j >=0 ; j-=gap) { if (array[j]>tmp){ array[j+gap] = array[j]; }else{ break; } } array[j+gap] = tmp; } gap/=2; } }
📕3. 冒泡排序
稳定性 : 稳定
时间复杂度 : 最好情况O(N) 最坏情况O(N^2) 平均情况O(N ^2)
空间复杂度 : O(1)
算法思路 :
- 比较相邻的元素。如果第一个比第二个大,就交换它们
- 对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对
- 这步做完后,最后的元素会是最大的数
- 针对所有的元素重复以上的步骤,除了已经是最大数的最后一个
- 持续每次对越来越少(每次重复都会少一个最大数)的元素重复上面的步骤,直到没有任何一对数字需要比较
public static void bubbleSort(int[] array){ for (int i = 0; i < array.length-1; i++) { for (int j = 0; j < array.length-1-i; j++) { if (array[j]>array[j+1]){ int tmp = array[j]; array[j] = array[j+1]; array[j+1] = tmp; } } } }
📕4. 选择排序
稳定性 : 不稳定
时间复杂度 : 最好情况O(N^2) 最坏情况O(N ^2) 平均情况O(N ^2)
空间复杂度 : O(1)
算法思路 :
- 从下标0开始遍历数组 , 首先把0下标认为是最小元素的下标
- 如果有比0下标元素更小的元素则调换下标 , 然后交换值
- 不断重复上述过程 , 直到遍历完数组
public static void selectSort(int[] array){ for (int i = 0; i < array.length-1; i++) { int midIndex = i; for (int j = i+1; j < array.length; j++) { if (array[j] < array[midIndex]){ midIndex = j; } } int tmp = array[midIndex]; array[midIndex] = array[i]; array[i] = tmp; } }
📕5. 堆排序
稳定性 : 不稳定
时间复杂度 : 最好情况O(nlogn) 最坏情况O(nlogn) 平均情况O(n*logn)
空间复杂度 : O(1)
算法思路 :
- 将无序序列构建成一个最大堆
- 将堆顶元素(最大值)与堆的最后一个元素交换
- 剔除最后一个元素(已排序),将剩余元素重新构建为最大堆
- 重复步骤2和3,直到堆中只剩下一个元素
public static void heapSort(int[] array){ for (int parent = (array.length-1-1)/2; parent>=0 ; parent--) { siftDown(array,parent,array.length); } int end = array.length-1; while(end>0){ int key = array[0]; array[0] = array[end]; array[end] = key; siftDown(array,0,end); end--; } } public static void siftDown(int[] array , int parent,int length){ int child = parent*2+1; while(child<length){ if (child+1<length&&array[child+1]>array[child]){ child++; } if (array[child]>array[parent]){ int tmp = array[child]; array[child] = array[parent]; array[parent] = tmp; parent = child; child = parent*2+1; }else{ break; } } }
📕6. 归并排序
稳定性 : 稳定
时间复杂度 : O(nlogn)
空间复杂度 : O(n)
算法思路:
- 划分 : 将待排序数组从中间划分为两个子序列
- 求解子问题 : 分别对这两个子序列进行排序 , 得到两个有序的子序列
- 合并 : 将这两个子序列合并为一个有序数列
public static void mergeSort(int[] array) { mergeSortChild(array,0,array.length-1); } private static void mergeSortChild(int[] array,int left,int right) { //防御性编程 if(left >= right) { return; } int mid = (left + right) / 2; mergeSortChild(array,left,mid); mergeSortChild(array,mid+1,right); //开始合并 merge(array,left,mid,right); } private static void merge(int[] array, int left, int mid, int right) { //临时数组 int[] tmpArr = new int[right-left+1]; //tmpArr数组下标 int k = 0; int s1 = left; int e1 = mid; int s2 = mid+1; int e2 = right; //当2个段都有数据的时候 while (s1 <= e1 && s2 <= e2) { if(array[s1] <= array[s2]) { tmpArr[k++] = array[s1++]; }else { tmpArr[k++] = array[s2++]; } } //一个段走完了 把另一个段的数据 拷贝到临时数组 while (s1 <= e1) { tmpArr[k++] = array[s1++]; } while (s2 <= e2) { tmpArr[k++] = array[s2++]; } //临时数组当中存储的是有序的数据 -> 拷贝数据到原始数组当中 for (int i = 0; i < k; i++) { array[i+left] = tmpArr[i]; } }
📕7. 快速排序
稳定性 : 不稳定
时间复杂度 : 最好情况O(nlogn) 最坏情况O(n^2) 平均情况O(nlogn)
空间复杂度 : 最好情况O(logn) 最坏情况O(n) 平均情况O(logn)
算法思路(挖坑法):
-
选择基准元素:从列表中选择一个元素作为基准(pivot)。选择方式可以是第一个元素、最后一个元素、中间元素或随机元素。
-
分区:将列表重新排列,使得所有小于基准元素的元素都在基准的左侧,所有大于基准元素的元素都在基准的右侧。基准元素的位置在分区完成后确定。
-
递归排序:对基准元素左侧和右侧的子列表分别递归地进行快速排序。
-
合并:由于分区操作是原地进行的,递归结束后整个列表已经有序。
public static void quickSort(int[] array){ quick(array,0,array.length-1); } private static void quick(int[] array, int startindex, int endindex) { if (startindex>=endindex){ return; } int par = getpartition(array,startindex,endindex); quick(array,startindex,par-1); quick(array,par+1,endindex); } private static int getpartition(int[] array, int startindex, int endindex) { int index = startindex; int left = startindex; int right = endindex; int pivot = array[index]; while (left<right){ while (left<right){ if (array[right]<pivot){ array[index] = array[right]; left++; index = right; break; }else { right--; } } while (left<right){ if (array[left]>pivot){ array[index] = array[left]; index = left; right--; break; }else { left++; } } } array[index] = pivot; return index; }