排序算法
排序算法的稳定性
稳定性: 两个相等的数据排序前后,保持相对位置不变,该排序算法具有稳定性
稳定的可以变成不稳定的,不稳定的不能变成稳定的
快速判断:如果发生了跳跃式的交换就是不稳定的排序算法
插入排序
时间复杂度 O(n^2) 最好O(n),越有序,排的越快.
空间复杂度O(1)
稳定,但可以变成不稳定
原理:将前一个数保存在一个临时变量中,并与后面的所有的数相比较,最后找到合适的位置插入.
public static void insertSort(int[] arr){
for(int i = 1;i < arr.length;i++){
int tmp = arr[i];
int j = i-1;
for(;j >= 0;j--){
if(arr[j] > tmp){
arr[j+1] = arr[j];
}else{
break;
}
}
arr[j+1] = tmp;
}
}
希尔排序
时间复杂度:[和增量有关系]😮(n^1.3~n&1.5)
空间复杂度O(1)
不稳定
原理: 是对插入排序的升级,将所排序的所有元素进行均匀分组,对每组进行排序,可以选择跳跃分组,利用选择排序对每个分组进行排序,最后将每个元素进行分组,即整体进行选择排序.
public static void shellSort(int[] arr, int gap) {
for (int i = gap; i < arr.length; i++) {
int tmp = arr[i];
int j = i - gap;
for (; j >= 0; j -= gap) {
if (arr[j] > tmp) {
arr[j + gap] = arr[j];
} else {
break;
}
}
arr[j + gap] = tmp;
}
}
选择排序
时间复杂度O(n^2)
空间复杂度O(1)
不稳定
原理:每一次从无序区间选出最大(或最小)的一个元素,存放在无序区间的最后(或最前),直到全部待排序的数据元素
排完 .
public static void main(String[] args){
int[] arr = {6, 5, 1, 3, 2, 4, 8, 7, 9};
// selectSort(arr);
// System.out.println(Arrays.toString(arr));
selectSort2(arr);
System.out.println(Arrays.toString(arr));
}
public static void selectSort(int[] arr){
for(int i = 0;i < arr.length;i++){
for(int j = i+1;j <arr.length;j++){
if(arr[i] > arr[j]){
swap(arr,i,j);
}
}
}
}
public static void selectSort2(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;
}
}
swap(arr,i,minIndex);
}
}
public static void swap(int[] arr,int i,int j){
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
堆排序
时间复杂度O(n*logn)
空间复杂度O(1)
不稳定
原理:先将数组变为一个大根堆,再将数组的首尾元素交换(实际是将大根堆的最大元素与末尾元素交换),最后去除末尾最大元素占用数组的长度后向下调整,将除最后一个元素的数组变为大根堆,如此反复,直到最后一个元素.
public class Demo {
public static void main(String[] args){
int[] arr = {6,5,1,3,2,4,8,7,9};
//建堆
createHeap(arr);
//排序
headSort(arr);
System.out.println(Arrays.toString(arr));
}
public static void createHeap(int[] arr){
for(int parent = (arr.length - 1 -1)/2;parent >= 0;parent--){
shiftDown(arr,parent,arr.length);
}
}
public static void shiftDown(int[] arr,int parent,int len){
int child = parent * 2 + 1;
while(child < len){
if(child +1 < len && arr[child] <arr[child+1]){
child++;
}
if(arr[child] > arr[parent]){
swap(arr,child,parent);
parent = child;
child = parent * 2 + 1;
}else{
break;
}
}
}
public static void swap(int[] arr,int i,int j){
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
public static void headSort(int[] arr){
int end = arr.length-1;
while(end > 0){
swap(arr,end,0);
shiftDown(arr,0,end);
end--;
}
}
}
冒泡排序
时间复杂度O(n^2),平均也是
空间复杂度O(1)
稳定排序
原理:每一次排序都将最大的一个数放到最后面(bubbleSort),优化(bubbleSort):但第二个for循环不进行交换是即已经排序完成.
public class Demo {
public static void main(String[] args){
int[] arr = {6, 5, 1, 3, 2, 4, 8, 7, 9};
// bubbleSort(arr);
bubbleSort2(arr);
System.out.println(Arrays.toString(arr));
}
public static void bubbleSort(int[] arr){
for(int i = 0 ;i <arr.length;i++){
for(int j = 0;j < arr.length-1-i;j++){
if(arr[j] > arr[j+1]){
swap(arr,j,j+1);
}
}
}
}
public static void bubbleSort2(int[] arr){
for(int i = 0 ;i <arr.length;i++){
boolean key = true;
for(int j = 0;j < arr.length-1-i;j++){
if(arr[j] > arr[j+1]){
swap(arr,j,j+1);
key = false;
}
}
if(key){
break;
}
}
}
public static void swap(int[] arr,int i,int j){
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}
快速排序:
时间复杂度O(n * log(n))
空间复杂度O(log(n))
不稳定.
原理:在数组中取一个数为基准,比基准值小的放到基准值的左边,将比基准值大的放到基准值的右边,对左右两个小区间按照同样的方式处理,直到小区间的长度 == 1,代表已经有序.
递归法:
public class Demo {
public static void main(String[] args){
int[] arr = {6, 5, 1, 3, 2, 4, 8, 7, 9};
quickSort(arr,0,arr.length-1);
System.out.println(Arrays.toString(arr));
}
public static void quickSort(int[] arr,int left,int right){
if(left > right){
return;
}
//优化2:当数据区间小于某个数据时,可以使用插排优化,插排数据越有序越快
if(right -left < 140){
//此时直接使用插排
for(int i = left+1;i < right;i++){
int tmp = arr[i];
int j = i-1;
for(;j >= 0;j--){
if(arr[j] > tmp){
arr[j+1] = arr[j];
}else{
break;
}
}
arr[j+1] = tmp;
}
}
//优化1:三数取中
int midMaxIndex = findMidMaxIndex(arr,left,right);
swap(arr,left,right);
int pivot = partition(arr,left,right);
quickSort(arr,left,pivot-1);
quickSort(arr,pivot+1,right);
}
private static int findMidMaxIndex(int[] arr, int left, int right) {
int mid = left + ((right - left) >>> 1);
int tmp = arr[left] > arr[right] ? left : right;
return arr[left] > arr[mid] ? left : mid;
}
private static 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[right] = tmp;
return right;
}
public static void swap(int[] array, int i, int j) {
int t = array[i];
array[i] = array[j];
array[j] = t;
}
}
非递归法:
public class Demo2 {
public static void main(String[] args){
int[] arr = {6,5,1,3,2,4,8,7,9};
quick2(arr,0,arr.length-1);
System.out.println(Arrays.toString(arr));
}
public static void quick2(int[] arr,int left,int right){
Stack<Integer> stack = new Stack();
int pivot = partition(arr,left,right);
if(pivot > left + 1){
stack.push(left);
stack.push(pivot - 1);
}
if(pivot < right-1 ){
stack.push(pivot + 1);
stack.push(right);
}
while(!stack.isEmpty()){
right = stack.pop();
left = stack.pop();
pivot = partition(arr,left,right);
if(pivot > left + 1){
stack.push(left);
stack.push(pivot - 1);
}
if(pivot < right-1 ){
stack.push(pivot + 1);
stack.push(right);
}
}
}
public static 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;
}
}
归并排序
时间复杂度:O(n * log(n))
空间复杂度:O(n)
稳定
原理:将数组中的元素拆分成多个小块,对每个小块进行单独排序,最后将有序小块合并为整个有序数组
递归法:
public class Demo {
public static void main(String[] args){
int[] arr = {6, 5, 1, 3, 2, 4, 8, 7, 9,10};
mergeSort(arr,0,arr.length-1);
System.out.println(Arrays.toString(arr));
}
public static void mergeSort(int[] arr,int left,int right){
if(left >= right){
return;
}
int mid = left + ((right - left) >> 1);
mergeSort(arr,left,mid);
mergeSort(arr,mid + 1,right);
merge(arr,left,mid,right);
}
public static void merge(int[] arr,int left,int mid,int right){
int[] tmp = new int[right - left + 1];
int k = 0;
int s1 = left;
int e1 = mid;
int s2 = mid + 1;
int e2 = right;
while(s1 <= e1 && s2 <= e2){
if(arr[s1] < arr[s2]){
tmp[k++] = arr[s1++];
}else{
tmp[k++] = arr[s2++];
}
}
while(s1 <= e1){
tmp[k++] = arr[s1++];
}
while(s2 <= e2){
tmp[k++] = arr[s2++];
}
for(int i = 0;i < k;i++){
arr[i + left] = tmp[i];
}
}
}