之前学习数据结构与算法时花了三天时间整理九大排序算法,并采用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. 4.HBASE数据迁移方案(之snapshot):

    4.HBASE数据迁移方案:  4.1 Import/Export  4.2 distcp  4.3 CopyTable  4.4 snapshot 快照方式迁移(以USER_info:user_lo ...

  2. java二分法来求一个数组中一个值的key

    package TestArray; import java.util.Arrays; /** * 二分法查找 */ public class Test { public static void ma ...

  3. EF更新时出错,An error occurred while updating the entries. See the inner exception for details

           在使用EF进行更新数据时出错,报出的异常是 "An error occurred while updating the entries. See the inner excep ...

  4. Java并发基础--线程安全

    一.线程安全 1.线程安全的概念 线程安全:某个类被单个线程,或者多个线程同时访问,所表现出来的行为是一致,则可以说这个类是线程安全的. 2.什么情况下会出现线程安全问题 在单线程中不会出现线程安全问 ...

  5. 九度OJ--Q1163

    import java.util.ArrayList;import java.util.Scanner; /* * 题目描述: * 输入一个整数n(2<=n<=10000),要求输出所有从 ...

  6. java设计模式之责任链模式以及在java中作用

    责任链模式是一种对象的行为模式.在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链.请求在这个链上传递,直到链上的某一个对象决定处理此请求.发出这个请求的客户端并不知道链上的哪一个 ...

  7. LCA(最近公共祖先)——离线 Tarjan 算法

    tarjan算法的步骤是(当dfs到节点u时):1 在并查集中建立仅有u的集合,设置该集合的祖先为u1 对u的每个孩子v:   1.1 tarjan之   1.2 合并v到父节点u的集合,确保集合的祖 ...

  8. 微信公众号开发java框架:wx4j(MaterialUtils篇)

    wx4j-MaterialUtils的使用 函数说明:上传永久视频素材 参数:文件路径.视频描述(通过setter填充内容即可) 返回值:微信服务器返回的json字符串 public static S ...

  9. Spring温故而知新 – bean的装配

    Spring装配机制 Spring提供了三种主要的装配机制: 1:通过XML进行显示配置 2:通过Java代码显示配置 3:自动化装配 自动化装配 Spring中IOC容器分两个步骤来完成自动化装配: ...

  10. application/x-www-form-urlencoded从前端到后台

    html <form id="userForm1" enctype="application/x-www-form-urlencoded" method= ...