java异或排序算法_排序算法(java版)

一直想理解一下基本的排序算法,最近正好在搞java所以就一并了(为了便于理解,这儿都是以从小到大排序为目的)

冒泡排序

也就是比较连续的两个值,如果前面一个值大于后面一个值,则交换。

时间复杂度为O(n*n),是稳定排序(稳定性意思:当两个值相同时,排序前后的这两个值的相对位置是否有交换)

注意点:第二重循环参数

//冒泡排序,从小到大

private static void BubbleSort(int[] arr, intn) {for (int i = 0; i < n; ++i) {for (int j = 0; j < n - i - 1; ++j) {if (arr[j] > arr[j + 1])

Swap(arr, j, j+ 1);

}

}

}

选择排序

就是从头到尾选择每个位置放这个位置到最后一个位置的最小值,其中我们可以标记,减少交换次数

时间复杂度为O(n*n),不稳定的排序

注意点:判断后再进行异或的交换

//选择排序

private static void SelectSort(int[] arr, intn) {inttemp;for (int i = 0; i < n; ++i) {

temp=i;for (int j = i + 1; j < n; ++j) {if (arr[temp] >arr[j]) {

temp=j;

}

}//异或交换时必须判断

if (i !=temp)

Swap(arr, i, temp);

}

}

直接插入排序

就是从头到尾遍历每个点,每个点再从尾到头找这个值放在数组的位置

时间复杂度为O(n*n),稳定的排序

注意点:temp与谁比较

//直接插入排序

private static void InsertSort(int[] arr, intn) {//哨兵

inttemp;for (int i = 1; i < n; ++i) {

temp=arr[i];int k = i - 1;//注意从后向前

for (int j = k; j >= 0 && temp < arr[j]; --j, --k) {

arr[j+ 1] =arr[j];

}

arr[k+ 1] =temp;

}

}

快速排序

其实就类似分治的方法,取一个中枢元素,然后找到一个位置放这个中枢元素,保证这个位置前面的值不大于它,后面的值不小于它,接着分别递归

最坏时间复杂度为O(n*n),平均时间复杂度为O(n*log2n),所以可以先打乱顺序。是不稳定的排序

注意点:中间两个while的顺序

//模拟洗牌打乱顺序

private static void Shuffle(int[] arr, intn) {

Random random= newRandom();for (int i = 0; i < n; ++i) {int x = random.nextInt(n - 1);int y = random.nextInt(n - 1);if (x !=y)

Swap(arr, x, y);

}

Print(arr, n);

}//快速排序(最坏时间是n*n,所以我们首先可以打乱顺序)

private static void quickSort(int[] arr, int left, intright) {//哨兵

int temp =arr[left];int i = left, j =right;//出口

if (i >=j)return;//保证左边一定不比temp大,右边一定不比temp小

while (i =temp) {--j;

}if (i

arr[i]=arr[j];++i;

}while (i < j && arr[i] <=temp) {++i;

}if (i

arr[j]=arr[i];--j;

}

}

arr[i]=temp;//左右递归

quickSort(arr, left, i - 1);

quickSort(arr, i+ 1, right);

}

二路归并排序

其实很简单,就是从中间分成左右两个数组分别递归,在最后返回时我们保证两个数组都是有序数组再合并,可以知道使用双指针的方法可以在O(n)的

时间内合并两个有序数组,然后知道这样分下去其实是几乎完全的二叉树,高度为log2n

时间复杂度为O(n*log2n),是稳定的排序

注意点:指针向后移动

//二路归并排序

private static void Marge(int[] arr, int left, int mid, intright) {int[] temp = new int[right - left + 1];int leftpos = left, rightpos = mid + 1;//双指针方法

for (int i = 0; i < right - left + 1; ++i) {if (rightpos > right || leftpos <= mid && arr[leftpos] <=arr[rightpos]) {

temp[i]= arr[leftpos++];

}else{

temp[i]= arr[rightpos++];

}

}for (int i = 0; i < right - left + 1; ++i) {

arr[i+ left] =temp[i];

}

}private static void MargeSort(int[] arr, int left, intright) {if (left >=right)return;//分成两段递归

int mid = (left + right >> 1);

MargeSort(arr, left, mid);

MargeSort(arr, mid+ 1, right);//关键:合并两端有序数组,只需用O(n)时间做到

Marge(arr, left, mid, right);

}

希尔排序

极其简单的算法,简答的方法是是设置步长并每次减一半,接着使用插入排序,排的是相隔步长的元素

时间复杂度很麻烦,但是最坏也是O(n*n),平均大概是O(n^3/2),是不稳定排序

//希尔排序

private static void HillSort(int[] arr, intn) {//设置步长,即保证相隔步长个元素为有序数组

for (int step = n / 2; step > 0; step /= 2)//修改的插入排序

for (int i = step; i < n; ++i)for (int j = i; j - step >= 0; j -=step)if (arr[j - step] >arr[j])

Swap(arr, j-step, j);

}

堆排序

为了让额外的空间复杂度为O(1),可以首先倒着使用原数组下沉的思想(就是保证了此点的子树一定是堆)建立大顶堆,

接着依次将头放到新空出来的位置,再更新堆

时间复杂度是O(n*log2n),是不稳定排序

注意点:边界值得大小

//堆的下沉,保证此树为堆

private static void DeleteEmp(int[] arr, int fat, intn) {intlefchild;int temp =arr[fat];for (; (fat << 1 | 1) < n; fat =lefchild) {

lefchild= fat << 1 | 1;if (lefchild < n - 1 && arr[lefchild] < arr[lefchild + 1])

lefchild++;if (arr[lefchild] >temp) {

arr[fat]=arr[lefchild];

}else{break;

}

}

arr[fat]=temp;

}//堆排序(额外的辅助空间为O(1)),聪明的方法是利用原来的空间直接建树,//但是从小到大时我们需要建立大顶堆,接着删除第一个点放到数组最后一个位置,倒着插入

private static void HeapSort(int[] arr, intn) {//建堆(注意普通建堆时间复杂度为O(n),因为上浮的平均时间复杂度为O(1))//保证以个点为树的都是堆,倒着来就可以保证整棵树

for (int i = n / 2; i >= 0; --i) {

DeleteEmp(arr, i, n);

}//堆顶放入后面,再调整堆

for (int i = n - 1; i > 0; --i) {

Swap(arr,0, i);

DeleteEmp(arr,0, i);

}

}

以上都是基于比较的排序方法,接下来介绍一些非基于比较的排序算法,而且都是线性的时间复杂度

计数排序

就是将数字变成数组下标,这种排序主要是保证数据范围比较小。

时间复杂度为O(k),是不稳定的排序

基数排序

首先从低到高枚举的是数字的每一位,每一位根据0-9的顺序放入桶中储存,再放回原数组,注意负数,还有就是非常耗费空间

时间复杂度为O(n),是稳定的排序

//基数排序,时间复杂度为O(n)的排序方式,m为最大数字的位数+1

private static void BaseSort(int[] arr, int n, intm) {//假设数字范围为(0,1000000000)

int[] order = new int[10];int[][] temp = new int[10][n];int mm = 0, k = 1;while (mm

for (int i = 0; i < n; ++i) {int last = arr[i] / k % 10;

temp[last][order[last]++] =arr[i];

}//10个桶中按顺序放回数组

int coun = 0;for (int i = 0; i < 10; ++i) {if (order[i] > 0) {for (int j = 0; j < order[i]; ++j)

arr[coun++] =temp[i][j];

order[i]= 0;

}

}

k*= 10;++mm;

}

}

桶排序

将数组分到有限数量的桶子里。每个桶子再个别排序

时间复杂度为O(n),是稳定的排序

接着还有一些比较奇特的排序方式:

鸡尾酒排序:双向冒泡排序

梳排序:在冒泡排序下让步长不为1,而且每次步长除以1.3

奇偶排序:多核处理有优势;先将待排序数组的所有奇数位与自己身后相邻的偶数位相比较,如果前者大于后者,则进行交换,直到这一趟结束。然后将偶数位与自己身后相邻的奇数位相比较,如果前者大于后者,则进行交换,直到这一趟结束。重复

外排序:以上都是在内存中排序的算法,即可以在内存中直接寻址,而外排序则可能是输入数据在磁带上,它使用的是归并排序,但是可以是多路归并排序

树形选择排序(锦标赛排序) :对N个数字,选出最小(大)的n/2个数,再进行新一轮的比较,直到选出最小(大)的。

1.把N个数放到完全二叉树的叶子节点,两两比较,选出最小的作为根节点,且保存到数组中

2.把最小的原始值设为无穷大,从那个地方开始新一轮比较

第一次需要比较n-1次,之后都是log2(n)次

小结

对于一般的内部排序,基本使用的是插入排序,希尔排序或者快速排序,主要是根据输入数据的大小来确定。

注意希尔排序可以使用Sedgewick增量运行,则预计运行时间就为O(N^7/6),而快速排序找枢纽可以使用三数中值分割法。

堆排序比希尔排序要慢,但是也可以根据Floyd提出的改进算法移动一次数据来优化。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值