排序:
稳定性
待排序的记录中,存在相同的关键字,排序后,关键字的相对次序不变,这就是一个稳定的排序.
内部排序在内存中 ,外部排序在外存中.
直接插入排序
简介:
基本操作是将一条记录插入到已排好的有序表中,从而得到一个新的、记录数量增1的有序表
默认将要插入的表是有序表
默认第一个数是有序的,第二个数要插入到有序表,和有序表后面的数字进行比较,插入到合适的位置.
public static List<Integer> insertSort(int[] arr){ for (int i = 1; i < arr.length ; i++) { int temp = arr[i]; int j; for ( j = i-1; j >= 0; j--) { if(arr[j] > temp){ arr[j+1] = arr[j]; }else { break; } } arr[j+1] = temp; } return list; }
时间复杂度O(n^2)
时间复杂度O(1)
稳定的排序
希尔排序:
希尔排序(Shell Sort)是插入排序的一种。也称缩小增量排序,是直接插入排序算法的一种更高效的改进版本
希尔排序是一种基于插入排序的排序算法,其核心思想是将整个待排序序列分成若干个子序列进行插入排序,然后依次缩短子序列的长度,直到最后子序列长度为1,完成对整个序列的排序。
public static int[] shellSort(int[] a){ int gap = a.length; while (gap > 1){ gap /=2; shell(a,gap); } shell(a,1); return a; } private static void shell(int[] a, int gap) { for (int i = gap; i <a.length ; i++) { int temp = a[i]; int j; for ( j = i-gap; j >=0 ; j-=gap) { for(temp < a[j]) a[j+gap] = a[j]; } else{ braek; } a[j+gap] = temp; } }
时间复杂度O(N^1.3)
不稳定的排序
选择排序:
从一组数据中找到最小或者最大的数据,放在初始位置.直到全部排好序列.
选择排序
待排序的记录中,存在相同的关键字,排序后,关键字的相对次序不变,这就是一个稳定的排序.
public vois selectSort(int[] arr){ for (int i = 0; i < arr.length; i++) { int min = arr[i]; int j; for (int j = i+1; j < arr.length; j++) { if (arr[j] < min){ swap(arr,j,min); } } } } public void swap(int[] arr,int j,int t){ int tmp = arr[j]; arr[j] = arr[t]; arr[t] = tmp; }
堆排序:
过程:(https://mp.csdn.net/mp_blog/creation/editor/130708844)
将一组数据从小到大排序:
使用大根堆,以此类推.
使用小根堆左右孩子不能保证谁打谁小.
public void heapSort(){ int end = usedSize-1; //记录下标 while (end > 0){ swap(elem,0,end);//交换堆顶和最后一个元素 shitfDown(0,end);堆顶元素向下调整 end--;//除去排好序的元素 } public void createHeap() { for (int parent = (usedSize - 1 - 1) / 2; parent >= 0; parent--) { //usedSize-2: -1是因为下标 再-1是因为计算parent的值就是(i-1)/2 shitfDown(parent, usedSize);//每棵树向下调整 } } public void shitfDown(int parent, int usedSize) { int child = 2 * parent + 1; while (child < usedSize) { //最起码要有左孩子 if (child + 1 < usedSize && elem[child + 1] > elem[child]) { child++;//判断左右孩子的最大值 } if (elem[child] > elem[parent]) { swap(elem, child, parent);//调整完一次 之后继续调整这棵树 parent = child; child = 2 * parent + 1; } else { break;//既然这是个大根堆 那就不用调整了,因为大根堆的子堆一定是大根堆 } } } public void swap(int[] elem, int child, int parent) { int tmp = elem[child]; elem[child] = elem[parent]; elem[parent] = tmp; }
冒泡排序
两两比较,找最大或者最小的.
public void bubbleSort(int[] arr){ for (int i = 0; i <arr.length - 1 ; i++) { // boolean flag = false; for (int j = 0; j <arr.length-1-i ; j++) { if (arr[j] > arr[j+1]){ //相邻元素进行比较 每次找到每轮最大的 swap(arr,j,j+1); flag = true; } } if (flag == false){ return; }//如果没有发生交换 说明有序 直接跳出循环 } }
时间复杂度:0(N^2)
空间复杂度O(1)
快速排序
挖坑法:
取一个元素作为基准,左边比它小,右边比它大 直到全部排序完.
left从做左边走,right从右面走.比如以6为例子.
把6先拿出来,right从后往前找到比6小的放前面.left找到比6大的放有空的哪个.
把left的元素放入right中.重复这个过程知道left和right相遇.
直到把6放入left和right相遇的位置.
如果先算前面,之后新的right就是这个基准-1.
public void quickSort(int[] arr){ quick(arr,0,arr.length-1); } public void quick(int[] arr,int left,int right){ //为什么取大于号 1 2 3 4 right=-1了 if (left >= right){ return; } if(right - left + 1 <=14){ for(int i=left;i<right;i++){ int tmp = arr[i]; for(int j=i-1;j>=0;j--){ if(tmp <arr[j]){ arr[j+1] = arr[j]; }else{ break; } arr[j+1] = tmp; } } return; }//如果区间小,直接插入排序 int index = partition(arr,left,right); quick(arr,left,index-1); quick(arr,index+1,right); } 挖坑: private int partition(int[] arr, int left, int right) { int tmp = arr[left]; while (left<right){ while (left < right &&arr[right] >= tmp){ //如果没有等号 这就是个死循环 right--; } //从后往前找比基准值小的 arr[left]=arr[right]; while (left < right && arr[left] <= tmp){ left++; } arr[right]=arr[left]; } arr[left] = tmp; return left; } Hoare法: private int partition2(int[] arr, int left, int right) { int tmp = arr[left]; int i = left; while (left < right){ while (left < right && arr[right] <= tmp){ right--; } while (left < right && arr[left] >= tmp){ left++; } swap(arr,right,left); } swap(arr,left,i); return left; }
归并排序
public static void mergeSort(int[] arr){ meregeSort(arr,0,arr.length-1);调用递归 } public static void meregeSort(int[] arr,int left,int right){ if (left >= right){ // 开始位置大于结束位置 如果只有右子树呢? return; } int mid = (left+right) / 2; //计算中间位置 meregeSort(arr,left,mid);//分组 直到只要1个数组 meregeSort(arr,mid+1,right); merge(arr,left,right,mid); //排序 } private static void merge(int[] arr,int start,int end,int mid){ int s1 = start; int s2 = mid+1;//第二组的开始位置 int[] tmp = new int[end-start+1]; //排好序就放在这个数组 int k = 0; // 新数组的下标 while (s1 <= mid && s2 <=end){//如果第一组和第二组都有元素则循环,而且两组都是有序序列 if (arr[s1] <= arr[s2]){ tmp[k++]=arr[s1++]; }else { tmp[k++] = arr[s2++]; } } while (s1 <=mid){ // 如果s1内有元素 tmp[k++] = arr[s1++]; } while (s2 <=end){ tmp[k++] = arr[s2++]; } for (int n = 0;n <arr.length;n++){//将排好序的数组放入要排序的数组中 arr[i+start] = tmp[i]; //画图 每次看开始位置 } }
非递归:
时间复杂度:
1三数取中:解决每次分割尽量不要使得一边为空
2小区间插入排序. 如果是一个二叉树,后两层占到70%的节点数.如果继续递归那么时间复杂度和空间复杂度就会上来.