之前学习数据结构与算法时花了三天时间整理九大排序算法,并采用Java语言来实现,今天第一次写博客,刚好可以把这些东西从总结的文档中拿出来与大家分享一下,同时作为自己以后的备忘录。

1.排序算法时间复杂度、稳定性分类:

2.排序算法问题描述与实现

2.1冒泡排序(交换排序-稳定)

【问题描述】对于一个int数组,请编写一个冒泡排序算法,对数组元素排序。 
问题分析:冒泡排序,顾名思义,从前往后遍历,每次遍历在末尾固定一个最大值。

易错点:每次内层循环结束都会在末尾确定一个元素的位置,因此内层循环的判断条件要减去外层的循环次数i

public int[] BobbleSort(int[] array){

for(int i=0;i<array.length-1;i++){

for(int j=0;j<array.length-1-i;j++){  //内层循环,注意array.length-1-i,一定要-i

swap(array,j,j+1);//交换数据元素的方法

}

}

return array;

}

public void swap(int[] array,int i,int j){//交换数据元素的方法

if(array[i]>array[j]){

int temp=array[i];

array[i]=array[j];

array[j]=temp;

}

}

冒泡排序的改进:加一个布尔型的flag,当某一趟冒泡排序没有元素交换时,则冒泡结束,元素已经有序,可以有效的减少冒泡次数。

2.2 选择排序(不稳定)

【问题描述】对于一个int数组,请编写一个选择排序算法,对数组元素排序。  
问题分析:选择排序,从前往后遍历,每次遍历从头开始依次选择最小的来排序。

注意点:

【初始升序】:交换0次,时间复杂度为o(n); 【初始降序】:交换n-1次,时间复杂度为o(n^2)。

【特点】:交换移动数据次数少,比较次数多。

public int[] choose(int[] array){

for(int i=0;i<array.length-1;i++){

int min=i;

for (int j = i+1; j < array.length; j++) {

if(array[min]>array[j])

min=j;

}

if(i!=min){

swap(array,i,min);

}

}

return array;

}

public void swap(int[] array,int i,int j){

if(array[i]>array[j]){

int temp=array[i];

array[i]=array[j];

array[j]=temp;

}

}

2.3 直接插入排序(稳定的 )

【问题描述】对于一个int数组,请编写一个插入排序算法,对数组元素排序。  
问题分析:插入排序,从前往后遍历,每次遍历确定一个元素的位置,下一个元素从后往前依次与排序好的元素比较,类似于摸扑克牌。

注意点:假定n是数组的长度,首先假设第一个元素被放置在正确的位置上,这样仅需从1-n-1范围内对剩余元素进行排序。对于每次遍历,从0-i-1范围内21.的元素已经被排好序,每次遍历的任务是:通过扫描前面已排序的子列表,将位置i处的元素定位到从0到i的子列表之内的正确的位置上。将arr[i]复制为一个名为target的临时元素。向下扫描列表,比较这个目标值target与arr[i-1]、arr[i-2]的大小,依次类推。这个比较过程在小于或等于目标值的第一个元素(arr[j])处停止,或者在列表开始处停止(j=0)。在arr[i]小于前面任何已排序元素时,后一个条件(j=0)为真,因此,这个元素会占用新排序子列表的第一个位置。在扫描期间,大于目标值target的每个元素都会向右滑动一个位置(arr[j]=arr[j-1])。一旦确定了正确位置j,目标值target(即原始的arr[i])就会被复制到这个位置。与选择排序不同的是,插入排序将数据向右滑动,并且不会执行交换。

public int[] insert1(int[] array){

for(int i=1;i<array.length;i++){

int j=i;

int temp=array[i];

while(j>0&&array[j-1]>temp){

array[j]=array[j-1];//元素右移

j--;

}

array[j]=temp;

}

return array;

}

2.4 希尔排序(2for循环+最后一个while,不稳定

问题描述:对于一个int数组,请编写一个希尔排序算法,对数组元素排序。  
问题分析:希尔排序是改进的插入排序,算法先将要排序的一组数按某个增量d(n/2,n为要排序数的个数)分成若干组,每组中记录的下标相差d.对每组中全部元素进行直接插入排序,然后再用一个较小的增量(d/2)对它进行分组,在每组中再进行直接插入排序。当增量减到1时,进行直接插入排序后,排序完成。希尔排序法(缩小增量法) 属于插入类排序,是将整个无序列分割成若干小的子序列分别进行插入排序的方法。

  • public int[] hillInsert(int[] array){
  • for (int d = array.length>>1; d>=1; d=d>>1) {
  • for(int i=d;i<array.length;i++){
  • int j=i;
  • int temp=array[i];
  • while(j>=d&&array[j-d]>temp){
  • array[j]=array[j-d];
  • j-=d;
  • }
  • array[j]=temp;
  • }
  • }
  • return array;
  • }

2.5.堆排序(不稳定)

 问题描述:对于一个int数组,请编写一个堆排序算法,对数组元素排序。  
问题分析:
堆数据结构是一种数组对象,它可以被视为一科完全二叉树结构(完全二叉树是由满二叉树而引出来的。对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树)。它的特点是父节点的值大于(小于)两个子节点的值(分别称为大顶堆和小顶堆)。它常用于管理算法执行过程中的信息,应用场景包括堆排序优先队列等。

完全二叉树性质:如果i>1,则双亲是结点[i/2]。也就是说下标i与2i和2i+1是双亲子女关系。 当排序对象为数组时,下标从0开始,所以下标 i 与下标 2i+1和2i+2是双亲子女关系。

算法思路:

  • 堆排序:(大根堆)
  • ①将存放在array[0,...,n-1]中的n个元素建成初始堆;
  • ②将堆顶元素与堆底元素进行交换,则序列的最大值即已放到正确的位置;
  • ③但此时堆被破坏,将堆顶元素向下调整使其继续保持大根堆的性质,再重复第②③步,直到堆中仅剩下一个元素为止。
  • 堆排序算法的性能分析:
  • 空间复杂度:o(1);
  • 时间复杂度:建堆:o(n),每次调整o(log n),故最好、最坏、平均情况下:o(n*logn);
  • 稳定性:不稳定
  • public static int[] heapMax(int[] array){  //建立大根堆
  • for(int i=(array.length-2)/2;i>=0;i--){
  • buildHeap(array,i,array.length);
  • }
  • return array;
  • }
  • private static void buildHeap(int[] array, int k, int length) {  //建堆方法
  • // TODO Auto-generated method stub
  • int temp=array[k];
  • for(int i=2*k+1;i<length-1;i=2*i+1){
  • if(array[i]<array[i+1]){
  • i++;
  • }
  • if(temp>=array[i])
  • break;
  • else{
  • array[k]=array[i];
  • k=i;
  • }
  • }
  • array[k]=temp;
  • }
  • public static int[] heapSortArray(int[] array){ //进行堆排序
  • array=heapMax(array);
  • for (int i =array.length-1; i >0; i--) {
  • int temp=array[0];
  • array[0]=array[i];
  • array[i]=temp;
  • buildHeap(array,0,i);
  • }
  • return array;
  • }

2.6 归并排序(稳定)

问题描述:对于一个int数组,请编写一个归并排序算法,对数组元素排序。 
问题分析:归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide
and Conquer)的一个非常典型的应用。首先考虑下如何将将二个有序数列合并。这个非常简单,只要从比较二个数列的第一个数,谁小就先取谁,取了后就在对应数列中删除这个数。然后再进行比较,如果有数列为空,那直接将另一个数列的数据依次取出即可。可以看出合并有序数列的效率是比较高的,可以达到O(n)。

解决了上面的合并有序数列问题,再来看归并排序,其的基本思路就是将数组分成二组A,B,如果这二组组内的数据都是有序的,那么就可以很方便的将这二组数据进行排序。如何让这二组组内数据有序了?

可以将A,B组各自再分成二组。依次类推,当分出来的小组只有一个数据时,可以认为这个小组组内已经达到了有序,然后再合并相邻的二个小组就可以了。这样通过先递归的分解数列,再合并数列就完成了归并排序。

public
int[]
mergeSort(int[]
A, int n) {

//归并排序,递归做法,分而治之

mSort(A,0,n-1);

return A;

}

private void mSort(int[] A,int
left,int right){

//分而治之,递归常用的思想,跳出递归的条件

if(left>=right){

return;

}

//中点

int
mid
= (left+right)/2;

//有点类似后序遍历!

mSort(A,left,mid);

mSort(A,mid+1,right);

merge(A,left,mid,right);

}

//将左右俩组的按序子序列排列成按序序列

private void
merge(int[]
A,int left,int mid,int rightEnd){

//充当tem数组的下标

int
record
= left;

//最后复制数组时使用

int
record2
= left;

//右子序列的开始下标

int
m =mid+1;

int[]
tem
= new
int[A.length];

//只要left>mid或是m>rightEnd,就跳出循环

while(left<=mid&&m<=rightEnd){

if(A[left]<=A[m]){

tem[record++]=A[left++];

}else{

tem[record++]=A[m++];

}

}

while(left<=mid){

tem[record++]=A[left++];

}

while(m<=rightEnd){

tem[record++]=A[m++];

}

//复制数组

for(
;record2<=rightEnd;record2++){

A[record2] = tem[record2];

}

}

2.7 快速排序算法(不稳定)

问题描述:对于一个int数组,请编写一个快速排序算法,对数组元素排序。 
问题分析:快速排序(Quicksort)是对冒泡排序的一种改进,使用分治法(Divide and conquer)策略来把一个序列(list)分为两个子序列(sub-lists)。

从数列中挑出一个元素,称为”枢轴”(pivot)。重新排序数列,所有元素比枢轴值小的摆放在基准前面,所有元素比枢轴值大的摆在枢轴的后面(相同的数可以到任一边)。

在这个分区结束之后,该枢轴就处于数列的中间位置。这个称为分区(partition)操作。递归地(recursive)把小于枢轴值元素的子数列和大于枢轴值元素的子数列排序。

  • public static final void quickSort(int[] array, intstart, intend) {
  • // i相当于助手1的位置,j相当于助手2的位置
  • int i = start, j = end;
  • int pivot = array[i]; // 取第1个元素为基准元素
  • int emptyIndex = i; // 表示空位的位置索引,默认为被取出的基准元素的位置
  • // 如果需要排序的元素个数不止1个,就进入快速排序(只要i和j不同,就表明至少有2个数组元素需要排序)
  • while (i < j) {
  • // 助手2开始从右向左一个个地查找小于基准元素的元素
  • while (i < j && pivot <= array[j])
  • j--;
  • if (i < j) {
  • // 如果助手2在遇到助手1之前就找到了对应的元素,就将该元素给助手1的"空位",j成了空位
  • array[emptyIndex] = array[emptyIndex = j];
  • }
  • // 助手1开始从左向右一个个地查找大于基准元素的元素
  • while (i < j && array[i] <= pivot)
  • i++;
  • if (i < j) {
  • // 如果助手1在遇到助手2之前就找到了对应的元素,就将该元素给助手2的"空位",i成了空位
  • array[emptyIndex] = array[emptyIndex = i];
  • }
  • }
  • // 助手1和助手2相遇后会停止循环,将最初取出的基准值给最后的空位
  • array[emptyIndex] = pivot;
  • // =====本轮快速排序完成=====
  • // 如果分割点i左侧有2个以上的元素,递归调用继续对其进行快速排序
  • if (i - start > 1) {
  • quickSort(array, 0, i - 1);
  • }
  • // 如果分割点j右侧有2个以上的元素,递归调用继续对其进行快速排序
  • if (end - j > 1) {
  • quickSort(array, j + 1, end);
  • }
  • }

算法优化:选取基准轴点时采用三数取中法:

public class Quick {

public void sort(int[] array){

int start=0;

int end=array.length-1;

quickSort(array,start,end);

}

public void quickSort(int[] array, int start, int end) {

// TODO Auto-generated method stub

int i=start;

int j=end;

int emptyIndex=start;

int pivot=middle3(array,start,end);

while(i<j){

while(i<j&&array[j]>=pivot){

j--;

}

if(i<j)

array[emptyIndex]=array[emptyIndex=j];

while(i<j&&array[i]<=pivot){

i++;

}

if(i<j)

array[emptyIndex]=array[emptyIndex=i];

}

array[emptyIndex]=pivot;

if(i-start>1)

quickSort(array,start,i-1);

if(end-j>1)

quickSort(array,j+1,end);

}

2.8 计数排序算法(稳定,也是桶排序)

问题描述:对于一个int数组,请编写一个计数排序算法,对数组元素排序。 
问题分析:

1. 提前必须是已知待排序的关键字为整型且范围已知。 
2. 时间复杂度为O(n+k),n指的是桶的个数,k指的是待排序数组的长度,不是基于比较的排序算法,因此效率非常之高。 
3. 稳定性好,这个是计数排序非常重要的特性,可以用在后面介绍的基数排序中。 
4. 但需要一些辅助数组,如C[0..k],因此待排序的关键字范围0~k不宜过大。

public int[] countingSort(int[] A, int n) {

if(A==null ||n<2){

return A;

}

//找出桶的范围,即通过要排序的数组的最大最小值来确定桶范围

int min=A[0];

int max=A[0];

for(int i=0;i<n;i++){

min=Math.min(A[i],min);

max=Math.max(A[i],max);

}

//确定桶数组,桶的下标即为需排序数组的值,桶的值为序排序数同一组值出现的次数

int[] arr = new int[max-min+1];

//往桶里分配元素

for(int i=0;i<n;i++){

arr[A[i]-min]++;

}

//从桶中取出元素

int index=0;

for(int i=0;i<arr.length;i++){

while(arr[i]-->0){

A[index++]=i+min;

}

}

return A;

}

}

2.9 基数排序算法(稳定)

问题描述:对于一个int数组,请编写一个基数排序算法,对数组元素排序。 
问题分析:

基数排序(Radix sort)是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。由于整数也可以表达字符串(比如名字或日期)和特定格式的浮点数,所以基数排序也不是只能使用于整数。将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列。

键字范围0~k不宜过大。

public void sort(int[] array){

int max=0;

int d=0;// 位数

for (int i = 0; i < array.length; i++) {

max=Math.max(max, array[i]);

}

while(max>0){

max/=10;

d++;

}

int t=0;

int m=1;

int n=1;

int[][] temp=new int[10][array.length];

int[] order=new int[10];

while(m<=d){

for (int i = 0; i < array.length; i++) {

int k=((array[i]/n)%10);

temp[k][order[k]++]=array[i];

}

for (int i = 0; i < 10; i++) {

if(order[i]!=0){

for (int j = 0; j <order[i]; j++) {

array[t++]=temp[i][j];

}

order[i]=0;

}

}

t=0;

n=n*10;

m++;

}

}

采用链表的方式来实现基数排序:

//基数排序开始
public static void radixSort(int[] array){
int max=array[0];
for (int i = 0; i < array.length; i++) {
max=Math.max(max, array[i]);
}
int time=0;
while(max!=0){
time++;
max=max/10;
}
List<List<Integer>> list=new ArrayList<List<Integer>>();
for(int i=0;i<10;i++){
List<Integer> item=new ArrayList<Integer>();
list.add(item);
}

for (int i = 0; i < time; i++) {
for (int j = 0; j < array.length; j++) {
int index=array[j]%(int)Math.pow(10, i+1);
index/=(int)Math.pow(10, i);
list.get(index).add(array[j]);
}
int count=0;
for (List<Integer> a : list) {
for (int m : a) {
if(m!=0){
array[count++]=m;
}
}
a.clear();
}
}
}

//基数排序结束

九大排序算法Java实现的更多相关文章

  1. C语言实现九大排序算法

    C语言实现九大排序算法 直接插入排序 折半插入排序 希尔排序 冒泡排序 快速排序 直接选择排序 堆排序 归并排序 基数排序 C语言实现九大排序算法 直接插入排序 将数组分为两个部分,一个是有序部分,一 ...

  2. 九大排序算法Demo

    1. 冒泡排序 冒泡排序(Bubble Sort)是一种简单的排序算法.它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来.走访数列的工作是重复地进行直到没有再需要交换, ...

  3. 【转】九大排序算法-C语言实现及详解

    概述 排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存. 我们这里说说八大排序就是内部排序. 当n较大, ...

  4. 10大排序算法——Java实现

    算法与实现 选择排序 算法思想 从数组中选择最小元素,将它与数组的第一个元素交换位置.再从数组剩下的元素中选择出最小的元素,将它与数组的第二个元素交换位置.不断进行这样的操作,直到将整个数组排序. 动 ...

  5. 九大排序算法的Java实现

    1.冒泡排序 package Sort; import java.util.Arrays; public class BubbleSort { public static void main(Stri ...

  6. 你需要知道的九大排序算法【Python实现】之堆排序

    六.堆排序 ​堆排序是一种树形选择排序,是对直接选择排序的有效改进. ​堆的定义下:具有n个元素的序列 (h1,h2,...,hn),当且仅当满足(hi>=h2i,hi>=2i+1)或(h ...

  7. 你需要知道的九大排序算法【Python实现】之插入排序

    三.插入排序 基本思想:插入排序的基本操作就是将一个数据插入到已经排好序的有序数据中,从而得到一个新的.个数加一的有序数据,算法适用于少量数据的排序,时间复杂度为O(n^2).是稳定的排序方法.插入算 ...

  8. 你需要知道的九大排序算法【Python实现】之基数排序

    八.基数排序 基本思想:基数排序(radix sort)属于"分配式排序"(distribution sort),又称"桶子法"(bucket sort)或bi ...

  9. 你需要知道的九大排序算法【Python实现】之快速排序

    五.快速排序 基本思想:  通过一趟排序将待排序记录分割成独立的两部分,其中一部分记录的关键字均比另一部分关键字小,则分别对这两部分继续进行排序,直到整个序列有序. 算法实现: ​ #coding: ...

随机推荐

  1. 安装sql server

    因为电脑中只有mysql数据库,所以昨天准备安装一个sql server.安装中出现了许多问题,首先第一遍的时候,安装组件中没有勾选管理工具这个选项,所以在最后的时候,文件夹中只有配置管理器,没有数据 ...

  2. 【Consul】Consul架构-Consensus协议

    Consul使用Consensus协议提供一致性(Consistency)--CAP定义的一致性.Consensus协议是基于"Raft:In search of an Understand ...

  3. 9 udp广播

    udp有广播  写信 tcp没有广播·  打电话 #coding=utf-8 import socket, sys dest = ('<broadcast>', 7788) # 创建udp ...

  4. 实用脚本 4 -- Makefile(不同文件下的多个可执行文件or静态库编译到同一目录下)

    不同文件下的多个可执行文件编译到同一目录下,这样方便观察编译结果,从而方便进程操作.使用时根据自己的需要在进行局部修改(如 链接库.目标文件等等). 1..bashrc 中设置编译主目录(例如) ex ...

  5. java线程池技术

    1.线程池的实现原理?简介: 多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力.假设一个服务器完成一项任务所需时间为:T1 创建线程时间, ...

  6. jmeter动态获取jsessionid

    思想是在一个线程组内添加一个cookie管理器,登录之后,用正则提取到sessionid,该线程组下的操作便可以共享这个session了. 1. 依次新建线程组.cookie管理器.http请求-登录 ...

  7. Mac上配置Cocos2d-x开发环境(多平台:Android/iOS)

    下载以下资源: Cocos2d-x (http://www.cocos2d-x.org) Android NDK(http://developer.android.com/tools/sdk/ndk/ ...

  8. 05-Mysql数据库----补充内容

    数据库命名规则: 数据库命名规则: 可以由字母.数字.下划线.@.#.$ 区分大小写 唯一性 不能使用关键字如 create select 不能单独使用数字 最长128位 # 基本上跟python或者 ...

  9. [PocketFlow]解决在coco上mAP非常低的bug

    1.问题 继上次训练挂起的bug后,又遇到了现在评估时AP非常低的bug.具体有多低呢?Pelee论文中提到,用128的batchsize大小在coco数据集上训练70K次迭代后,AP@0.5:0.9 ...

  10. JavaScript Map数据结构

    Array.prototype.remove = function (s) { for (var i = 0; i < this.length; i++) { if (s == this[i]) ...