常用七大排序算法

目录

1.常见排序算法分类

 2.直接插入排序

2.1 解析

 2.2 代码实现

2.3 性能分析

3. 希尔排序

4. 选择排序

5. 堆排序

6.冒泡排序

7. 快速排序

8. 归并排序

8.1 两个有序数组合并

 8.2 归并排序

8.3 非递归的归并排序


1.常见排序算法分类

 2.直接插入排序

通过两个 for 循环来逐一比较插入。常用于 数据量不多且整体趋于有序。

2.1 解析

 

 

 

 

 2.2 代码实现

public static void inserSort(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.3 性能分析

稳定性:稳定
插入排序,初始数据越接近有序,时间效率越高

3. 希尔排序

希尔排序法又称缩小增量法。希尔排序法的基本思想是:先选定一个整数,把待排序文件中所有记录分成个组,所有距离为的记录分在同一组内,并对每一组内的记录进行排序。
然后,取,重复上述分组和排序的工作。当到达=1 时, 所有记录在统一组内排好序。
1. 希尔排序是对直接插入排序的优化。
2. gap > 1 时都是预排序,目的是让数组更接近于有序。当 gap == 1 时,数组已经接近有序的了,这样就会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比。

 代码:

/**
 * 希尔排序(本质是直接插入排序)
 * @param array 待排序列
 * @param gap   组数
 */
public static void shell(int[] array, int gap){
	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;
	}
}

/**
 * 希尔排序
 * 时间复杂度:O(n^1.3 - n^1.5)
 * 空间复杂度:O(1)
 * 不具有稳定性
 * @param array 待排数组
 */
public static void shellSort(int[] array) {
	int gap = array.length;
	while (gap > 1) {
		shell(array,gap);
		gap /= 2;
	}
	shell(array,1);//保证最后是1组
}

4. 选择排序

从前面开始有序

解析

 实现

public static void selectSort1(int[] array) {
	for (int i = 0; i < array.length; i++) {
		for (int j = i+1; j < array.length; j++) {
			if (array[j] < array[i]) {
				int tmp = array[i];
				array[i] = array[j];
				array[j] = tmp;
			}
		}
	}
}
//优化后的选择排序
public static void selectSort(int[] array) {
	for (int i = 0; i < array.length; i++) {
		int minIndex = i;
		for (int j = i+1; j < array.length; j++) {
			if (array[j] < array[minIndex]) {
				minIndex = j;
			}
		}
		int tmp = array[i];
		array[i] = array[minIndex];
		array[minIndex] = tmp;
	}
}

 * 时间复杂度:O(n^2)
 * 空间复杂度:O(1)
 * 稳定性:不稳定

5. 堆排序

基本原理也是选择排序,只是不在使用遍历的方式查找无序区间的最大的数,而是通过堆来选择无序区间的最大的数。
注意: 排升序要建大堆;排降序要建小堆。
讲解升序过程

代码实现

public static void heapSort(int[] array) {
	//1.建堆 O(n)
	createHeap(array);
	int end = array.length;
	//2.交换后调整O(n^log n)
	while (end > 0) {
		swap(array,0,end);
		shifDowm(array,0,end);
		end--;
	}
	
}

public static void createHeap(int[] array) {
	for (int parent = (array.length-1-1)/2; parent >= 0; parent--) {
		//向下调整为大根堆
		shifDowm(array,parent,array.length);
	}
}

public static void shifDowm(int[] array, int parent, int len) {
	int child = 2*parent+1;//左孩子下标
	while(child < len) {
		if (child+1 < len && array[child] < array[child+1]) {
			child++;
		}
		//child 下标,就是左右孩子最大值的下标
		if (array[child] > array[parent]) {
			swap(array,child,parent);
			parent = child;
			child = 2*parent+1;
		}else {
			break;
		}
	}
}

public static void swap(int[] array, int i, int j) {
	int tmp = array[i];
	array[i] = array[j];
	array[j] = tmp;
}
     * 时间复杂度:O(N * log N)
     *
     * 空间复杂度:O(1)
     *
     * 稳定性:不稳定

6.冒泡排序

从后面开始有序,

在无序区间,通过相邻数的比较,将最大的数冒泡到无序区间的最后,持续这个过程,直到数组整体有序
解析:
i = 0 时,走完内存 j 循环

 i = 1 时,走完内存 j 循环

 i= 2 时,走完内部循环都没有交换,排序结束

 

 代码:

public void bubbleSort(int[] array) {
	for (int i = 0; i < array.length-1; i++) {
		boolean flg = false;
		for (int j = 0; j < array.length-1-i; j++) {
			if (array[j] > array[j+1]) {
				swap(array,j + 1,j);
				flg = true;
			}
		}
		if (flg = false) {
			break;// j 循环没有交换,说明排序结束
		}
	}
}

public static void swap(int[] array, int i, int j) {
	int tmp = array[i];
	array[i] = array[j];
	array[j] = tmp;
}

* 时间复杂度:O(N^2)
 * 有序情况下:O(n)
 * 空间复杂度:O(1)
 * 稳定性:稳定的排序

7. 快速排序

1. 从待排序区间选择一个数,作为基准值 (pivot)
2. Partition: 遍历整个待排序区间,将比基准值小的(可以包含相等的)放到基准值的左边,将比基准值大的(可 以包含相等的)放到基准值的右边;
3. 采用分治思想,对左右两个小区间按照同样的方式处理,直到小区间的长度 == 1 ,代表已经有序,或者小区间 的长度 == 0 ,代表没有数据。
原理(挖坑法)

第一个基准 

第一个基准的左边

 

 第一个基准的左边已经有序了,到右边

在达到基准为5 的右边

 代码实现


public static void quickSort(int[] array) {
	quick(array,0,array.length-1);
}

public static void quick(int[] array, int left, int right) {
	if (left >= right) {
		return;
	}
	int pivot = partition(array,left,right);//基准

	quick(array, left, pivot-1);//递归基准的左边
	quick(array, pivot+1, right);
}

private static int partition(int[] array,int start, int end) {
	 int tmp = array[start];
	 while(start < end) {
	 	while(start < end && array[end] >= tmp) {
	 		end--;
	 	}
	 	//end 下标遇到了 < tmp 的值
	 	array[start] = array[end];
	 	while(start < end && array[start] <= tmp) {
	 		start++;
	 	}
	 	//start 下标遇到了 > tmp 的值
	 	array[end] = array[start];
	 }
	 //start 和end 相遇,为基准
	 array[start] = array[tmp];
	 //返回基准的下标
	 return start;
}
 

* 时间复杂度:

* 最好【每次可以均匀的分割待排序序列】:O(N*logn)

* 最坏:数据有序 或者逆序的情况 O(N^2)

* 空间复杂度:

* 最好:O(logn)

* 最坏:O(n) 单分支的一棵树

* 稳定性:不稳定的排序

代码优化(三数取中法)

public static void quickSort(int[] array) {
	quick(array,0,array.length-1);
}

public static void quick(int[] array, int left, int right) {
	if (left >= right) {
		return;
	}

	//1.找基准之前,先找中间大小值——使用三数取中法
	int midValIndex = findMidValIndex(array,left,right);
	swap(array,midValIndex,left);

	int pivot = partition(array,left,right);//基准
	quick(array, left, pivot-1);//递归基准的左边
	quick(array, pivot+1, right);
}

public static int findMidValIndex(int[] array,int start,int end) {
	int mid = start + ((end-start) >>>1); 
	if (array[start] < array[end]) {
		if (array[mid] < array[start]) {
			return start
		}else if (array[mid] > array[end]) {
			return end;
		}else {
			return mid;
		}
	}else {
		if (array[mid] > array[start]) {
			return start;
		}else if (array[mid] < array[end]) {
			return end
		}else {
			return mid;
		}
	}
}

public static void swap(int[] array, int i, int j) {
	int tmp = array[i];
	array[i] = array[j];
	array[j] = tmp;
}

private static int partition(int[] array,int start, int end) {
	 int tmp = array[start];
	 while(start < end) {
	 	while(start < end && array[end] >= tmp) {
	 		end--;
	 	}
	 	//end 下标遇到了 < tmp 的值
	 	array[start] = array[end];
	 	while(start < end && array[end] <= tmp) {
	 		start++;
	 	}
	 	//start 下标遇到了 > tmp 的值
	 	array[end] = array[start];
	 }
	 //start 和end 相遇,为基准
	 array[start] = array[tmp];
	 //返回基准的下标
	 return start;
}

8. 归并排序

8.1 两个有序数组合并

原理:

两个有序数组分别一个个元素比较,小的放入新的数组,然后往后走一步,再继续比较

1.

 2.

 3.

 

 4.

5.

 

 6.

 7.

8.

 

 代码实现

public static int[] mergeArray(int[] array1,int[] array2) {
        int[] tmp = new int[array1.length + array2.length+1];

        int k = 0;
        int s1 = 0;
        int e1 = array1.length-1;

        int s2 = 0;
        int e2 = array2.length-1;
        while(s1 <= e1 && s2 <= e2) {
            if (array1[s1] < array2[s2]) {
                tmp[k++] = array1[s1++];
                //k++
                //s1++
            }else {
                tmp[k++] = array2[s2++];
            }
        }
        //当有一个数组走完,另一个没走完,不满足上面循环时,
        //再继续走没走完的数组
        //因为不知道哪个走完了,所以两个数组都要单独写
        while(s1 <= e1) {
            tmp[k++] = array1[s1++];
        }
        while(s2 <= e2) {
            tmp[k++] = array2[s2++];
        }
        return tmp;
    }

 8.2 归并排序

原理:利用二叉树的递归实现

递归完每一个左子树就合并排序

 代码实现

public static void mergeSort(int[] array) {
	mergeSortInternal(array,0,array.length-1);
}

private static void mergeSortInternal(int[] array, int low, int high) {
	if (low >= high) {
		return;
	}
	int mid = low + (high-low) >>> 1;
	//左边
	mergeSortInternal(array,low,mid);
	//右边
	mergeSortInternal(array,mid)
	//合并
	merge(array,low,mid,high);
}

private void merge(int[] array, int low, int mid, int high) {
	int[] tmp = new int[high-low+1];
	int k = 0;

	int s1 = low;
	int e1 = mid;

	int s2 = low + 1;
	int e2 = high;

	while(s1 <= e1 && s2 <= e2) {
		if (array[s1] <= array[s2]) {
			tmp[k++] = array[s1++];
		}else {
			tmp[k++] = array[s2++];
		}
	} 

	while(s1 <= e1) {
		tmp[k++] = array[s1++];
	}
	while(s2 <= e2) {
		tmp[k++] = array[s2++];
	}

	//拷贝tmp的数组元素 放到原来数组array当中
	for (int i = 0; i < k; i++) {
		array[i+low] = tmp[i];
	}
}

* 时间复杂度:O(N*logN)
* 空间复杂度:O(N)

 * 稳定性:稳定的排序
 * 如果 array[s1] <= array[s2] 不取等号  那么就是不稳定的排序

8.3 非递归的归并排序

非递归的归并排序就是利用分组来两两归并,以 2*n 为一组,n 从0开始。

原理解析

 一个元素一组时

两个元素一组时

4个元素一组时

 

 代码实现


private void merge(int[] array, int low, int mid, int high) {
	int[] tmp = new int[high-low+1];
	int k = 0;

	int s1 = low;
	int e1 = mid;

	int s2 = low + 1;
	int e2 = high;

	while(s1 <= e1 && s2 <= e2) {
		if (array[s1] <= array[s2]) {
			tmp[k++] = array[s1++];
		}else {
			tmp[k++] = array[s2++];
		}
	} 

	while(s1 <= e1) {
		tmp[k++] = array[s1++];
	}
	while(s2 <= e2) {
		tmp[k++] = array[s2++];
	}

	//拷贝tmp的数组元素 放到原来数组array当中
	for (int i = 0; i < k; i++) {
		array[i+low] = tmp[i];
	}
}

/**
 * 非递归实现归并排序
 * @param array [description]
 */
public static void mergeSort(int[] array) {
	int nums = 1;//每组的数据个数
	while(nums < array.length) {
		//数组每次都要进行遍历,确定要归并的区间
		for (int i = 0; i < array.length; i += nums*2) {
			int left = i;
			int mid = left + nums - 1;
			//防止越界
			if (mid >= array.length) {
				mid = array.length - 1;
			}
			int right = mid + nums;
            //防止越界
			if (right >= array.length) {
				right = array.length - 1;
			}
			//下标确定之后,进行合并
			merge(array,left,mid,right);
		}
		nums *= 2;
	}
}

  • 14
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 12
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Fly upward

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值