标签:stat 课程 下界 分治 应用 计算机 数组 指针 大量数据
Java 排序算法十种常见排序算法可以分为两大类:
非线性时间比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此称为非线性时间比较类排序。
线性时间非比较类排序:不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此称为线性时间非比较类排序。
稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面。
不稳定:如果a原本在b的前面,而a=b,排序之后 a 可能会出现在 b 的后面。
时间复杂度:对排序数据的总的操作次数。反映当n变化时,操作次数呈现什么规律。
空间复杂度:是指算法在计算机内执行时所需存储空间的度量,它也是数据规模n的函数。
/**
* 冒泡排序
* 分类 -------------- 内部比较排序
* 数据结构 ---------- 数组
* 最差时间复杂度 ---- O(n^2)
* 最优时间复杂度 ---- 如果能在内部循环第一次运行时,使用一个旗标来表示有无需要交换的可能,可以把最优时间复杂度降低到O(n)
* 平均时间复杂度 ---- O(n^2)
* 所需辅助空间 ------ O(1)
* 稳定性 ------------ 稳定
*/
public void bubble (int []array){
int temp;//交换数据
System.out.println("冒泡排序:");
for(int i=0;i<array.length-1;i++) { //最后一个元素不需要比,所以总次数减少1
for(int j=0;j<array.length-1-i;j++) { //每次少比一回
if(array[j]>array[j+1]) { //大于时交换升降,反之降序
temp=array[j];
array[j]=array[j+1];
array[j+1]=temp;
}
}
}
}
n个记录的直接选择排序可经过n-1趟直接选择排序得到有序结果。具体算法描述如下:
/**
* 选择排序
* 分类 -------------- 内部比较排序
* 数据结构 ---------- 数组
* 最差时间复杂度 ---- O(n^2)
* 最优时间复杂度 ---- O(n^2)
* 平均时间复杂度 ---- O(n^2)
* 所需辅助空间 ------ O(1)
* 稳定性 ------------ 不稳定
*/
public void selection (int []arr){
int len = arr.length;
int minIndex, temp;
for (int i = 0; i < len - 1; i++) {
minIndex = i; //用来记住数组元素的下标
for (int j = i + 1; j < len; j++) {
if (arr[j] < arr[minIndex]) { // 寻找最小的数
minIndex = j; // 将最小数的索引保存
}
}
//一轮排序进行一次数组位置交换
if(i!=minIndex) {
temp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = temp;
}
}
}
/**
* 插入排序
* 分类 ------------- 内部比较排序
* 数据结构 ---------- 数组
* 最差时间复杂度 ---- 最坏情况为输入序列是降序排列的,此时时间复杂度O(n^2)
* 最优时间复杂度 ---- 最好情况为输入序列是升序排列的,此时时间复杂度O(n)
* 平均时间复杂度 ---- O(n^2)
* 所需辅助空间 ------ O(1)
* 稳定性 ------------ 稳定
*/
public void insertion (int []arr){
int len = arr.length;
int preIndex, current;
for (int i = 1; i < len; i++) {
preIndex = i - 1;
current = arr[i];
while (preIndex >= 0 && arr[preIndex] > current) {
arr[preIndex + 1] = arr[preIndex];
preIndex--;
}
arr[preIndex + 1] = current;
}
}
/**
* 希尔排序
* 分类 -------------- 内部比较排序
* 数据结构 ---------- 数组
* 最差时间复杂度 ---- 根据步长序列的不同而不同。已知最好的为O(n(logn)^2)
* 最优时间复杂度 ---- O(n)
* 平均时间复杂度 ---- 根据步长序列的不同而不同。
* 所需辅助空间 ------ O(1)
* 稳定性 ------------ 不稳定
*/
public void shellSort(int[] arrays) {
if (arrays == null || arrays.length <= 1) {
return;
}
//增量
int incrementNum = arrays.length / 2;
while (incrementNum >= 1) {
for (int i = 0; i < arrays.length; i++) {
//进行插入排序
for (int j = i; j < arrays.length - incrementNum; j = j + incrementNum) {
if (arrays[j] > arrays[j + incrementNum]) {
int temple = arrays[j];
arrays[j] = arrays[j + incrementNum];
arrays[j + incrementNum] = temple;
}
}
}
//设置新的增量
incrementNum = incrementNum / 2;
}
System.out.println(Arrays.toString(arrays));
}
实现步骤(分而治之)
/**
* 归并排序
* 分类 -------------- 内部比较排序
* 数据结构 ---------- 数组
* 最差时间复杂度 ---- O(nlogn)
* 最优时间复杂度 ---- O(nlogn)
* 平均时间复杂度 ---- O(nlogn)
* 所需辅助空间 ------ O(n)
* 稳定性 ------------ 稳定
*/
public class MergeSort {
public static void main(String[] args) {
int[] arr = {9, 8, 7, 6, 5, 4, 3, 2, 1};
sort(arr);
System.out.println(Arrays.toString(arr));
}
public static void sort(int[] arr) {
int[] temp = new int[arr.length];//在排序前,先建好一个长度等于原数组长度的临时数组,避免递归中频繁开辟空间
sort(arr, 0, arr.length - 1, temp);
}
private static void sort(int[] arr, int left, int right, int[] temp) {
if (left < right) {
int mid = (left + right) / 2;
sort(arr, left, mid, temp);//左边归并排序,使得左子序列有序
sort(arr, mid + 1, right, temp);//右边归并排序,使得右子序列有序
merge(arr, left, mid, right, temp);//将两个有序子数组合并操作
}
}
private static void merge(int[] arr, int left, int mid, int right, int[] temp) {
int i = left;//左序列指针
int j = mid + 1;//右序列指针
int t = 0;//临时数组指针
while (i <= mid && j <= right) {
if (arr[i] <= arr[j]) {
temp[t++] = arr[i++];
} else {
temp[t++] = arr[j++];
}
}
while (i <= mid) {//将左边剩余元素填充进temp中
temp[t++] = arr[i++];
}
while (j <= right) {//将右序列剩余元素填充进temp中
temp[t++] = arr[j++];
}
t = 0;
//将temp中的元素全部拷贝到原数组中
while (left <= right) {
arr[left++] = temp[t++];
}
}
}
/**
* 快速排序
* 分类 ------------ 内部比较排序
* 数据结构 --------- 数组
* 最差时间复杂度 ---- 每次选取的基准都是最大(或最小)的元素,导致每次只划分出了一个分区,需要进行n-1次划分才能结束递归,时间复杂度为O(n^2)
* 最优时间复杂度 ---- 每次选取的基准都是中位数,这样每次都均匀的划分出两个分区,只需要logn次划分就能结束递归,时间复杂度为O(nlogn)
* 平均时间复杂度 ---- O(nlogn)
* 所需辅助空间 ------ 主要是递归造成的栈空间的使用(用来保存left和right等局部变量),取决于递归树的深度,一般为O(logn),最差为O(n)
* 稳定性 ---------- 不稳定
*/
public class QuickSort {
public static void main(String[] args) {
int[] a = {1, 2, 4, 5, 7, 4, 5, 3, 9, 0};
System.out.println(Arrays.toString(a));
quickSort(a);
System.out.println(Arrays.toString(a));
}
public static void quickSort(int[] a) {
if (a.length > 0) {
quickSort(a, 0, a.length - 1);
}
}
private static void quickSort(int[] a, int low, int high) {
//1,找到递归算法的出口
if (low > high) {
return;
}
//2, 存
int i = low;
int j = high;
//3,key
int key = a[low];
//4,完成一趟排序
while (i < j) {
//4.1 ,从右往左找到第一个小于key的数
while (i < j && a[j] > key) {
j--;
}
// 4.2 从左往右找到第一个大于key的数
while (i < j && a[i] <= key) {
i++;
}
//4.3 交换
if (i < j) {
int p = a[i];
a[i] = a[j];
a[j] = p;
}
}
// 4.4,调整key的位置
int p = a[i];
a[i] = a[low];
a[low] = p;
//5, 对key左边的数快排
quickSort(a, low, i - 1);
//6, 对key右边的数快排
quickSort(a, i + 1, high);
}
}
import java.util.Arrays;
/**
* 堆排序
* 分类 -------------- 内部比较排序
* 数据结构 ---------- 数组
* 最差时间复杂度 ---- O(nlogn)
* 最优时间复杂度 ---- O(nlogn)
* 平均时间复杂度 ---- O(nlogn)
* 所需辅助空间 ------ O(1)
* 稳定性 ------------ 不稳定
*/
public class HeapSort {
public static void main(String[] args) {
int a[] = { 51, 46, 20, 18, 65, 97, 82, 30, 77, 50 };
heapSort(a);
System.out.println(Arrays.toString(a));
}
/**
* 构建大顶堆
*/
public static void adjustHeap(int[] a, int i, int len) {
int temp, j;
temp = a[i];
for (j = 2 * i; j < len; j *= 2) {// 沿关键字较大的孩子结点向下筛选
if (j < len && a[j] < a[j + 1])
++j; // j为关键字中较大记录的下标
if (temp >= a[j])
break;
a[i] = a[j];
i = j;
}
a[i] = temp;
}
public static void heapSort(int[] a) {
int i;
for (i = a.length / 2 - 1; i >= 0; i--) {// 构建一个大顶堆
adjustHeap(a, i, a.length - 1);
}
for (i = a.length - 1; i >= 0; i--) {// 将堆顶记录和当前未经排序子序列的最后一个记录交换
int temp = a[0];
a[0] = a[i];
a[i] = temp;
adjustHeap(a, 0, i - 1);// 将a中前i-1个记录重新调整为大顶堆
}
}
}
/**
* 计数排序
* 分类 ------------ 内部非比较排序
* 数据结构 --------- 数组
* 最差时间复杂度 ---- O(n + k)
* 最优时间复杂度 ---- O(n + k)
* 平均时间复杂度 ---- O(n + k)
* 所需辅助空间 ------ O(n + k)
* 稳定性 ----------- 稳定
*/
public class CountSort {
private static int[] countSort(int[] array,int k)
{
int[] C=new int[k+1];//构造C数组
int length=array.length,sum=0;//获取A数组大小用于构造B数组
int[] B=new int[length];//构造B数组
for(int i=0;i<length;i++)
{
C[array[i]]+=1;// 统计A中各元素个数,存入C数组
}
for(int i=0;i<k+1;i++)//修改C数组
{
sum+=C[i];
C[i]=sum;
}
for(int i=length-1;i>=0;i--)//遍历A数组,构造B数组
{
B[C[array[i]]-1]=array[i];//将A中该元素放到排序后数组B中指定的位置
C[array[i]]--;//将C中该元素-1,方便存放下一个同样大小的元素
}
return B;//将排序好的数组返回,完成排序
}
public static void main(String[] args)
{
int[] A=new int[]{2,5,3,0,2,3,0,3};
int[] B=countSort(A, 5);
System.out.println(Arrays.toString(B));
}
}
/**
* 桶排序
* * 分类 ------------- 内部非比较排序
* 数据结构 --------- 数组
* 最差时间复杂度 ---- O(nlogn)或O(n^2),只有一个桶,取决于桶内排序方式
* 最优时间复杂度 ---- O(n),每个元素占一个桶
* 平均时间复杂度 ---- O(n),保证各个桶内元素个数均匀即可
* 所需辅助空间 ------ O(n + bn)
* 稳定性 ----------- 稳定
*/
public static void bucketSort(int[] arr){
int max = Integer.MIN_VALUE;
int min = Integer.MAX_VALUE;
for(int i = 0; i < arr.length; i++){
max = Math.max(max, arr[i]);
min = Math.min(min, arr[i]);
}
//桶数
int bucketNum = (max - min) / arr.length + 1;
ArrayList<ArrayList<Integer>> bucketArr = new ArrayList<>(bucketNum);
for(int i = 0; i < bucketNum; i++){
bucketArr.add(new ArrayList<Integer>());
}
//将每个元素放入桶
for(int i = 0; i < arr.length; i++){
int num = (arr[i] - min) / (arr.length);
bucketArr.get(num).add(arr[i]);
}
//对每个桶进行排序
for(int i = 0; i < bucketArr.size(); i++){
Collections.sort(bucketArr.get(i));
}
System.out.println(bucketArr.toString());
}
/**
* 基数排序
* 分类 ------------- 内部非比较排序
* 数据结构 ---------- 数组
* 最差时间复杂度 ---- O(n * dn)
* 最优时间复杂度 ---- O(n * dn)
* 平均时间复杂度 ---- O(n * dn)
* 所需辅助空间 ------ O(n * dn)
* 稳定性 ----------- 稳定
*/
private static void radixSort(int[] array,int d)
{
int n=1;//代表位数对应的数:1,10,100...
int k=0;//保存每一位排序后的结果用于下一位的排序输入
int length=array.length;
int[][] bucket=new int[10][length];//排序桶用于保存每次排序后的结果,这一位上排序结果相同的数字放在同一个桶里
int[] order=new int[length];//用于保存每个桶里有多少个数字
while(n<d)
{
for(int num:array) //将数组array里的每个数字放在相应的桶里
{
int digit=(num/n)%10;
bucket[digit][order[digit]]=num;
order[digit]++;
}
for(int i=0;i<length;i++)//将前一个循环生成的桶里的数据覆盖到原数组中用于保存这一位的排序结果
{
if(order[i]!=0)//这个桶里有数据,从上到下遍历这个桶并将数据保存到原数组中
{
for(int j=0;j<order[i];j++)
{
array[k]=bucket[i][j];
k++;
}
}
order[i]=0;//将桶里计数器置0,用于下一次位排序
}
n*=10;
k=0;//将k置0,用于下一轮保存位排序结果
}
}
欢迎进×××流学习,更多不定期福利、免费课程等你来~
QQ群号:560819979
敲门砖(验证信息):浪淘沙
标签:stat 课程 下界 分治 应用 计算机 数组 指针 大量数据
原文地址:http://blog.51cto.com/13477015/2174330